mirror of https://github.com/garrytan/gstack.git
merge: resolve version/changelog conflicts with main (v0.12.8.1)
Main shipped v0.12.8.1 (zsh glob compatibility — setopt guards and find-based alternatives for 38 glob patterns across 13 templates). Resolution: keep v0.13.0.0, both CHANGELOG entries preserved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
36cd50c5da
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -29,6 +29,16 @@ gstack can generate real UI mockups. Not ASCII art, not text descriptions of hex
|
||||||
- `design/dist/` gitignored like `browse/dist/`
|
- `design/dist/` gitignored like `browse/dist/`
|
||||||
- Full design doc: `docs/designs/DESIGN_TOOLS_V1.md`
|
- Full design doc: `docs/designs/DESIGN_TOOLS_V1.md`
|
||||||
|
|
||||||
|
## [0.12.8.1] - 2026-03-27 — zsh Glob Compatibility
|
||||||
|
|
||||||
|
Skill scripts now work correctly in zsh. Previously, bash code blocks in skill templates used raw glob patterns like `.github/workflows/*.yaml` and `ls ~/.gstack/projects/$SLUG/*-design-*.md` that would throw "no matches found" errors in zsh when no files matched. Fixed 38 instances across 13 templates and 2 resolvers using two approaches: `find`-based alternatives for complex patterns, and `setopt +o nomatch` guards for simple `ls` commands.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **`.github/workflows/` globs replaced with `find`.** `cat .github/workflows/*deploy*`, `for f in .github/workflows/*.yml`, and `ls .github/workflows/*.yaml` patterns in `/land-and-deploy`, `/setup-deploy`, `/cso`, and the deploy bootstrap resolver now use `find ... -name` instead of raw globs.
|
||||||
|
- **`~/.gstack/` and `~/.claude/` globs guarded with `setopt`.** Design doc lookups, eval result listings, test plan discovery, and retro history checks across 10 skills now prepend `setopt +o nomatch 2>/dev/null || true` (no-op in bash, disables NOMATCH in zsh).
|
||||||
|
- **Test framework detection globs guarded.** `ls jest.config.* vitest.config.*` in the testing resolver now has a setopt guard.
|
||||||
|
|
||||||
## [0.12.8.0] - 2026-03-27 — Codex No Longer Reviews the Wrong Project
|
## [0.12.8.0] - 2026-03-27 — Codex No Longer Reviews the Wrong Project
|
||||||
|
|
||||||
When you run gstack in Conductor with multiple workspaces open, Codex could silently review the wrong project. The `codex exec -C` flag resolved the repo root inline via `$(git rev-parse --show-toplevel)`, which evaluates in whatever cwd the background shell inherits. In multi-workspace environments, that cwd might be a different project entirely.
|
When you run gstack in Conductor with multiple workspaces open, Codex could silently review the wrong project. The `codex exec -C` flag resolved the repo root inline via `$(git rev-parse --show-toplevel)`, which evaluates in whatever cwd the background shell inherits. In multi-workspace environments, that cwd might be a different project entirely.
|
||||||
|
|
|
||||||
|
|
@ -408,6 +408,7 @@ If the Read fails (file not found), say:
|
||||||
|
|
||||||
After /office-hours completes, re-run the design doc check:
|
After /office-hours completes, re-run the design doc check:
|
||||||
```bash
|
```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)")
|
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')
|
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)
|
DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||||
|
|
|
||||||
|
|
@ -650,6 +650,7 @@ TMPERR=$(mktemp /tmp/codex-err-XXXXXX.txt)
|
||||||
3. **Plan review auto-detection:** If the user's prompt is about reviewing a plan,
|
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:
|
or if plan files exist and the user said `/codex` with no arguments:
|
||||||
```bash
|
```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
|
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`
|
If no project-scoped match, fall back to `ls -t ~/.claude/plans/*.md 2>/dev/null | head -1`
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,7 @@ TMPERR=$(mktemp /tmp/codex-err-XXXXXX.txt)
|
||||||
3. **Plan review auto-detection:** If the user's prompt is about reviewing a plan,
|
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:
|
or if plan files exist and the user said `/codex` with no arguments:
|
||||||
```bash
|
```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
|
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`
|
If no project-scoped match, fall back to `ls -t ~/.claude/plans/*.md 2>/dev/null | head -1`
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,7 @@ ls go.mod 2>/dev/null && echo "STACK: Go"
|
||||||
ls Cargo.toml 2>/dev/null && echo "STACK: Rust"
|
ls Cargo.toml 2>/dev/null && echo "STACK: Rust"
|
||||||
ls pom.xml build.gradle 2>/dev/null && echo "STACK: JVM"
|
ls pom.xml build.gradle 2>/dev/null && echo "STACK: JVM"
|
||||||
ls composer.json 2>/dev/null && echo "STACK: PHP"
|
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:**
|
**Framework detection:**
|
||||||
|
|
@ -395,7 +395,8 @@ Map what an attacker sees — both code surface and infrastructure surface.
|
||||||
|
|
||||||
**Infrastructure surface:**
|
**Infrastructure surface:**
|
||||||
```bash
|
```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 "Dockerfile*" -o -name "docker-compose*.yml" 2>/dev/null
|
||||||
find . -maxdepth 4 -name "*.tf" -o -name "*.tfvars" -o -name "kustomization.yaml" 2>/dev/null
|
find . -maxdepth 4 -name "*.tf" -o -name "*.tfvars" -o -name "kustomization.yaml" 2>/dev/null
|
||||||
ls .env .env.* 2>/dev/null
|
ls .env .env.* 2>/dev/null
|
||||||
|
|
@ -445,7 +446,7 @@ grep -q "^\.env$\|^\.env\.\*" .gitignore 2>/dev/null && echo ".env IS gitignored
|
||||||
|
|
||||||
**CI configs with inline secrets (not using secret stores):**
|
**CI configs with inline secrets (not using secret stores):**
|
||||||
```bash
|
```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\.'
|
[ -f "$f" ] && grep -n "password:\|token:\|secret:\|api_key:" "$f" | grep -v '\${{' | grep -v 'secrets\.'
|
||||||
done 2>/dev/null
|
done 2>/dev/null
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ ls go.mod 2>/dev/null && echo "STACK: Go"
|
||||||
ls Cargo.toml 2>/dev/null && echo "STACK: Rust"
|
ls Cargo.toml 2>/dev/null && echo "STACK: Rust"
|
||||||
ls pom.xml build.gradle 2>/dev/null && echo "STACK: JVM"
|
ls pom.xml build.gradle 2>/dev/null && echo "STACK: JVM"
|
||||||
ls composer.json 2>/dev/null && echo "STACK: PHP"
|
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:**
|
**Framework detection:**
|
||||||
|
|
@ -110,7 +110,8 @@ Map what an attacker sees — both code surface and infrastructure surface.
|
||||||
|
|
||||||
**Infrastructure surface:**
|
**Infrastructure surface:**
|
||||||
```bash
|
```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 "Dockerfile*" -o -name "docker-compose*.yml" 2>/dev/null
|
||||||
find . -maxdepth 4 -name "*.tf" -o -name "*.tfvars" -o -name "kustomization.yaml" 2>/dev/null
|
find . -maxdepth 4 -name "*.tf" -o -name "*.tfvars" -o -name "kustomization.yaml" 2>/dev/null
|
||||||
ls .env .env.* 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):**
|
**CI configs with inline secrets (not using secret stores):**
|
||||||
```bash
|
```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\.'
|
[ -f "$f" ] && grep -n "password:\|token:\|secret:\|api_key:" "$f" | grep -v '\${{' | grep -v 'secrets\.'
|
||||||
done 2>/dev/null
|
done 2>/dev/null
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,7 @@ ls src/ app/ pages/ components/ 2>/dev/null | head -30
|
||||||
Look for office-hours output:
|
Look for office-hours output:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
||||||
ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5
|
ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5
|
||||||
ls .context/*office-hours* .context/attachments/*office-hours* 2>/dev/null | head -5
|
ls .context/*office-hours* .context/attachments/*office-hours* 2>/dev/null | head -5
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ ls src/ app/ pages/ components/ 2>/dev/null | head -30
|
||||||
Look for office-hours output:
|
Look for office-hours output:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
{{SLUG_EVAL}}
|
{{SLUG_EVAL}}
|
||||||
ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5
|
ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5
|
||||||
ls .context/*office-hours* .context/attachments/*office-hours* 2>/dev/null | head -5
|
ls .context/*office-hours* .context/attachments/*office-hours* 2>/dev/null | head -5
|
||||||
|
|
|
||||||
|
|
@ -401,6 +401,7 @@ If `NEEDS_SETUP`:
|
||||||
**Detect existing test framework and project runtime:**
|
**Detect existing test framework and project runtime:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Detect project runtime
|
# Detect project runtime
|
||||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||||
[ -f package.json ] && echo "RUNTIME:node"
|
[ -f package.json ] && echo "RUNTIME:node"
|
||||||
|
|
|
||||||
|
|
@ -470,7 +470,7 @@ else
|
||||||
SAVED_HASH=$(cat ~/.gstack/projects/$SLUG/land-deploy-confirmed 2>/dev/null)
|
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)
|
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
|
# 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}"
|
COMBINED_HASH="${CURRENT_HASH}-${WORKFLOW_HASH}"
|
||||||
if [ "$SAVED_HASH" != "$COMBINED_HASH" ] && [ -n "$SAVED_HASH" ]; then
|
if [ "$SAVED_HASH" != "$COMBINED_HASH" ] && [ -n "$SAVED_HASH" ]; then
|
||||||
echo "CONFIG_CHANGED"
|
echo "CONFIG_CHANGED"
|
||||||
|
|
@ -527,7 +527,7 @@ fi
|
||||||
([ -f railway.json ] || [ -f railway.toml ]) && echo "PLATFORM:railway"
|
([ -f railway.json ] || [ -f railway.toml ]) && echo "PLATFORM:railway"
|
||||||
|
|
||||||
# Detect deploy workflows
|
# 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 "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"
|
[ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f"
|
||||||
done
|
done
|
||||||
|
|
@ -613,7 +613,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:
|
2. **GitHub Actions staging workflow:** Check for workflow files with "staging" in the name or content:
|
||||||
```bash
|
```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"
|
[ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f"
|
||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
@ -663,7 +663,7 @@ Save the deploy config fingerprint so we can detect future changes:
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/.gstack/projects/$SLUG
|
mkdir -p ~/.gstack/projects/$SLUG
|
||||||
CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
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
|
echo "${CURRENT_HASH}-${WORKFLOW_HASH}" > ~/.gstack/projects/$SLUG/land-deploy-confirmed
|
||||||
```
|
```
|
||||||
Continue to Step 2.
|
Continue to Step 2.
|
||||||
|
|
@ -805,6 +805,7 @@ If tests fail: **BLOCKER.** Cannot merge with failing tests.
|
||||||
**E2E tests — check recent results:**
|
**E2E tests — check recent results:**
|
||||||
|
|
||||||
```bash
|
```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
|
ls -t ~/.gstack-dev/evals/*-e2e-*-$(date +%Y-%m-%d)*.json 2>/dev/null | head -20
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -820,6 +821,7 @@ If E2E results exist but have failures: **WARNING — N tests failed.** List the
|
||||||
**LLM judge evals — check recent results:**
|
**LLM judge evals — check recent results:**
|
||||||
|
|
||||||
```bash
|
```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
|
ls -t ~/.gstack-dev/evals/*-llm-judge-*-$(date +%Y-%m-%d)*.json 2>/dev/null | head -5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -1025,7 +1027,7 @@ fi
|
||||||
([ -f railway.json ] || [ -f railway.toml ]) && echo "PLATFORM:railway"
|
([ -f railway.json ] || [ -f railway.toml ]) && echo "PLATFORM:railway"
|
||||||
|
|
||||||
# Detect deploy workflows
|
# 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 "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"
|
[ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f"
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ else
|
||||||
SAVED_HASH=$(cat ~/.gstack/projects/$SLUG/land-deploy-confirmed 2>/dev/null)
|
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)
|
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
|
# 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}"
|
COMBINED_HASH="${CURRENT_HASH}-${WORKFLOW_HASH}"
|
||||||
if [ "$SAVED_HASH" != "$COMBINED_HASH" ] && [ -n "$SAVED_HASH" ]; then
|
if [ "$SAVED_HASH" != "$COMBINED_HASH" ] && [ -n "$SAVED_HASH" ]; then
|
||||||
echo "CONFIG_CHANGED"
|
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:
|
2. **GitHub Actions staging workflow:** Check for workflow files with "staging" in the name or content:
|
||||||
```bash
|
```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"
|
[ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f"
|
||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
@ -273,7 +273,7 @@ Save the deploy config fingerprint so we can detect future changes:
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/.gstack/projects/$SLUG
|
mkdir -p ~/.gstack/projects/$SLUG
|
||||||
CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
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
|
echo "${CURRENT_HASH}-${WORKFLOW_HASH}" > ~/.gstack/projects/$SLUG/land-deploy-confirmed
|
||||||
```
|
```
|
||||||
Continue to Step 2.
|
Continue to Step 2.
|
||||||
|
|
@ -415,6 +415,7 @@ If tests fail: **BLOCKER.** Cannot merge with failing tests.
|
||||||
**E2E tests — check recent results:**
|
**E2E tests — check recent results:**
|
||||||
|
|
||||||
```bash
|
```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
|
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:**
|
**LLM judge evals — check recent results:**
|
||||||
|
|
||||||
```bash
|
```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
|
ls -t ~/.gstack-dev/evals/*-llm-judge-*-$(date +%Y-%m-%d)*.json 2>/dev/null | head -5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -368,6 +368,7 @@ eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
||||||
3. Use Grep/Glob to map the codebase areas most relevant to the user's request.
|
3. Use Grep/Glob to map the codebase areas most relevant to the user's request.
|
||||||
4. **List existing design docs for this project:**
|
4. **List existing design docs for this project:**
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
||||||
```
|
```
|
||||||
If design docs exist, list them: "Prior designs for this project: [titles + dates]"
|
If design docs exist, list them: "Prior designs for this project: [titles + dates]"
|
||||||
|
|
@ -598,6 +599,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:
|
Extract 3-5 significant keywords from the user's problem statement and grep across design docs:
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
grep -li "<keyword1>\|<keyword2>\|<keyword3>" ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
grep -li "<keyword1>\|<keyword2>\|<keyword3>" ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -1007,6 +1009,7 @@ DATETIME=$(date +%Y%m%d-%H%M%S)
|
||||||
|
|
||||||
**Design lineage:** Before writing, check for existing design docs on this branch:
|
**Design lineage:** Before writing, check for existing design docs on this branch:
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
PRIOR=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
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.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
3. Use Grep/Glob to map the codebase areas most relevant to the user's request.
|
||||||
4. **List existing design docs for this project:**
|
4. **List existing design docs for this project:**
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
||||||
```
|
```
|
||||||
If design docs exist, list them: "Prior designs for this project: [titles + dates]"
|
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:
|
Extract 3-5 significant keywords from the user's problem statement and grep across design docs:
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
grep -li "<keyword1>\|<keyword2>\|<keyword3>" ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
grep -li "<keyword1>\|<keyword2>\|<keyword3>" ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -424,6 +426,7 @@ DATETIME=$(date +%Y%m%d-%H%M%S)
|
||||||
|
|
||||||
**Design lineage:** Before writing, check for existing design docs on this branch:
|
**Design lineage:** Before writing, check for existing design docs on this branch:
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
PRIOR=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
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.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -445,6 +445,7 @@ Then read CLAUDE.md, TODOS.md, and any existing architecture docs.
|
||||||
|
|
||||||
**Design doc check:**
|
**Design doc check:**
|
||||||
```bash
|
```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)")
|
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')
|
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)
|
DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||||
|
|
@ -455,6 +456,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):
|
**Handoff note check** (reuses $SLUG and $BRANCH from the design doc check above):
|
||||||
```bash
|
```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)
|
HANDOFF=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1)
|
||||||
[ -n "$HANDOFF" ] && echo "HANDOFF_FOUND: $HANDOFF" || echo "NO_HANDOFF"
|
[ -n "$HANDOFF" ] && echo "HANDOFF_FOUND: $HANDOFF" || echo "NO_HANDOFF"
|
||||||
```
|
```
|
||||||
|
|
@ -509,6 +511,7 @@ If the Read fails (file not found), say:
|
||||||
|
|
||||||
After /office-hours completes, re-run the design doc check:
|
After /office-hours completes, re-run the design doc check:
|
||||||
```bash
|
```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)")
|
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')
|
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)
|
DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||||
|
|
@ -1270,6 +1273,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.
|
the review is complete and the context is no longer needed.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
||||||
rm -f ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true
|
rm -f ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ Then read CLAUDE.md, TODOS.md, and any existing architecture docs.
|
||||||
|
|
||||||
**Design doc check:**
|
**Design doc check:**
|
||||||
```bash
|
```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)")
|
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')
|
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)
|
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):
|
**Handoff note check** (reuses $SLUG and $BRANCH from the design doc check above):
|
||||||
```bash
|
```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)
|
HANDOFF=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1)
|
||||||
[ -n "$HANDOFF" ] && echo "HANDOFF_FOUND: $HANDOFF" || echo "NO_HANDOFF"
|
[ -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.
|
the review is complete and the context is no longer needed.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
{{SLUG_EVAL}}
|
{{SLUG_EVAL}}
|
||||||
rm -f ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true
|
rm -f ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,7 @@ When evaluating architecture, think "boring by default." When reviewing tests, t
|
||||||
|
|
||||||
### Design Doc Check
|
### Design Doc Check
|
||||||
```bash
|
```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)")
|
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')
|
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)
|
DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||||
|
|
@ -420,6 +421,7 @@ If the Read fails (file not found), say:
|
||||||
|
|
||||||
After /office-hours completes, re-run the design doc check:
|
After /office-hours completes, re-run the design doc check:
|
||||||
```bash
|
```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)")
|
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')
|
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)
|
DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||||
|
|
@ -497,6 +499,7 @@ Before analyzing coverage, detect the project's test framework:
|
||||||
2. **If CLAUDE.md has no testing section, auto-detect:**
|
2. **If CLAUDE.md has no testing section, auto-detect:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Detect project runtime
|
# Detect project runtime
|
||||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||||
[ -f package.json ] && echo "RUNTIME:node"
|
[ -f package.json ] && echo "RUNTIME:node"
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ When evaluating architecture, think "boring by default." When reviewing tests, t
|
||||||
|
|
||||||
### Design Doc Check
|
### Design Doc Check
|
||||||
```bash
|
```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)")
|
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')
|
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)
|
DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,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
|
1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
||||||
ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
|
ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.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
|
1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
{{SLUG_EVAL}}
|
{{SLUG_EVAL}}
|
||||||
ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
|
ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -442,6 +442,7 @@ If `NEEDS_SETUP`:
|
||||||
**Detect existing test framework and project runtime:**
|
**Detect existing test framework and project runtime:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Detect project runtime
|
# Detect project runtime
|
||||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||||
[ -f package.json ] && echo "RUNTIME:node"
|
[ -f package.json ] && echo "RUNTIME:node"
|
||||||
|
|
@ -604,6 +605,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
|
1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
||||||
ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
|
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
|
1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
{{SLUG_EVAL}}
|
{{SLUG_EVAL}}
|
||||||
ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
|
ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -629,6 +629,7 @@ Count backward from today — how many consecutive days have at least one commit
|
||||||
Before saving the new snapshot, check for prior retro history:
|
Before saving the new snapshot, check for prior retro history:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
ls -t .context/retros/*.json 2>/dev/null
|
ls -t .context/retros/*.json 2>/dev/null
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -655,6 +656,7 @@ mkdir -p .context/retros
|
||||||
|
|
||||||
Determine the next sequence number for today (substitute the actual date for `$(date +%Y-%m-%d)`):
|
Determine the next sequence number for today (substitute the actual date for `$(date +%Y-%m-%d)`):
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Count existing retros for today to get next sequence number
|
# Count existing retros for today to get next sequence number
|
||||||
today=$(date +%Y-%m-%d)
|
today=$(date +%Y-%m-%d)
|
||||||
existing=$(ls .context/retros/${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
|
existing=$(ls .context/retros/${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
|
||||||
|
|
@ -778,6 +780,7 @@ Narrative covering:
|
||||||
Check review JSONL logs for plan completion data from /ship runs this period:
|
Check review JSONL logs for plan completion data from /ship runs this period:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
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"
|
cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA"
|
||||||
```
|
```
|
||||||
|
|
@ -1079,6 +1082,7 @@ Considering the full cross-project picture.
|
||||||
### Global Step 8: Load history & compare
|
### Global Step 8: Load history & compare
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
ls -t ~/.gstack/retros/global-*.json 2>/dev/null | head -5
|
ls -t ~/.gstack/retros/global-*.json 2>/dev/null | head -5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -1096,6 +1100,7 @@ mkdir -p ~/.gstack/retros
|
||||||
|
|
||||||
Determine the next sequence number for today:
|
Determine the next sequence number for today:
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
today=$(date +%Y-%m-%d)
|
today=$(date +%Y-%m-%d)
|
||||||
existing=$(ls ~/.gstack/retros/global-${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
|
existing=$(ls ~/.gstack/retros/global-${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
|
||||||
next=$((existing + 1))
|
next=$((existing + 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:
|
Before saving the new snapshot, check for prior retro history:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
ls -t .context/retros/*.json 2>/dev/null
|
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)`):
|
Determine the next sequence number for today (substitute the actual date for `$(date +%Y-%m-%d)`):
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Count existing retros for today to get next sequence number
|
# Count existing retros for today to get next sequence number
|
||||||
today=$(date +%Y-%m-%d)
|
today=$(date +%Y-%m-%d)
|
||||||
existing=$(ls .context/retros/${today}-*.json 2>/dev/null | wc -l | tr -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:
|
Check review JSONL logs for plan completion data from /ship runs this period:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
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"
|
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
|
### Global Step 8: Load history & compare
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
ls -t ~/.gstack/retros/global-*.json 2>/dev/null | head -5
|
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:
|
Determine the next sequence number for today:
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
today=$(date +%Y-%m-%d)
|
today=$(date +%Y-%m-%d)
|
||||||
existing=$(ls ~/.gstack/retros/global-${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
|
existing=$(ls ~/.gstack/retros/global-${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
|
||||||
next=$((existing + 1))
|
next=$((existing + 1))
|
||||||
|
|
|
||||||
|
|
@ -394,6 +394,7 @@ Before reviewing code quality, check: **did they build what was requested — no
|
||||||
2. **Content-based search (fallback):** If no plan file is referenced in conversation context, search by content:
|
2. **Content-based search (fallback):** If no plan file is referenced in conversation context, search by content:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-')
|
BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-')
|
||||||
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
|
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
|
||||||
# Search common plan file locations
|
# Search common plan file locations
|
||||||
|
|
@ -650,6 +651,7 @@ Before analyzing coverage, detect the project's test framework:
|
||||||
2. **If CLAUDE.md has no testing section, auto-detect:**
|
2. **If CLAUDE.md has no testing section, auto-detect:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Detect project runtime
|
# Detect project runtime
|
||||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||||
[ -f package.json ] && echo "RUNTIME:node"
|
[ -f package.json ] && echo "RUNTIME:node"
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,7 @@ If the Read fails (file not found), say:
|
||||||
|
|
||||||
After /${first} completes, re-run the design doc check:
|
After /${first} completes, re-run the design doc check:
|
||||||
\`\`\`bash
|
\`\`\`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)")
|
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')
|
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)
|
DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
|
||||||
|
|
@ -614,6 +615,7 @@ function generatePlanFileDiscovery(): string {
|
||||||
2. **Content-based search (fallback):** If no plan file is referenced in conversation context, search by content:
|
2. **Content-based search (fallback):** If no plan file is referenced in conversation context, search by content:
|
||||||
|
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-')
|
BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-')
|
||||||
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
|
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
|
||||||
# Search common plan file locations
|
# Search common plan file locations
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ export function generateTestBootstrap(_ctx: TemplateContext): string {
|
||||||
**Detect existing test framework and project runtime:**
|
**Detect existing test framework and project runtime:**
|
||||||
|
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Detect project runtime
|
# Detect project runtime
|
||||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||||
[ -f package.json ] && echo "RUNTIME:node"
|
[ -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:**
|
2. **If CLAUDE.md has no testing section, auto-detect:**
|
||||||
|
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Detect project runtime
|
# Detect project runtime
|
||||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||||
[ -f package.json ] && echo "RUNTIME:node"
|
[ -f package.json ] && echo "RUNTIME:node"
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ fi
|
||||||
([ -f railway.json ] || [ -f railway.toml ]) && echo "PLATFORM:railway"
|
([ -f railway.json ] || [ -f railway.toml ]) && echo "PLATFORM:railway"
|
||||||
|
|
||||||
# Detect deploy workflows
|
# 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 "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"
|
[ -f "$f" ] && grep -qiE "staging" "$f" 2>/dev/null && echo "STAGING_WORKFLOW:$f"
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -349,13 +349,13 @@ Run the platform detection from the deploy bootstrap:
|
||||||
[ -f railway.json ] || [ -f railway.toml ] && echo "PLATFORM:railway"
|
[ -f railway.json ] || [ -f railway.toml ] && echo "PLATFORM:railway"
|
||||||
|
|
||||||
# GitHub Actions deploy workflows
|
# 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"
|
[ -f "$f" ] && grep -qiE "deploy|release|production|staging|cd" "$f" 2>/dev/null && echo "DEPLOY_WORKFLOW:$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Project type
|
# Project type
|
||||||
[ -f package.json ] && grep -q '"bin"' package.json 2>/dev/null && echo "PROJECT_TYPE:cli"
|
[ -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
|
### Step 3: Platform-specific setup
|
||||||
|
|
|
||||||
|
|
@ -64,13 +64,13 @@ Run the platform detection from the deploy bootstrap:
|
||||||
[ -f railway.json ] || [ -f railway.toml ] && echo "PLATFORM:railway"
|
[ -f railway.json ] || [ -f railway.toml ] && echo "PLATFORM:railway"
|
||||||
|
|
||||||
# GitHub Actions deploy workflows
|
# 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"
|
[ -f "$f" ] && grep -qiE "deploy|release|production|staging|cd" "$f" 2>/dev/null && echo "DEPLOY_WORKFLOW:$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Project type
|
# Project type
|
||||||
[ -f package.json ] && grep -q '"bin"' package.json 2>/dev/null && echo "PROJECT_TYPE:cli"
|
[ -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
|
### Step 3: Platform-specific setup
|
||||||
|
|
|
||||||
|
|
@ -514,6 +514,7 @@ git fetch origin <base> && git merge origin/<base> --no-edit
|
||||||
**Detect existing test framework and project runtime:**
|
**Detect existing test framework and project runtime:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Detect project runtime
|
# Detect project runtime
|
||||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||||
[ -f package.json ] && echo "RUNTIME:node"
|
[ -f package.json ] && echo "RUNTIME:node"
|
||||||
|
|
@ -866,6 +867,7 @@ Before analyzing coverage, detect the project's test framework:
|
||||||
2. **If CLAUDE.md has no testing section, auto-detect:**
|
2. **If CLAUDE.md has no testing section, auto-detect:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
# Detect project runtime
|
# Detect project runtime
|
||||||
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
[ -f Gemfile ] && echo "RUNTIME:ruby"
|
||||||
[ -f package.json ] && echo "RUNTIME:node"
|
[ -f package.json ] && echo "RUNTIME:node"
|
||||||
|
|
@ -1124,6 +1126,7 @@ Repo: {owner/repo}
|
||||||
2. **Content-based search (fallback):** If no plan file is referenced in conversation context, search by content:
|
2. **Content-based search (fallback):** If no plan file is referenced in conversation context, search by content:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
setopt +o nomatch 2>/dev/null || true # zsh compat
|
||||||
BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-')
|
BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-')
|
||||||
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
|
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
|
||||||
# Search common plan file locations
|
# Search common plan file locations
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,43 @@ describe('gen-skill-docs', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('bash blocks with shell globs are zsh-safe (setopt guard or find)', () => {
|
||||||
|
for (const skill of ALL_SKILLS) {
|
||||||
|
const content = fs.readFileSync(path.join(ROOT, skill.dir, 'SKILL.md'), 'utf-8');
|
||||||
|
const bashBlocks = [...content.matchAll(/```bash\n([\s\S]*?)```/g)].map(m => m[1]);
|
||||||
|
|
||||||
|
for (const block of bashBlocks) {
|
||||||
|
const lines = block.split('\n');
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trimStart();
|
||||||
|
if (trimmed.startsWith('#')) continue;
|
||||||
|
if (!trimmed.includes('*')) continue;
|
||||||
|
// Skip lines where * is inside find -name, git pathspecs, or $(find)
|
||||||
|
if (/\bfind\b/.test(trimmed)) continue;
|
||||||
|
if (/\bgit\b/.test(trimmed)) continue;
|
||||||
|
if (/\$\(find\b/.test(trimmed)) continue;
|
||||||
|
|
||||||
|
// Check 1: "for VAR in <glob>" must use $(find ...) — caught above by the
|
||||||
|
// $(find check, so any surviving for-in with a glob pattern is a violation
|
||||||
|
if (/\bfor\s+\w+\s+in\b/.test(trimmed) && /\*\./.test(trimmed)) {
|
||||||
|
throw new Error(
|
||||||
|
`Unsafe for-in glob in ${skill.dir}/SKILL.md: "${trimmed}". ` +
|
||||||
|
`Use \`for f in $(find ... -name '*.ext')\` for zsh compatibility.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check 2: ls/cat/rm/grep with glob file args must have setopt guard
|
||||||
|
const isGlobCmd = /\b(?:ls|cat|rm|grep)\b/.test(trimmed) &&
|
||||||
|
/(?:\/\*[a-z.*]|\*\.[a-z])/.test(trimmed);
|
||||||
|
if (isGlobCmd) {
|
||||||
|
expect(block).toContain('setopt +o nomatch');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('preamble-using skills have correct skill name in telemetry', () => {
|
test('preamble-using skills have correct skill name in telemetry', () => {
|
||||||
const PREAMBLE_SKILLS = [
|
const PREAMBLE_SKILLS = [
|
||||||
{ dir: '.', name: 'gstack' },
|
{ dir: '.', name: 'gstack' },
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue