mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-05 13:15:24 +02:00
fix: replace zsh-incompatible raw globs with find-based alternatives and setopt guards
Zsh's NOMATCH option (on by default) causes raw globs like `*.yaml` and `*deploy*` to throw errors when no files match, instead of silently expanding to nothing as bash does. The preamble resolver already handled this correctly with find, but 38 glob instances across 13 templates and 2 resolvers still used raw shell globs. Two fix approaches based on complexity: - find-based replacement for cat/for/ls-with-pipes patterns (.github/workflows/) - setopt +o nomatch guard for simple ls -t patterns (~/.gstack/, ~/.claude/) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -240,6 +240,7 @@ TMPERR=$(mktemp /tmp/codex-err-XXXXXX.txt)
|
||||
3. **Plan review auto-detection:** If the user's prompt is about reviewing a plan,
|
||||
or if plan files exist and the user said `/codex` with no arguments:
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
ls -t ~/.claude/plans/*.md 2>/dev/null | xargs grep -l "$(basename $(pwd))" 2>/dev/null | head -1
|
||||
```
|
||||
If no project-scoped match, fall back to `ls -t ~/.claude/plans/*.md 2>/dev/null | head -1`
|
||||
|
||||
+4
-3
@@ -73,7 +73,7 @@ ls go.mod 2>/dev/null && echo "STACK: Go"
|
||||
ls Cargo.toml 2>/dev/null && echo "STACK: Rust"
|
||||
ls pom.xml build.gradle 2>/dev/null && echo "STACK: JVM"
|
||||
ls composer.json 2>/dev/null && echo "STACK: PHP"
|
||||
ls *.csproj *.sln 2>/dev/null && echo "STACK: .NET"
|
||||
find . -maxdepth 1 \( -name '*.csproj' -o -name '*.sln' \) 2>/dev/null | grep -q . && echo "STACK: .NET"
|
||||
```
|
||||
|
||||
**Framework detection:**
|
||||
@@ -110,7 +110,8 @@ Map what an attacker sees — both code surface and infrastructure surface.
|
||||
|
||||
**Infrastructure surface:**
|
||||
```bash
|
||||
ls .github/workflows/*.yml .github/workflows/*.yaml .gitlab-ci.yml 2>/dev/null | wc -l
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
{ find .github/workflows -maxdepth 1 \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null; [ -f .gitlab-ci.yml ] && echo .gitlab-ci.yml; } | wc -l
|
||||
find . -maxdepth 4 -name "Dockerfile*" -o -name "docker-compose*.yml" 2>/dev/null
|
||||
find . -maxdepth 4 -name "*.tf" -o -name "*.tfvars" -o -name "kustomization.yaml" 2>/dev/null
|
||||
ls .env .env.* 2>/dev/null
|
||||
@@ -160,7 +161,7 @@ grep -q "^\.env$\|^\.env\.\*" .gitignore 2>/dev/null && echo ".env IS gitignored
|
||||
|
||||
**CI configs with inline secrets (not using secret stores):**
|
||||
```bash
|
||||
for f in .github/workflows/*.yml .github/workflows/*.yaml .gitlab-ci.yml .circleci/config.yml; do
|
||||
for f in $(find .github/workflows -maxdepth 1 \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null) .gitlab-ci.yml .circleci/config.yml; do
|
||||
[ -f "$f" ] && grep -n "password:\|token:\|secret:\|api_key:" "$f" | grep -v '\${{' | grep -v 'secrets\.'
|
||||
done 2>/dev/null
|
||||
```
|
||||
|
||||
@@ -53,6 +53,7 @@ ls src/ app/ pages/ components/ 2>/dev/null | head -30
|
||||
Look for office-hours output:
|
||||
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
{{SLUG_EVAL}}
|
||||
ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5
|
||||
ls .context/*office-hours* .context/attachments/*office-hours* 2>/dev/null | head -5
|
||||
|
||||
@@ -113,7 +113,7 @@ else
|
||||
SAVED_HASH=$(cat ~/.gstack/projects/$SLUG/land-deploy-confirmed 2>/dev/null)
|
||||
CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
||||
# Also hash workflow files that affect deploy behavior
|
||||
WORKFLOW_HASH=$(cat .github/workflows/*deploy* .github/workflows/*cd* 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
||||
WORKFLOW_HASH=$(find .github/workflows -maxdepth 1 \( -name '*deploy*' -o -name '*cd*' \) 2>/dev/null | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
||||
COMBINED_HASH="${CURRENT_HASH}-${WORKFLOW_HASH}"
|
||||
if [ "$SAVED_HASH" != "$COMBINED_HASH" ] && [ -n "$SAVED_HASH" ]; then
|
||||
echo "CONFIG_CHANGED"
|
||||
@@ -223,7 +223,7 @@ grep -i "staging" CLAUDE.md 2>/dev/null | head -3
|
||||
|
||||
2. **GitHub Actions staging workflow:** Check for workflow files with "staging" in the name or content:
|
||||
```bash
|
||||
for f in .github/workflows/*.yml .github/workflows/*.yaml; do
|
||||
for f in $(find .github/workflows -maxdepth 1 \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null); do
|
||||
[ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f"
|
||||
done
|
||||
```
|
||||
@@ -273,7 +273,7 @@ Save the deploy config fingerprint so we can detect future changes:
|
||||
```bash
|
||||
mkdir -p ~/.gstack/projects/$SLUG
|
||||
CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
||||
WORKFLOW_HASH=$(cat .github/workflows/*deploy* .github/workflows/*cd* 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
||||
WORKFLOW_HASH=$(find .github/workflows -maxdepth 1 \( -name '*deploy*' -o -name '*cd*' \) 2>/dev/null | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
||||
echo "${CURRENT_HASH}-${WORKFLOW_HASH}" > ~/.gstack/projects/$SLUG/land-deploy-confirmed
|
||||
```
|
||||
Continue to Step 2.
|
||||
@@ -415,6 +415,7 @@ If tests fail: **BLOCKER.** Cannot merge with failing tests.
|
||||
**E2E tests — check recent results:**
|
||||
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
ls -t ~/.gstack-dev/evals/*-e2e-*-$(date +%Y-%m-%d)*.json 2>/dev/null | head -20
|
||||
```
|
||||
|
||||
@@ -430,6 +431,7 @@ If E2E results exist but have failures: **WARNING — N tests failed.** List the
|
||||
**LLM judge evals — check recent results:**
|
||||
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
ls -t ~/.gstack-dev/evals/*-llm-judge-*-$(date +%Y-%m-%d)*.json 2>/dev/null | head -5
|
||||
```
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ Understand the project and the area the user wants to change.
|
||||
3. Use Grep/Glob to map the codebase areas most relevant to the user's request.
|
||||
4. **List existing design docs for this project:**
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
||||
```
|
||||
If design docs exist, list them: "Prior designs for this project: [titles + dates]"
|
||||
@@ -278,6 +279,7 @@ After the user states the problem (first question in Phase 2A or 2B), search exi
|
||||
|
||||
Extract 3-5 significant keywords from the user's problem statement and grep across design docs:
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
grep -li "<keyword1>\|<keyword2>\|<keyword3>" ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
||||
```
|
||||
|
||||
@@ -422,6 +424,7 @@ DATETIME=$(date +%Y%m%d-%H%M%S)
|
||||
|
||||
**Design lineage:** Before writing, check for existing design docs on this branch:
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
PRIOR=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||
```
|
||||
If `$PRIOR` exists, the new doc gets a `Supersedes:` field referencing it. This creates a revision chain — you can trace how a design evolved across office hours sessions.
|
||||
|
||||
@@ -105,6 +105,7 @@ Then read CLAUDE.md, TODOS.md, and any existing architecture docs.
|
||||
|
||||
**Design doc check:**
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch')
|
||||
DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||
@@ -115,6 +116,7 @@ If a design doc exists (from `/office-hours`), read it. Use it as the source of
|
||||
|
||||
**Handoff note check** (reuses $SLUG and $BRANCH from the design doc check above):
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
HANDOFF=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1)
|
||||
[ -n "$HANDOFF" ] && echo "HANDOFF_FOUND: $HANDOFF" || echo "NO_HANDOFF"
|
||||
```
|
||||
@@ -703,6 +705,7 @@ After producing the Completion Summary, clean up any handoff notes for this bran
|
||||
the review is complete and the context is no longer needed.
|
||||
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
{{SLUG_EVAL}}
|
||||
rm -f ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true
|
||||
```
|
||||
|
||||
@@ -68,6 +68,7 @@ When evaluating architecture, think "boring by default." When reviewing tests, t
|
||||
|
||||
### Design Doc Check
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch')
|
||||
DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||
|
||||
@@ -55,6 +55,7 @@ Before falling back to git diff heuristics, check for richer test plan sources:
|
||||
|
||||
1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
{{SLUG_EVAL}}
|
||||
ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
|
||||
```
|
||||
|
||||
@@ -96,6 +96,7 @@ Before falling back to git diff heuristics, check for richer test plan sources:
|
||||
|
||||
1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
{{SLUG_EVAL}}
|
||||
ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
|
||||
```
|
||||
|
||||
@@ -307,6 +307,7 @@ Count backward from today — how many consecutive days have at least one commit
|
||||
Before saving the new snapshot, check for prior retro history:
|
||||
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
ls -t .context/retros/*.json 2>/dev/null
|
||||
```
|
||||
|
||||
@@ -333,6 +334,7 @@ mkdir -p .context/retros
|
||||
|
||||
Determine the next sequence number for today (substitute the actual date for `$(date +%Y-%m-%d)`):
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
# Count existing retros for today to get next sequence number
|
||||
today=$(date +%Y-%m-%d)
|
||||
existing=$(ls .context/retros/${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
|
||||
@@ -456,6 +458,7 @@ Narrative covering:
|
||||
Check review JSONL logs for plan completion data from /ship runs this period:
|
||||
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
||||
cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA"
|
||||
```
|
||||
@@ -757,6 +760,7 @@ Considering the full cross-project picture.
|
||||
### Global Step 8: Load history & compare
|
||||
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
ls -t ~/.gstack/retros/global-*.json 2>/dev/null | head -5
|
||||
```
|
||||
|
||||
@@ -774,6 +778,7 @@ mkdir -p ~/.gstack/retros
|
||||
|
||||
Determine the next sequence number for today:
|
||||
```bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
today=$(date +%Y-%m-%d)
|
||||
existing=$(ls ~/.gstack/retros/global-${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
|
||||
next=$((existing + 1))
|
||||
|
||||
@@ -6,6 +6,7 @@ export function generateTestBootstrap(_ctx: TemplateContext): string {
|
||||
**Detect existing test framework and project runtime:**
|
||||
|
||||
\`\`\`bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
# Detect project runtime
|
||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||
[ -f package.json ] && echo "RUNTIME:node"
|
||||
@@ -200,6 +201,7 @@ Before analyzing coverage, detect the project's test framework:
|
||||
2. **If CLAUDE.md has no testing section, auto-detect:**
|
||||
|
||||
\`\`\`bash
|
||||
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||
# Detect project runtime
|
||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||
[ -f package.json ] && echo "RUNTIME:node"
|
||||
|
||||
@@ -72,7 +72,7 @@ fi
|
||||
([ -f railway.json ] || [ -f railway.toml ]) && echo "PLATFORM:railway"
|
||||
|
||||
# Detect deploy workflows
|
||||
for f in .github/workflows/*.yml .github/workflows/*.yaml; do
|
||||
for f in $(find .github/workflows -maxdepth 1 \\( -name '*.yml' -o -name '*.yaml' \\) 2>/dev/null); do
|
||||
[ -f "$f" ] && grep -qiE "deploy|release|production|cd" "$f" 2>/dev/null && echo "DEPLOY_WORKFLOW:$f"
|
||||
[ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f"
|
||||
done
|
||||
|
||||
@@ -64,13 +64,13 @@ Run the platform detection from the deploy bootstrap:
|
||||
[ -f railway.json ] || [ -f railway.toml ] && echo "PLATFORM:railway"
|
||||
|
||||
# GitHub Actions deploy workflows
|
||||
for f in .github/workflows/*.yml .github/workflows/*.yaml; do
|
||||
for f in $(find .github/workflows -maxdepth 1 \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null); do
|
||||
[ -f "$f" ] && grep -qiE "deploy|release|production|staging|cd" "$f" 2>/dev/null && echo "DEPLOY_WORKFLOW:$f"
|
||||
done
|
||||
|
||||
# Project type
|
||||
[ -f package.json ] && grep -q '"bin"' package.json 2>/dev/null && echo "PROJECT_TYPE:cli"
|
||||
ls *.gemspec 2>/dev/null && echo "PROJECT_TYPE:library"
|
||||
find . -maxdepth 1 -name '*.gemspec' 2>/dev/null | grep -q . && echo "PROJECT_TYPE:library"
|
||||
```
|
||||
|
||||
### Step 3: Platform-specific setup
|
||||
|
||||
Reference in New Issue
Block a user