diff --git a/bin/gstack-slug b/bin/gstack-slug new file mode 100755 index 00000000..7336b7b4 --- /dev/null +++ b/bin/gstack-slug @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# gstack-slug — output project slug and sanitized branch name +# Usage: eval $(gstack-slug) → sets SLUG and BRANCH variables +# Or: gstack-slug → prints SLUG=... and BRANCH=... lines +set -euo pipefail +SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-') +echo "SLUG=$SLUG" +echo "BRANCH=$BRANCH" diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index d92d47fd..0d919950 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -114,7 +114,7 @@ ls src/ app/ pages/ components/ 2>/dev/null | head -30 Look for brainstorm output: ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) ls ~/.gstack/projects/$SLUG/*brainstorm* 2>/dev/null | head -5 ls .context/*brainstorm* .context/attachments/*brainstorm* 2>/dev/null | head -5 ``` diff --git a/design-consultation/SKILL.md.tmpl b/design-consultation/SKILL.md.tmpl index 11d868fa..ef5044fb 100644 --- a/design-consultation/SKILL.md.tmpl +++ b/design-consultation/SKILL.md.tmpl @@ -49,7 +49,7 @@ ls src/ app/ pages/ components/ 2>/dev/null | head -30 Look for brainstorm output: ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) ls ~/.gstack/projects/$SLUG/*brainstorm* 2>/dev/null | head -5 ls .context/*brainstorm* .context/attachments/*brainstorm* 2>/dev/null | head -5 ``` diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index 4cd8f481..b5c0775b 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -154,8 +154,7 @@ For LLM/prompt changes: check the "Prompt/LLM changes" file patterns listed in C After producing the test diagram, write a test plan artifact to the project directory so `/qa` and `/qa-only` can consume it as primary test input (replacing the lossy git-diff heuristic): ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') -BRANCH=$(git rev-parse --abbrev-ref HEAD) +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) mkdir -p ~/.gstack/projects/$SLUG @@ -259,5 +258,52 @@ Check the git log for this branch. If there are prior commits suggesting a previ * One sentence max per option. Pick in under 5 seconds. * After each review section, pause and ask for feedback before moving on. +## Review Log + +After producing the Completion Summary above, persist the review result: + +```bash +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) +mkdir -p ~/.gstack/projects/$SLUG +echo '{"skill":"plan-eng-review","timestamp":"TIMESTAMP","status":"STATUS","unresolved":N,"critical_gaps":N,"mode":"MODE"}' >> ~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl +``` + +Substitute values from the Completion Summary: +- **TIMESTAMP**: current ISO 8601 datetime +- **STATUS**: "clean" if 0 unresolved decisions AND 0 critical gaps; otherwise "issues_open" +- **unresolved**: number from "Unresolved decisions" count +- **critical_gaps**: number from "Failure modes: ___ critical gaps flagged" +- **MODE**: SCOPE_REDUCTION / BIG_CHANGE / SMALL_CHANGE + +## Review Readiness Dashboard + +After completing the review, read the review log to display the dashboard. + +```bash +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) +cat ~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl 2>/dev/null || echo "NO_REVIEWS" +``` + +Parse the output. Find the most recent entry for each skill (plan-ceo-review, plan-eng-review, plan-design-review). Ignore entries with timestamps older than 7 days. Display: + +``` ++====================================================================+ +| REVIEW READINESS DASHBOARD | ++====================================================================+ +| Review | Runs | Last Run | Status | +|-----------------|------|---------------------|----------------------| +| CEO Review | 1 | 2026-03-16 14:30 | CLEAR | +| Eng Review | 1 | 2026-03-16 15:00 | CLEAR | +| Design Review | 0 | — | NOT YET RUN | ++--------------------------------------------------------------------+ +| VERDICT: 2/3 CLEAR — Design Review not yet run | ++====================================================================+ +``` + +**Verdict logic:** +- **CLEARED TO SHIP (3/3)**: All three have >= 1 entry within 7 days AND most recent status is "clean" +- **N/3 CLEAR**: Show count and list which are missing, have open issues, or are stale (>7 days) +- Informational only — does NOT block. + ## Unresolved decisions If the user does not respond to an AskUserQuestion or interrupts to move on, note which decisions were left unresolved. At the end of the review, list these as "Unresolved decisions that may bite you later" — never silently default to an option. diff --git a/plan-eng-review/SKILL.md.tmpl b/plan-eng-review/SKILL.md.tmpl index 410b0726..4905d8d3 100644 --- a/plan-eng-review/SKILL.md.tmpl +++ b/plan-eng-review/SKILL.md.tmpl @@ -89,8 +89,7 @@ For LLM/prompt changes: check the "Prompt/LLM changes" file patterns listed in C After producing the test diagram, write a test plan artifact to the project directory so `/qa` and `/qa-only` can consume it as primary test input (replacing the lossy git-diff heuristic): ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') -BRANCH=$(git rev-parse --abbrev-ref HEAD) +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) mkdir -p ~/.gstack/projects/$SLUG @@ -194,5 +193,24 @@ Check the git log for this branch. If there are prior commits suggesting a previ * One sentence max per option. Pick in under 5 seconds. * After each review section, pause and ask for feedback before moving on. +## Review Log + +After producing the Completion Summary above, persist the review result: + +```bash +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) +mkdir -p ~/.gstack/projects/$SLUG +echo '{"skill":"plan-eng-review","timestamp":"TIMESTAMP","status":"STATUS","unresolved":N,"critical_gaps":N,"mode":"MODE"}' >> ~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl +``` + +Substitute values from the Completion Summary: +- **TIMESTAMP**: current ISO 8601 datetime +- **STATUS**: "clean" if 0 unresolved decisions AND 0 critical gaps; otherwise "issues_open" +- **unresolved**: number from "Unresolved decisions" count +- **critical_gaps**: number from "Failure modes: ___ critical gaps flagged" +- **MODE**: SCOPE_REDUCTION / BIG_CHANGE / SMALL_CHANGE + +{{REVIEW_DASHBOARD}} + ## Unresolved decisions If the user does not respond to an AskUserQuestion or interrupts to move on, note which decisions were left unresolved. At the end of the review, list these as "Unresolved decisions that may bite you later" — never silently default to an option. diff --git a/qa-design-review/SKILL.md b/qa-design-review/SKILL.md index 08c9916a..b4b1e436 100644 --- a/qa-design-review/SKILL.md +++ b/qa-design-review/SKILL.md @@ -396,7 +396,7 @@ Compare screenshots and observations across pages for: **Project-scoped:** ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) mkdir -p ~/.gstack/projects/$SLUG ``` Write to: `~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md` @@ -602,7 +602,7 @@ Write the report to both local and project-scoped locations: **Project-scoped:** ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) mkdir -p ~/.gstack/projects/$SLUG ``` Write to `~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md` diff --git a/qa-design-review/SKILL.md.tmpl b/qa-design-review/SKILL.md.tmpl index 86e9aab9..0053a494 100644 --- a/qa-design-review/SKILL.md.tmpl +++ b/qa-design-review/SKILL.md.tmpl @@ -191,7 +191,7 @@ Write the report to both local and project-scoped locations: **Project-scoped:** ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) mkdir -p ~/.gstack/projects/$SLUG ``` Write to `~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md` diff --git a/qa-only/SKILL.md b/qa-only/SKILL.md index 5e74f03e..e382a4f8 100644 --- a/qa-only/SKILL.md +++ b/qa-only/SKILL.md @@ -134,7 +134,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 - SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') + eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation @@ -426,7 +426,7 @@ Write the report to both local and project-scoped locations: **Project-scoped:** Write test outcome artifact for cross-session context: ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) mkdir -p ~/.gstack/projects/$SLUG ``` Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` diff --git a/qa-only/SKILL.md.tmpl b/qa-only/SKILL.md.tmpl index 5d49e201..101cd71c 100644 --- a/qa-only/SKILL.md.tmpl +++ b/qa-only/SKILL.md.tmpl @@ -52,7 +52,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 - SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') + eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation @@ -72,7 +72,7 @@ Write the report to both local and project-scoped locations: **Project-scoped:** Write test outcome artifact for cross-session context: ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) mkdir -p ~/.gstack/projects/$SLUG ``` Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` diff --git a/qa/SKILL.md b/qa/SKILL.md index 6b078a9d..ba1868db 100644 --- a/qa/SKILL.md +++ b/qa/SKILL.md @@ -171,7 +171,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 - SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') + eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation @@ -578,7 +578,7 @@ Write the report to both local and project-scoped locations: **Project-scoped:** Write test outcome artifact for cross-session context: ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) mkdir -p ~/.gstack/projects/$SLUG ``` Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` diff --git a/qa/SKILL.md.tmpl b/qa/SKILL.md.tmpl index f491999e..45dfbea6 100644 --- a/qa/SKILL.md.tmpl +++ b/qa/SKILL.md.tmpl @@ -72,7 +72,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 - SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') + eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation @@ -207,7 +207,7 @@ Write the report to both local and project-scoped locations: **Project-scoped:** Write test outcome artifact for cross-session context: ```bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) mkdir -p ~/.gstack/projects/$SLUG ``` Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` diff --git a/scripts/gen-skill-docs.ts b/scripts/gen-skill-docs.ts index 7a0b85ab..2e838c29 100644 --- a/scripts/gen-skill-docs.ts +++ b/scripts/gen-skill-docs.ts @@ -729,7 +729,7 @@ Compare screenshots and observations across pages for: **Project-scoped:** \`\`\`bash -SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\\([^/]*/[^/]*\\)\\.git$|\\1|;s|.*[:/]\\([^/]*/[^/]*\\)$|\\1|' | tr '/' '-') +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) mkdir -p ~/.gstack/projects/$SLUG \`\`\` Write to: \`~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md\` @@ -812,6 +812,38 @@ Tie everything to user goals and product objectives. Always suggest specific imp 10. **Depth over breadth.** 5-10 well-documented findings with screenshots and specific suggestions > 20 vague observations.`; } +function generateReviewDashboard(): string { + return `## Review Readiness Dashboard + +After completing the review, read the review log to display the dashboard. + +\`\`\`bash +eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null) +cat ~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl 2>/dev/null || echo "NO_REVIEWS" +\`\`\` + +Parse the output. Find the most recent entry for each skill (plan-ceo-review, plan-eng-review, plan-design-review). Ignore entries with timestamps older than 7 days. Display: + +\`\`\` ++====================================================================+ +| REVIEW READINESS DASHBOARD | ++====================================================================+ +| Review | Runs | Last Run | Status | +|-----------------|------|---------------------|----------------------| +| CEO Review | 1 | 2026-03-16 14:30 | CLEAR | +| Eng Review | 1 | 2026-03-16 15:00 | CLEAR | +| Design Review | 0 | — | NOT YET RUN | ++--------------------------------------------------------------------+ +| VERDICT: 2/3 CLEAR — Design Review not yet run | ++====================================================================+ +\`\`\` + +**Verdict logic:** +- **CLEARED TO SHIP (3/3)**: All three have >= 1 entry within 7 days AND most recent status is "clean" +- **N/3 CLEAR**: Show count and list which are missing, have open issues, or are stale (>7 days) +- Informational only — does NOT block.`; +} + const RESOLVERS: Record string> = { COMMAND_REFERENCE: generateCommandReference, SNAPSHOT_FLAGS: generateSnapshotFlags, @@ -820,6 +852,7 @@ const RESOLVERS: Record string> = { BASE_BRANCH_DETECT: generateBaseBranchDetect, QA_METHODOLOGY: generateQAMethodology, DESIGN_METHODOLOGY: generateDesignMethodology, + REVIEW_DASHBOARD: generateReviewDashboard, }; // ─── Template Processing ──────────────────────────────────── diff --git a/test/skill-validation.test.ts b/test/skill-validation.test.ts index 83cdca6e..78a9bef7 100644 --- a/test/skill-validation.test.ts +++ b/test/skill-validation.test.ts @@ -665,3 +665,45 @@ describe('Planted-bug fixture validation', () => { expect(content).toContain('update_column'); }); }); + +// --- gstack-slug helper --- + +describe('gstack-slug', () => { + const SLUG_BIN = path.join(ROOT, 'bin', 'gstack-slug'); + + test('binary exists and is executable', () => { + expect(fs.existsSync(SLUG_BIN)).toBe(true); + const stat = fs.statSync(SLUG_BIN); + expect(stat.mode & 0o111).toBeGreaterThan(0); + }); + + test('outputs SLUG and BRANCH lines in a git repo', () => { + const result = Bun.spawnSync([SLUG_BIN], { cwd: ROOT, stdout: 'pipe', stderr: 'pipe' }); + expect(result.exitCode).toBe(0); + const output = result.stdout.toString(); + expect(output).toContain('SLUG='); + expect(output).toContain('BRANCH='); + }); + + test('SLUG does not contain forward slashes', () => { + const result = Bun.spawnSync([SLUG_BIN], { cwd: ROOT, stdout: 'pipe', stderr: 'pipe' }); + const slug = result.stdout.toString().match(/SLUG=(.*)/)?.[1] ?? ''; + expect(slug).not.toContain('/'); + expect(slug.length).toBeGreaterThan(0); + }); + + test('BRANCH does not contain forward slashes', () => { + const result = Bun.spawnSync([SLUG_BIN], { cwd: ROOT, stdout: 'pipe', stderr: 'pipe' }); + const branch = result.stdout.toString().match(/BRANCH=(.*)/)?.[1] ?? ''; + expect(branch).not.toContain('/'); + expect(branch.length).toBeGreaterThan(0); + }); + + test('output is eval-compatible (KEY=VALUE format)', () => { + const result = Bun.spawnSync([SLUG_BIN], { cwd: ROOT, stdout: 'pipe', stderr: 'pipe' }); + const lines = result.stdout.toString().trim().split('\n'); + expect(lines.length).toBe(2); + expect(lines[0]).toMatch(/^SLUG=.+/); + expect(lines[1]).toMatch(/^BRANCH=.+/); + }); +});