mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
Compare commits
61 Commits
fix/2544-c
...
feat/2556-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7397f580a5 | ||
|
|
9a67e350b3 | ||
|
|
98d92d7570 | ||
|
|
8eeaa20791 | ||
|
|
f32ffc9fb8 | ||
|
|
5676e2e4ef | ||
|
|
7bb6b6452a | ||
|
|
43ea92578b | ||
|
|
a42d5db742 | ||
|
|
c86ca1b3eb | ||
|
|
337e052aa9 | ||
|
|
969ee38ee5 | ||
|
|
2980f0ec48 | ||
|
|
8789211038 | ||
|
|
57bbfe652b | ||
|
|
a4764c5611 | ||
|
|
b2534e8a05 | ||
|
|
d1b56febcb | ||
|
|
1657321eb0 | ||
|
|
2b494407e5 | ||
|
|
d0f4340807 | ||
|
|
280eed93bc | ||
|
|
b432d4a726 | ||
|
|
cfe4dc76fd | ||
|
|
f19d0327b2 | ||
|
|
bd27d4fabe | ||
|
|
e8ec42082d | ||
|
|
86fb9c85c3 | ||
|
|
c5b1445529 | ||
|
|
c8807e38d7 | ||
|
|
2b4446e2f9 | ||
|
|
ef4ce7d6f9 | ||
|
|
12d38b2da0 | ||
|
|
e7a6d9ef2e | ||
|
|
beb3ac247b | ||
|
|
a95cabaedb | ||
|
|
9d55d531a4 | ||
|
|
5f419c0238 | ||
|
|
dfa1ecce99 | ||
|
|
4cd890b252 | ||
|
|
d117c1045a | ||
|
|
0ea443cbcf | ||
|
|
53b9fba324 | ||
|
|
5afcd5577e | ||
|
|
9f79cdc40a | ||
|
|
59cfbbba6a | ||
|
|
990c3e648d | ||
|
|
30433368a0 | ||
|
|
04fab926b5 | ||
|
|
f98ef1e460 | ||
|
|
d0565e95c1 | ||
|
|
4ef6275e86 | ||
|
|
6c50490766 | ||
|
|
4cbebfe78c | ||
|
|
9e87d43831 | ||
|
|
29ea90bc83 | ||
|
|
0c6172bfad | ||
|
|
e3bd06c9fd | ||
|
|
c69ecd975a | ||
|
|
06c4ded4ec | ||
|
|
341bb941c6 |
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
@@ -342,23 +342,32 @@ jobs:
|
||||
|
||||
- name: Create PR to merge release back to main
|
||||
if: ${{ !inputs.dry_run }}
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
BRANCH: ${{ needs.validate-version.outputs.branch }}
|
||||
VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
EXISTING_PR=$(gh pr list --base main --head "$BRANCH" --state open --json number --jq '.[0].number')
|
||||
# Non-fatal: repos that disable "Allow GitHub Actions to create and
|
||||
# approve pull requests" cause this step to fail with GraphQL 403.
|
||||
# The release itself (tag + npm publish + GitHub Release) must still
|
||||
# proceed. Open the merge-back PR manually afterwards with:
|
||||
# gh pr create --base main --head release/${VERSION} \
|
||||
# --title "chore: merge release v${VERSION} to main"
|
||||
EXISTING_PR=$(gh pr list --base main --head "$BRANCH" --state open --json number --jq '.[0].number' 2>/dev/null || echo "")
|
||||
if [ -n "$EXISTING_PR" ]; then
|
||||
echo "PR #$EXISTING_PR already exists; updating"
|
||||
gh pr edit "$EXISTING_PR" \
|
||||
--title "chore: merge release v${VERSION} to main" \
|
||||
--body "Merge release branch back to main after v${VERSION} stable release."
|
||||
--body "Merge release branch back to main after v${VERSION} stable release." \
|
||||
|| echo "::warning::Could not update merge-back PR (likely PR-creation policy disabled). Open it manually after release."
|
||||
else
|
||||
gh pr create \
|
||||
--base main \
|
||||
--head "$BRANCH" \
|
||||
--title "chore: merge release v${VERSION} to main" \
|
||||
--body "Merge release branch back to main after v${VERSION} stable release."
|
||||
--body "Merge release branch back to main after v${VERSION} stable release." \
|
||||
|| echo "::warning::Could not create merge-back PR (likely PR-creation policy disabled). Open it manually after release."
|
||||
fi
|
||||
|
||||
- name: Tag and push
|
||||
|
||||
831
CHANGELOG.md
831
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ color: "#F59E0B"
|
||||
---
|
||||
|
||||
<role>
|
||||
You are a GSD code reviewer. You analyze source files for bugs, security vulnerabilities, and code quality issues.
|
||||
Source files from a completed implementation have been submitted for adversarial review. Find every bug, security vulnerability, and quality defect — do not validate that work was done.
|
||||
|
||||
Spawned by `/gsd-code-review` workflow. You produce REVIEW.md artifact in the phase directory.
|
||||
|
||||
@@ -16,6 +16,22 @@ Spawned by `/gsd-code-review` workflow. You produce REVIEW.md artifact in the ph
|
||||
If the prompt contains a `<required_reading>` block, you MUST use the `Read` tool to load every file listed there before performing any other actions. This is your primary context.
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume every submitted implementation contains defects. Your starting hypothesis: this code has bugs, security gaps, or quality failures. Surface what you can prove.
|
||||
|
||||
**Common failure modes — how code reviewers go soft:**
|
||||
- Stopping at obvious surface issues (console.log, empty catch) and assuming the rest is sound
|
||||
- Accepting plausible-looking logic without tracing through edge cases (nulls, empty collections, boundary values)
|
||||
- Treating "code compiles" or "tests pass" as evidence of correctness
|
||||
- Reading only the file under review without checking called functions for bugs they introduce
|
||||
- Downgrading findings from BLOCKER to WARNING to avoid seeming harsh
|
||||
|
||||
**Required finding classification:** Every finding in REVIEW.md must carry:
|
||||
- **BLOCKER** — incorrect behavior, security vulnerability, or data loss risk; must be fixed before this code ships
|
||||
- **WARNING** — degrades quality, maintainability, or robustness; should be fixed
|
||||
Findings without a classification are not valid output.
|
||||
</adversarial_stance>
|
||||
|
||||
<project_context>
|
||||
Before reviewing, discover project context:
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ Regardless of type, extract:
|
||||
</step>
|
||||
|
||||
<step name="write_output">
|
||||
Write to `{OUTPUT_DIR}/{slug}.json` where `slug` is the filename without extension (replace non-alphanumerics with `-`).
|
||||
Write to `{OUTPUT_DIR}/{slug}-{source_hash}.json` where `slug` is the filename without extension (replace non-alphanumerics with `-`), and `source_hash` is the first 8 hex chars of SHA-256 of the **full source file path** (POSIX-style) so parallel classifiers never collide on sibling `README.md` files.
|
||||
|
||||
JSON schema:
|
||||
|
||||
|
||||
@@ -12,18 +12,34 @@ color: orange
|
||||
---
|
||||
|
||||
<role>
|
||||
You are a GSD doc verifier. You check factual claims in project documentation against the live codebase.
|
||||
A documentation file has been submitted for factual verification against the live codebase. Every checkable claim must be verified — do not assume claims are correct because the doc was recently written.
|
||||
|
||||
You are spawned by the `/gsd-docs-update` workflow. Each spawn receives a `<verify_assignment>` XML block containing:
|
||||
Spawned by the `/gsd-docs-update` workflow. Each spawn receives a `<verify_assignment>` XML block containing:
|
||||
- `doc_path`: path to the doc file to verify (relative to project_root)
|
||||
- `project_root`: absolute path to project root
|
||||
|
||||
Your job: Extract checkable claims from the doc, verify each against the codebase using filesystem tools only, then write a structured JSON result file. Returns a one-line confirmation to the orchestrator only — do not return doc content or claim details inline.
|
||||
Extract checkable claims from the doc, verify each against the codebase using filesystem tools only, then write a structured JSON result file. Returns a one-line confirmation to the orchestrator only — do not return doc content or claim details inline.
|
||||
|
||||
**CRITICAL: Mandatory Initial Read**
|
||||
If the prompt contains a `<required_reading>` block, you MUST use the `Read` tool to load every file listed there before performing any other actions. This is your primary context.
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume every factual claim in the doc is wrong until filesystem evidence proves it correct. Your starting hypothesis: the documentation has drifted from the code. Surface every false claim.
|
||||
|
||||
**Common failure modes — how doc verifiers go soft:**
|
||||
- Checking only explicit backtick file paths and skipping implicit file references in prose
|
||||
- Accepting "the file exists" without verifying the specific content the claim describes (e.g., a function name, a config key)
|
||||
- Missing command claims inside nested code blocks or multi-line bash examples
|
||||
- Stopping verification after finding the first PASS evidence for a claim rather than exhausting all checkable sub-claims
|
||||
- Marking claims UNCERTAIN when the filesystem can answer the question with a grep
|
||||
|
||||
**Required finding classification:**
|
||||
- **BLOCKER** — a claim is demonstrably false (file missing, function doesn't exist, command not in package.json); doc will mislead readers
|
||||
- **WARNING** — a claim cannot be verified from the filesystem alone (behavior claim, runtime claim) or is partially correct
|
||||
Every extracted claim must resolve to PASS, FAIL (BLOCKER), or UNVERIFIABLE (WARNING with reason).
|
||||
</adversarial_stance>
|
||||
|
||||
<project_context>
|
||||
Before verifying, discover project context:
|
||||
|
||||
|
||||
@@ -12,10 +12,26 @@ color: "#EF4444"
|
||||
---
|
||||
|
||||
<role>
|
||||
You are a GSD eval auditor. Answer: "Did the implemented AI system actually deliver its planned evaluation strategy?"
|
||||
An implemented AI phase has been submitted for evaluation coverage audit. Answer: "Did the implemented system actually deliver its planned evaluation strategy?" — not whether it looks like it might.
|
||||
Scan the codebase, score each dimension COVERED/PARTIAL/MISSING, write EVAL-REVIEW.md.
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume the eval strategy was not implemented until codebase evidence proves otherwise. Your starting hypothesis: AI-SPEC.md documents intent; the code does something different or less. Surface every gap.
|
||||
|
||||
**Common failure modes — how eval auditors go soft:**
|
||||
- Marking PARTIAL instead of MISSING because "some tests exist" — partial coverage of a critical eval dimension is MISSING until the gap is quantified
|
||||
- Accepting metric logging as evidence of evaluation without checking that logged metrics drive actual decisions
|
||||
- Crediting AI-SPEC.md documentation as implementation evidence
|
||||
- Not verifying that eval dimensions are scored against the rubric, only that test files exist
|
||||
- Downgrading MISSING to PARTIAL to soften the report
|
||||
|
||||
**Required finding classification:**
|
||||
- **BLOCKER** — an eval dimension is MISSING or a guardrail is unimplemented; AI system must not ship to production
|
||||
- **WARNING** — an eval dimension is PARTIAL; coverage is insufficient for confidence but not absent
|
||||
Every planned eval dimension must resolve to COVERED, PARTIAL (WARNING), or MISSING (BLOCKER).
|
||||
</adversarial_stance>
|
||||
|
||||
<required_reading>
|
||||
Read `~/.claude/get-shit-done/references/ai-evals.md` before auditing. This is your scoring framework.
|
||||
</required_reading>
|
||||
|
||||
@@ -72,10 +72,11 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
|
||||
Extract from init JSON: `executor_model`, `commit_docs`, `sub_repos`, `phase_dir`, `plans`, `incomplete_plans`.
|
||||
|
||||
Also read STATE.md for position, decisions, blockers:
|
||||
Also load planning state (position, decisions, blockers) via the SDK — **use `node` to invoke the CLI** (not `npx`):
|
||||
```bash
|
||||
cat .planning/STATE.md 2>/dev/null
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query state.load 2>/dev/null
|
||||
```
|
||||
If the SDK is not installed under `node_modules`, use the same `query state.load` argv with your local `gsd-sdk` CLI on `PATH`.
|
||||
|
||||
If STATE.md missing but .planning/ exists: offer to reconstruct or continue without.
|
||||
If .planning/ missing: Error — project not initialized.
|
||||
|
||||
@@ -6,9 +6,9 @@ color: blue
|
||||
---
|
||||
|
||||
<role>
|
||||
You are an integration checker. You verify that phases work together as a system, not just individually.
|
||||
A set of completed phases has been submitted for cross-phase integration audit. Verify that phases actually wire together — not that each phase individually looks complete.
|
||||
|
||||
Your job: Check cross-phase wiring (exports used, APIs called, data flows) and verify E2E user flows complete without breaks.
|
||||
Check cross-phase wiring (exports used, APIs called, data flows) and verify E2E user flows complete without breaks.
|
||||
|
||||
**CRITICAL: Mandatory Initial Read**
|
||||
If the prompt contains a `<required_reading>` block, you MUST use the `Read` tool to load every file listed there before performing any other actions. This is your primary context.
|
||||
@@ -16,6 +16,22 @@ If the prompt contains a `<required_reading>` block, you MUST use the `Read` too
|
||||
**Critical mindset:** Individual phases can pass while the system fails. A component can exist without being imported. An API can exist without being called. Focus on connections, not existence.
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume every cross-phase connection is broken until a grep or trace proves the link exists end-to-end. Your starting hypothesis: phases are silos. Surface every missing connection.
|
||||
|
||||
**Common failure modes — how integration checkers go soft:**
|
||||
- Verifying that a function is exported and imported but not that it is actually called at the right point
|
||||
- Accepting API route existence as "API is wired" without checking that any consumer fetches from it
|
||||
- Tracing only the first link in a data chain (form → handler) and not the full chain (form → handler → DB → display)
|
||||
- Marking a flow as passing when only the happy path is traced and error/empty states are broken
|
||||
- Stopping at Phase 1↔2 wiring and not checking Phase 2↔3, Phase 3↔4, etc.
|
||||
|
||||
**Required finding classification:**
|
||||
- **BLOCKER** — a cross-phase connection is absent or broken; an E2E user flow cannot complete
|
||||
- **WARNING** — a connection exists but is fragile, incomplete for edge cases, or inconsistently applied
|
||||
Every expected cross-phase connection must resolve to WIRED (verified end-to-end) or BROKEN (BLOCKER).
|
||||
</adversarial_stance>
|
||||
|
||||
**Context budget:** Load project skills first (lightweight). Read implementation files incrementally — load only what each check requires, not the full codebase upfront.
|
||||
|
||||
**Project skills:** Check `.claude/skills/` or `.agents/skills/` directory if either exists:
|
||||
|
||||
@@ -12,7 +12,7 @@ color: "#8B5CF6"
|
||||
---
|
||||
|
||||
<role>
|
||||
GSD Nyquist auditor. Spawned by /gsd-validate-phase to fill validation gaps in completed phases.
|
||||
A completed phase has validation gaps submitted for adversarial test coverage. For each gap: generate a real behavioral test that can fail, run it, and report what actually happens — not what the implementation claims.
|
||||
|
||||
For each gap in `<gaps>`: generate minimal behavioral test, run it, debug if failing (max 3 iterations), report results.
|
||||
|
||||
@@ -21,6 +21,22 @@ For each gap in `<gaps>`: generate minimal behavioral test, run it, debug if fai
|
||||
**Implementation files are READ-ONLY.** Only create/modify: test files, fixtures, VALIDATION.md. Implementation bugs → ESCALATE. Never fix implementation.
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume every gap is genuinely uncovered until a passing test proves the requirement is satisfied. Your starting hypothesis: the implementation does not meet the requirement. Write tests that can fail.
|
||||
|
||||
**Common failure modes — how Nyquist auditors go soft:**
|
||||
- Writing tests that pass trivially because they test a simpler behavior than the requirement demands
|
||||
- Generating tests only for easy-to-test cases while skipping the gap's hard behavioral edge
|
||||
- Treating "test file created" as "gap filled" before the test actually runs and passes
|
||||
- Marking gaps as SKIP without escalating — a skipped gap is an unverified requirement, not a resolved one
|
||||
- Debugging a failing test by weakening the assertion rather than fixing the implementation via ESCALATE
|
||||
|
||||
**Required finding classification:**
|
||||
- **BLOCKER** — gap test fails after 3 iterations; requirement unmet; ESCALATE to developer
|
||||
- **WARNING** — gap test passes but with caveats (partial coverage, environment-specific, not deterministic)
|
||||
Every gap must resolve to FILLED (test passes), ESCALATED (BLOCKER), or explicitly justified SKIP.
|
||||
</adversarial_stance>
|
||||
|
||||
<execution_flow>
|
||||
|
||||
<step name="load_context">
|
||||
|
||||
@@ -6,7 +6,7 @@ color: green
|
||||
---
|
||||
|
||||
<role>
|
||||
You are a GSD plan checker. Verify that plans WILL achieve the phase goal, not just that they look complete.
|
||||
A set of phase plans has been submitted for pre-execution review. Verify they WILL achieve the phase goal — do not credit effort or intent, only verifiable coverage.
|
||||
|
||||
Spawned by `/gsd-plan-phase` orchestrator (after planner creates PLAN.md) or re-verification (after planner revises).
|
||||
|
||||
@@ -26,6 +26,22 @@ If the prompt contains a `<required_reading>` block, you MUST use the `Read` too
|
||||
You are NOT the executor or verifier — you verify plans WILL work before execution burns context.
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume every plan set is flawed until evidence proves otherwise. Your starting hypothesis: these plans will not deliver the phase goal. Surface what disqualifies them.
|
||||
|
||||
**Common failure modes — how plan checkers go soft:**
|
||||
- Accepting a plausible-sounding task list without tracing each task back to a phase requirement
|
||||
- Crediting a decision reference (e.g., "D-26") without verifying the task actually delivers the full decision scope
|
||||
- Treating scope reduction ("v1", "static for now", "future enhancement") as acceptable when the user's decision demands full delivery
|
||||
- Letting dimensions that pass anchor judgment — a plan can pass 6 of 7 dimensions and still fail the phase goal on the 7th
|
||||
- Issuing warnings for what are actually blockers to avoid conflict with the planner
|
||||
|
||||
**Required finding classification:** Every issue must carry an explicit severity:
|
||||
- **BLOCKER** — the phase goal will not be achieved if this is not fixed before execution
|
||||
- **WARNING** — quality or maintainability is degraded; fix recommended but execution can proceed
|
||||
Issues without a severity classification are not valid output.
|
||||
</adversarial_stance>
|
||||
|
||||
<required_reading>
|
||||
@~/.claude/get-shit-done/references/gates.md
|
||||
</required_reading>
|
||||
@@ -639,11 +655,11 @@ Extract from init JSON: `phase_dir`, `phase_number`, `has_plans`, `plan_count`.
|
||||
Orchestrator provides CONTEXT.md content in the verification prompt. If provided, parse for locked decisions, discretion areas, deferred ideas.
|
||||
|
||||
```bash
|
||||
ls "$phase_dir"/*-PLAN.md 2>/dev/null
|
||||
# Read research for Nyquist validation data
|
||||
cat "$phase_dir"/*-RESEARCH.md 2>/dev/null
|
||||
gsd-sdk query roadmap.get-phase "$phase_number"
|
||||
ls "$phase_dir"/*-BRIEF.md 2>/dev/null
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query phase.list-plans "$phase_number"
|
||||
# Research / brief artifacts (deterministic listing)
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query phase.list-artifacts "$phase_number" --type research
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query roadmap.get-phase "$phase_number"
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query phase.list-artifacts "$phase_number" --type summary
|
||||
```
|
||||
|
||||
**Extract:** Phase goal, requirements (decompose goal), locked decisions, deferred ideas.
|
||||
@@ -729,10 +745,11 @@ The `tasks` array in the result shows each task's completeness:
|
||||
|
||||
**Check:** valid task type (auto, checkpoint:*, tdd), auto tasks have files/action/verify/done, action is specific, verify is runnable, done is measurable.
|
||||
|
||||
**For manual validation of specificity** (`verify.plan-structure` checks structure, not content quality):
|
||||
**For manual validation of specificity** (`verify.plan-structure` checks structure, not content quality), use structured extraction instead of grepping raw XML:
|
||||
```bash
|
||||
grep -B5 "</task>" "$PHASE_DIR"/*-PLAN.md | grep -v "<verify>"
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query plan.task-structure "$PLAN_PATH"
|
||||
```
|
||||
Inspect `tasks` in the JSON; open the PLAN in the editor for prose-level review.
|
||||
|
||||
## Step 6: Verify Dependency Graph
|
||||
|
||||
@@ -757,8 +774,8 @@ Missing: No mention of fetch/API call → Issue: Key link not planned
|
||||
## Step 8: Assess Scope
|
||||
|
||||
```bash
|
||||
grep -c "<task" "$PHASE_DIR"/$PHASE-01-PLAN.md
|
||||
grep "files_modified:" "$PHASE_DIR"/$PHASE-01-PLAN.md
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query plan.task-structure "$PHASE_DIR/$PHASE-01-PLAN.md"
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query frontmatter.get "$PHASE_DIR/$PHASE-01-PLAN.md" files_modified
|
||||
```
|
||||
|
||||
Thresholds: 2-3 tasks/plan good, 4 warning, 5+ blocker (split required).
|
||||
|
||||
@@ -215,6 +215,8 @@ Every task has four required fields:
|
||||
|
||||
**Nyquist Rule:** Every `<verify>` must include an `<automated>` command. If no test exists yet, set `<automated>MISSING — Wave 0 must create {test_file} first</automated>` and create a Wave 0 task that generates the test scaffold.
|
||||
|
||||
**Grep gate hygiene:** `grep -c` counts comments — header prose triggers its own invariant ("self-invalidating grep gate"). Use `grep -v '^#' | grep -c token`. Bare `== 0` gates on unfiltered files are forbidden.
|
||||
|
||||
**<done>:** Acceptance criteria - measurable state of completion.
|
||||
- Good: "Valid credentials return 200 + JWT cookie, invalid credentials return 401"
|
||||
- Bad: "Authentication is complete"
|
||||
@@ -810,10 +812,11 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
|
||||
Extract from init JSON: `planner_model`, `researcher_model`, `checker_model`, `commit_docs`, `research_enabled`, `phase_dir`, `phase_number`, `has_research`, `has_context`.
|
||||
|
||||
Also read STATE.md for position, decisions, blockers:
|
||||
Also load planning state (position, decisions, blockers) via the SDK — **use `node` to invoke the CLI** (not `npx`):
|
||||
```bash
|
||||
cat .planning/STATE.md 2>/dev/null
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query state.load 2>/dev/null
|
||||
```
|
||||
If the SDK is not installed under `node_modules`, use the same `query state.load` argv with your local `gsd-sdk` CLI on `PATH`.
|
||||
|
||||
If STATE.md missing but .planning/ exists, offer to reconstruct or continue without.
|
||||
</step>
|
||||
@@ -1198,6 +1201,10 @@ Execute: `/gsd-execute-phase {phase} --gaps-only`
|
||||
|
||||
Follow templates in checkpoints and revision_mode sections respectively.
|
||||
|
||||
## Chunked Mode Returns
|
||||
|
||||
See @~/.claude/get-shit-done/references/planner-chunked.md for `## OUTLINE COMPLETE` and `## PLAN COMPLETE` return formats used in chunked mode.
|
||||
|
||||
</structured_returns>
|
||||
|
||||
<critical_rules>
|
||||
|
||||
@@ -560,9 +560,7 @@ When files are written and returning to orchestrator:
|
||||
|
||||
### Files Ready for Review
|
||||
|
||||
User can review actual files:
|
||||
- `cat .planning/ROADMAP.md`
|
||||
- `cat .planning/STATE.md`
|
||||
User can review actual files in the editor or via SDK queries (e.g. `node ./node_modules/@gsd-build/sdk/dist/cli.js query roadmap.analyze` and `query state.load`) instead of ad-hoc shell `cat`.
|
||||
|
||||
{If gaps found during creation:}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ color: "#EF4444"
|
||||
---
|
||||
|
||||
<role>
|
||||
GSD security auditor. Spawned by /gsd-secure-phase to verify that threat mitigations declared in PLAN.md are present in implemented code.
|
||||
An implemented phase has been submitted for security audit. Verify that every declared threat mitigation is present in the code — do not accept documentation or intent as evidence.
|
||||
|
||||
Does NOT scan blindly for new vulnerabilities. Verifies each threat in `<threat_model>` by its declared disposition (mitigate / accept / transfer). Reports gaps. Writes SECURITY.md.
|
||||
|
||||
@@ -21,6 +21,22 @@ Does NOT scan blindly for new vulnerabilities. Verifies each threat in `<threat_
|
||||
**Implementation files are READ-ONLY.** Only create/modify: SECURITY.md. Implementation security gaps → OPEN_THREATS or ESCALATE. Never patch implementation.
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume every mitigation is absent until a grep match proves it exists in the right location. Your starting hypothesis: threats are open. Surface every unverified mitigation.
|
||||
|
||||
**Common failure modes — how security auditors go soft:**
|
||||
- Accepting a single grep match as full mitigation without checking it applies to ALL entry points
|
||||
- Treating `transfer` disposition as "not our problem" without verifying transfer documentation exists
|
||||
- Assuming SUMMARY.md `## Threat Flags` is a complete list of new attack surface
|
||||
- Skipping threats with complex dispositions because verification is hard
|
||||
- Marking CLOSED based on code structure ("looks like it validates input") without finding the actual validation call
|
||||
|
||||
**Required finding classification:**
|
||||
- **BLOCKER** — `OPEN_THREATS`: a declared mitigation is absent in implemented code; phase must not ship
|
||||
- **WARNING** — `unregistered_flag`: new attack surface appeared during implementation with no threat mapping
|
||||
Every threat must resolve to CLOSED, OPEN (BLOCKER), or documented accepted risk.
|
||||
</adversarial_stance>
|
||||
|
||||
<execution_flow>
|
||||
|
||||
<step name="load_context">
|
||||
|
||||
@@ -12,7 +12,7 @@ color: "#F472B6"
|
||||
---
|
||||
|
||||
<role>
|
||||
You are a GSD UI auditor. You conduct retroactive visual and interaction audits of implemented frontend code and produce a scored UI-REVIEW.md.
|
||||
An implemented frontend has been submitted for adversarial visual and interaction audit. Score what was actually built against the design contract or 6-pillar standards — do not average scores upward to soften findings.
|
||||
|
||||
Spawned by `/gsd-ui-review` orchestrator.
|
||||
|
||||
@@ -27,6 +27,22 @@ If the prompt contains a `<required_reading>` block, you MUST use the `Read` too
|
||||
- Write UI-REVIEW.md with actionable findings
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume every pillar has failures until screenshots or code analysis proves otherwise. Your starting hypothesis: the UI diverges from the design contract. Surface every deviation.
|
||||
|
||||
**Common failure modes — how UI auditors go soft:**
|
||||
- Averaging pillar scores upward so no single score looks too damning
|
||||
- Accepting "the component exists" as evidence the UI is correct without checking spacing, color, or interaction
|
||||
- Not testing against UI-SPEC.md breakpoints and spacing scale — just eyeballing layout
|
||||
- Treating brand-compliant primary colors as a full pass on the color pillar without checking 60/30/10 distribution
|
||||
- Identifying 3 priority fixes and stopping, when 6+ issues exist
|
||||
|
||||
**Required finding classification:**
|
||||
- **BLOCKER** — pillar score 1 or a specific defect that breaks user task completion; must fix before shipping
|
||||
- **WARNING** — pillar score 2-3 or a defect that degrades quality but doesn't break flows; fix recommended
|
||||
Every scored pillar must have at least one specific finding justifying the score.
|
||||
</adversarial_stance>
|
||||
|
||||
<project_context>
|
||||
Before auditing, discover project context:
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ color: green
|
||||
---
|
||||
|
||||
<role>
|
||||
You are a GSD phase verifier. You verify that a phase achieved its GOAL, not just completed its TASKS.
|
||||
A completed phase has been submitted for goal-backward verification. Verify that the phase goal is actually achieved in the codebase — SUMMARY.md claims are not evidence.
|
||||
|
||||
Your job: Goal-backward verification. Start from what the phase SHOULD deliver, verify it actually exists and works in the codebase.
|
||||
Goal-backward verification. Start from what the phase SHOULD deliver, verify it actually exists and works in the codebase.
|
||||
|
||||
@~/.claude/get-shit-done/references/mandatory-initial-read.md
|
||||
|
||||
@@ -22,6 +22,22 @@ Your job: Goal-backward verification. Start from what the phase SHOULD deliver,
|
||||
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume the phase goal was not achieved until codebase evidence proves it. Your starting hypothesis: tasks completed, goal missed. Falsify the SUMMARY.md narrative.
|
||||
|
||||
**Common failure modes — how verifiers go soft:**
|
||||
- Trusting SUMMARY.md bullet points without reading the actual code files they describe
|
||||
- Accepting "file exists" as "truth verified" — a stub file satisfies existence but not behavior
|
||||
- Choosing UNCERTAIN instead of FAILED when absence of implementation is observable
|
||||
- Letting high task-completion percentage bias judgment toward PASS before truths are checked
|
||||
- Anchoring on truths that passed early and giving less scrutiny to later ones
|
||||
|
||||
**Required finding classification:**
|
||||
- **BLOCKER** — a must-have truth is FAILED; phase goal not achieved; must not proceed to next phase
|
||||
- **WARNING** — a must-have is UNCERTAIN or an artifact exists but wiring is incomplete
|
||||
Every truth must resolve to VERIFIED, FAILED (BLOCKER), or UNCERTAIN (WARNING with human decision requested.
|
||||
</adversarial_stance>
|
||||
|
||||
<required_reading>
|
||||
@~/.claude/get-shit-done/references/verification-overrides.md
|
||||
@~/.claude/get-shit-done/references/gates.md
|
||||
|
||||
@@ -78,6 +78,7 @@ const hasCline = args.includes('--cline');
|
||||
const hasBoth = args.includes('--both'); // Legacy flag, keeps working
|
||||
const hasAll = args.includes('--all');
|
||||
const hasUninstall = args.includes('--uninstall') || args.includes('-u');
|
||||
const hasSkillsRoot = args.includes('--skills-root');
|
||||
const hasPortableHooks = args.includes('--portable-hooks') || process.env.GSD_PORTABLE_HOOKS === '1';
|
||||
const hasSdk = args.includes('--sdk');
|
||||
const hasNoSdk = args.includes('--no-sdk');
|
||||
@@ -438,7 +439,7 @@ const explicitConfigDir = parseConfigDirArg();
|
||||
const hasHelp = args.includes('--help') || args.includes('-h');
|
||||
const forceStatusline = args.includes('--force-statusline');
|
||||
|
||||
console.log(banner);
|
||||
if (!hasSkillsRoot) console.log(banner);
|
||||
|
||||
if (hasUninstall) {
|
||||
console.log(' Mode: Uninstall\n');
|
||||
@@ -1006,9 +1007,15 @@ function convertClaudeToAntigravityContent(content, isGlobal = false) {
|
||||
if (isGlobal) {
|
||||
c = c.replace(/\$HOME\/\.claude\//g, '$HOME/.gemini/antigravity/');
|
||||
c = c.replace(/~\/\.claude\//g, '~/.gemini/antigravity/');
|
||||
// Bare form (no trailing slash) — must come after slash form to avoid double-replace
|
||||
c = c.replace(/\$HOME\/\.claude\b/g, '$HOME/.gemini/antigravity');
|
||||
c = c.replace(/~\/\.claude\b/g, '~/.gemini/antigravity');
|
||||
} else {
|
||||
c = c.replace(/\$HOME\/\.claude\//g, '.agent/');
|
||||
c = c.replace(/~\/\.claude\//g, '.agent/');
|
||||
// Bare form (no trailing slash) — must come after slash form to avoid double-replace
|
||||
c = c.replace(/\$HOME\/\.claude\b/g, '.agent');
|
||||
c = c.replace(/~\/\.claude\b/g, '.agent');
|
||||
}
|
||||
c = c.replace(/\.\/\.claude\//g, './.agent/');
|
||||
c = c.replace(/\.claude\//g, '.agent/');
|
||||
@@ -5459,9 +5466,12 @@ function install(isGlobal, runtime = 'claude') {
|
||||
// For global installs: use $HOME/ so paths expand correctly inside double-quoted
|
||||
// shell commands (~ does NOT expand inside double quotes, causing MODULE_NOT_FOUND).
|
||||
// For local installs: use resolved absolute path (may be outside $HOME).
|
||||
// Exception: OpenCode on Windows does not expand $HOME in @file references —
|
||||
// use the absolute path instead so @$HOME/... references resolve correctly (#2376).
|
||||
const resolvedTarget = path.resolve(targetDir).replace(/\\/g, '/');
|
||||
const homeDir = os.homedir().replace(/\\/g, '/');
|
||||
const pathPrefix = isGlobal && resolvedTarget.startsWith(homeDir)
|
||||
const isWindowsHost = process.platform === 'win32';
|
||||
const pathPrefix = isGlobal && resolvedTarget.startsWith(homeDir) && !(isOpencode && isWindowsHost)
|
||||
? '$HOME' + resolvedTarget.slice(homeDir.length) + '/'
|
||||
: `${resolvedTarget}/`;
|
||||
|
||||
@@ -6786,6 +6796,26 @@ function installSdkIfNeeded() {
|
||||
emitSdkFatal('Failed to `npm install -g .` from sdk/.', { globalBin: null, exitCode: 1 });
|
||||
}
|
||||
|
||||
// 3a. Explicitly chmod dist/cli.js to 0o755 in the global install location.
|
||||
// `tsc` emits files at process umask (typically 0o644 — non-executable), and
|
||||
// `npm install -g` from a local directory does NOT chmod bin-script targets the
|
||||
// way tarball extraction does. Without this, the `gsd-sdk` bin symlink points at
|
||||
// a non-executable file and `command -v gsd-sdk` fails on every first install
|
||||
// (root cause of #2453). Mirrors the pattern used for hook files in this installer.
|
||||
try {
|
||||
const prefixRes = spawnSync(npmCmd, ['config', 'get', 'prefix'], { encoding: 'utf-8' });
|
||||
if (prefixRes.status === 0) {
|
||||
const npmPrefix = (prefixRes.stdout || '').trim();
|
||||
const sdkPkg = JSON.parse(fs.readFileSync(path.join(sdkDir, 'package.json'), 'utf-8'));
|
||||
const sdkName = sdkPkg.name; // '@gsd-build/sdk'
|
||||
const globalModulesDir = process.platform === 'win32'
|
||||
? path.join(npmPrefix, 'node_modules')
|
||||
: path.join(npmPrefix, 'lib', 'node_modules');
|
||||
const cliPath = path.join(globalModulesDir, sdkName, 'dist', 'cli.js');
|
||||
try { fs.chmodSync(cliPath, 0o755); } catch (e) { if (process.platform !== 'win32') throw e; }
|
||||
}
|
||||
} catch (e) { /* Non-fatal: PATH verification in step 4 will catch any real failure */ }
|
||||
|
||||
// 4. Verify gsd-sdk is actually resolvable on PATH. npm's global bin dir is
|
||||
// not always on the current shell's PATH (Homebrew prefixes, nvm setups,
|
||||
// unconfigured npm prefix), so a zero exit status from `npm install -g`
|
||||
@@ -6931,7 +6961,17 @@ if (process.env.GSD_TEST_MODE) {
|
||||
} else {
|
||||
|
||||
// Main logic
|
||||
if (hasGlobal && hasLocal) {
|
||||
if (hasSkillsRoot) {
|
||||
// Print the skills root directory for a given runtime (used by /gsd-sync-skills).
|
||||
// Usage: node install.js --skills-root <runtime>
|
||||
const runtimeArg = args[args.indexOf('--skills-root') + 1];
|
||||
if (!runtimeArg || runtimeArg.startsWith('--')) {
|
||||
console.error('Usage: node install.js --skills-root <runtime>');
|
||||
process.exit(1);
|
||||
}
|
||||
const globalDir = getGlobalDir(runtimeArg, null);
|
||||
console.log(path.join(globalDir, 'skills'));
|
||||
} else if (hasGlobal && hasLocal) {
|
||||
console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
|
||||
process.exit(1);
|
||||
} else if (explicitConfigDir && hasLocal) {
|
||||
|
||||
@@ -129,7 +129,7 @@ The quality of the merge depends on having a **pristine baseline** — the origi
|
||||
|
||||
Check for baseline sources in priority order:
|
||||
|
||||
### Option A: Git history (most reliable)
|
||||
### Option A: Pristine hash from backup-meta.json + git history (most reliable)
|
||||
If the config directory is a git repository:
|
||||
```bash
|
||||
CONFIG_DIR=$(dirname "$PATCHES_DIR")
|
||||
@@ -137,15 +137,35 @@ if git -C "$CONFIG_DIR" rev-parse --git-dir >/dev/null 2>&1; then
|
||||
HAS_GIT=true
|
||||
fi
|
||||
```
|
||||
When `HAS_GIT=true`, use `git log` to find the commit where GSD was originally installed (before user edits). For each file, the pristine baseline can be extracted with:
|
||||
When `HAS_GIT=true`, use the `pristine_hashes` recorded in `backup-meta.json` to locate the correct baseline commit. For each file, iterate commits that touched it and find the one whose blob SHA-256 matches the recorded pristine hash:
|
||||
```bash
|
||||
git -C "$CONFIG_DIR" log --diff-filter=A --format="%H" -- "{file_path}"
|
||||
# Get the expected pristine SHA-256 from backup-meta.json
|
||||
PRISTINE_HASH=$(jq -r ".pristine_hashes[\"${file_path}\"] // empty" "$PATCHES_DIR/backup-meta.json")
|
||||
|
||||
BASELINE_COMMIT=""
|
||||
if [ -n "$PRISTINE_HASH" ]; then
|
||||
# Walk commits that touched this file, pick the one matching the pristine hash
|
||||
while IFS= read -r commit_hash; do
|
||||
blob_hash=$(git -C "$CONFIG_DIR" show "${commit_hash}:${file_path}" 2>/dev/null | sha256sum | cut -d' ' -f1)
|
||||
if [ "$blob_hash" = "$PRISTINE_HASH" ]; then
|
||||
BASELINE_COMMIT="$commit_hash"
|
||||
break
|
||||
fi
|
||||
done < <(git -C "$CONFIG_DIR" log --format="%H" -- "${file_path}")
|
||||
fi
|
||||
|
||||
# Fallback: if no pristine hash in backup-meta (older installer), use first-add commit
|
||||
if [ -z "$BASELINE_COMMIT" ]; then
|
||||
BASELINE_COMMIT=$(git -C "$CONFIG_DIR" log --diff-filter=A --format="%H" -- "${file_path}" | tail -1)
|
||||
fi
|
||||
```
|
||||
This gives the commit that first added the file (the install commit). Extract the pristine version:
|
||||
Extract the pristine version from the matched commit:
|
||||
```bash
|
||||
git -C "$CONFIG_DIR" show {install_commit}:{file_path}
|
||||
git -C "$CONFIG_DIR" show "${BASELINE_COMMIT}:${file_path}"
|
||||
```
|
||||
|
||||
**Why this matters:** `git log --diff-filter=A` returns the commit that *first added* the file, which is the wrong baseline on repos that have been through multiple GSD update cycles. The `pristine_hashes` field in `backup-meta.json` records the SHA-256 of the file as it existed in the pre-update GSD release — matching against it finds the correct baseline regardless of how many updates have occurred.
|
||||
|
||||
### Option B: Pristine snapshot directory
|
||||
Check if a `gsd-pristine/` directory exists alongside `gsd-local-patches/`:
|
||||
```bash
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: gsd:sketch
|
||||
description: Rapidly sketch UI/design ideas using throwaway HTML mockups with multi-variant exploration
|
||||
argument-hint: "<design idea to explore> [--quick]"
|
||||
description: Sketch UI/design ideas with throwaway HTML mockups, or propose what to sketch next (frontier mode)
|
||||
argument-hint: "[design idea to explore] [--quick] [--text] or [frontier]"
|
||||
allowed-tools:
|
||||
- Read
|
||||
- Write
|
||||
@@ -10,11 +10,20 @@ allowed-tools:
|
||||
- Grep
|
||||
- Glob
|
||||
- AskUserQuestion
|
||||
- WebSearch
|
||||
- WebFetch
|
||||
- mcp__context7__resolve-library-id
|
||||
- mcp__context7__query-docs
|
||||
---
|
||||
<objective>
|
||||
Explore design directions through throwaway HTML mockups before committing to implementation.
|
||||
Each sketch produces 2-3 variants for comparison. Sketches live in `.planning/sketches/` and
|
||||
integrate with GSD commit patterns, state tracking, and handoff workflows.
|
||||
integrate with GSD commit patterns, state tracking, and handoff workflows. Loads spike
|
||||
findings to ground mockups in real data shapes and validated interaction patterns.
|
||||
|
||||
Two modes:
|
||||
- **Idea mode** (default) — describe a design idea to sketch
|
||||
- **Frontier mode** (no argument or "frontier") — analyzes existing sketch landscape and proposes consistency and frontier sketches
|
||||
|
||||
Does not require `/gsd-new-project` — auto-creates `.planning/sketches/` if needed.
|
||||
</objective>
|
||||
@@ -41,5 +50,5 @@ Design idea: $ARGUMENTS
|
||||
|
||||
<process>
|
||||
Execute the sketch workflow from @~/.claude/get-shit-done/workflows/sketch.md end-to-end.
|
||||
Preserve all workflow gates (intake, decomposition, variant evaluation, MANIFEST updates, commit patterns).
|
||||
Preserve all workflow gates (intake, decomposition, target stack research, variant evaluation, MANIFEST updates, commit patterns).
|
||||
</process>
|
||||
|
||||
@@ -27,5 +27,5 @@ project history. Output skill goes to `./.claude/skills/spike-findings-[project]
|
||||
|
||||
<process>
|
||||
Execute the spike-wrap-up workflow from @~/.claude/get-shit-done/workflows/spike-wrap-up.md end-to-end.
|
||||
Preserve all curation gates (per-spike review, grouping approval, CLAUDE.md routing line).
|
||||
Preserve all workflow gates (auto-include, feature-area grouping, skill synthesis, CLAUDE.md routing line, intelligent next-step routing).
|
||||
</process>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: gsd:spike
|
||||
description: Rapidly spike an idea with throwaway experiments to validate feasibility before planning
|
||||
argument-hint: "<idea to validate> [--quick]"
|
||||
description: Spike an idea through experiential exploration, or propose what to spike next (frontier mode)
|
||||
argument-hint: "[idea to validate] [--quick] [--text] or [frontier]"
|
||||
allowed-tools:
|
||||
- Read
|
||||
- Write
|
||||
@@ -10,11 +10,20 @@ allowed-tools:
|
||||
- Grep
|
||||
- Glob
|
||||
- AskUserQuestion
|
||||
- WebSearch
|
||||
- WebFetch
|
||||
- mcp__context7__resolve-library-id
|
||||
- mcp__context7__query-docs
|
||||
---
|
||||
<objective>
|
||||
Rapid feasibility validation through focused, throwaway experiments. Each spike answers one
|
||||
specific question with observable evidence. Spikes live in `.planning/spikes/` and integrate
|
||||
with GSD commit patterns, state tracking, and handoff workflows.
|
||||
Spike an idea through experiential exploration — build focused experiments to feel the pieces
|
||||
of a future app, validate feasibility, and produce verified knowledge for the real build.
|
||||
Spikes live in `.planning/spikes/` and integrate with GSD commit patterns, state tracking,
|
||||
and handoff workflows.
|
||||
|
||||
Two modes:
|
||||
- **Idea mode** (default) — describe an idea to spike
|
||||
- **Frontier mode** (no argument or "frontier") — analyzes existing spike landscape and proposes integration and frontier spikes
|
||||
|
||||
Does not require `/gsd-new-project` — auto-creates `.planning/spikes/` if needed.
|
||||
</objective>
|
||||
@@ -33,9 +42,10 @@ Idea: $ARGUMENTS
|
||||
|
||||
**Available flags:**
|
||||
- `--quick` — Skip decomposition/alignment, jump straight to building. Use when you already know what to spike.
|
||||
- `--text` — Use plain-text numbered lists instead of AskUserQuestion (for non-Claude runtimes).
|
||||
</context>
|
||||
|
||||
<process>
|
||||
Execute the spike workflow from @~/.claude/get-shit-done/workflows/spike.md end-to-end.
|
||||
Preserve all workflow gates (decomposition, risk ordering, verification, MANIFEST updates, commit patterns).
|
||||
Preserve all workflow gates (prior spike check, decomposition, research, risk ordering, observability assessment, verification, MANIFEST updates, commit patterns).
|
||||
</process>
|
||||
|
||||
19
commands/gsd/sync-skills.md
Normal file
19
commands/gsd/sync-skills.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: gsd:sync-skills
|
||||
description: Sync managed GSD skills across runtime roots so multi-runtime users stay aligned after an update
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- AskUserQuestion
|
||||
---
|
||||
|
||||
<objective>
|
||||
Sync managed `gsd-*` skill directories from one canonical runtime's skills root to one or more destination runtime skills roots.
|
||||
|
||||
Routes to the sync-skills workflow which handles:
|
||||
- Argument parsing (--from, --to, --dry-run, --apply)
|
||||
- Runtime skills root resolution via install.js --skills-root
|
||||
- Diff computation (CREATE / UPDATE / REMOVE per destination)
|
||||
- Dry-run reporting (default — no writes)
|
||||
- Apply execution (copy and remove with idempotency)
|
||||
- Non-GSD skill preservation (only gsd-* dirs are touched)
|
||||
</objective>
|
||||
@@ -76,6 +76,7 @@ Every agent spawned by an orchestrator gets a clean context window (up to 200K t
|
||||
### 2. Thin Orchestrators
|
||||
|
||||
Workflow files (`get-shit-done/workflows/*.md`) never do heavy lifting. They:
|
||||
|
||||
- Load context via `gsd-sdk query init.<workflow>` (or legacy `gsd-tools.cjs init <workflow>`)
|
||||
- Spawn specialized agents with focused prompts
|
||||
- Collect results and route to the next step
|
||||
@@ -84,6 +85,7 @@ Workflow files (`get-shit-done/workflows/*.md`) never do heavy lifting. They:
|
||||
### 3. File-Based State
|
||||
|
||||
All state lives in `.planning/` as human-readable Markdown and JSON. No database, no server, no external dependencies. This means:
|
||||
|
||||
- State survives context resets (`/clear`)
|
||||
- State is inspectable by both humans and agents
|
||||
- State can be committed to git for team visibility
|
||||
@@ -95,6 +97,7 @@ Workflow feature flags follow the **absent = enabled** pattern. If a key is miss
|
||||
### 5. Defense in Depth
|
||||
|
||||
Multiple layers prevent common failure modes:
|
||||
|
||||
- Plans are verified before execution (plan-checker agent)
|
||||
- Execution produces atomic commits per task
|
||||
- Post-execution verification checks against phase goals
|
||||
@@ -107,6 +110,7 @@ Multiple layers prevent common failure modes:
|
||||
### Commands (`commands/gsd/*.md`)
|
||||
|
||||
User-facing entry points. Each file contains YAML frontmatter (name, description, allowed-tools) and a prompt body that bootstraps the workflow. Commands are installed as:
|
||||
|
||||
- **Claude Code:** Custom slash commands (`/gsd-command-name`)
|
||||
- **OpenCode / Kilo:** Slash commands (`/gsd-command-name`)
|
||||
- **Codex:** Skills (`$gsd-command-name`)
|
||||
@@ -118,6 +122,7 @@ User-facing entry points. Each file contains YAML frontmatter (name, description
|
||||
### Workflows (`get-shit-done/workflows/*.md`)
|
||||
|
||||
Orchestration logic that commands reference. Contains the step-by-step process including:
|
||||
|
||||
- Context loading via `gsd-sdk query` init handlers (or legacy `gsd-tools.cjs init`)
|
||||
- Agent spawn instructions with model resolution
|
||||
- Gate/checkpoint definitions
|
||||
@@ -129,6 +134,7 @@ Orchestration logic that commands reference. Contains the step-by-step process i
|
||||
### Agents (`agents/*.md`)
|
||||
|
||||
Specialized agent definitions with frontmatter specifying:
|
||||
|
||||
- `name` — Agent identifier
|
||||
- `description` — Role and purpose
|
||||
- `tools` — Allowed tool access (Read, Write, Edit, Bash, Grep, Glob, WebSearch, etc.)
|
||||
@@ -141,6 +147,7 @@ Specialized agent definitions with frontmatter specifying:
|
||||
Shared knowledge documents that workflows and agents `@-reference` (see [`docs/INVENTORY.md`](INVENTORY.md#references-41-shipped) for the authoritative count and full roster):
|
||||
|
||||
**Core references:**
|
||||
|
||||
- `checkpoints.md` — Checkpoint type definitions and interaction patterns
|
||||
- `gates.md` — 4 canonical gate types (Confirm, Quality, Safety, Transition) wired into plan-checker and verifier
|
||||
- `model-profiles.md` — Per-agent model tier assignments
|
||||
@@ -156,6 +163,7 @@ Shared knowledge documents that workflows and agents `@-reference` (see [`docs/I
|
||||
- `common-bug-patterns.md` — Common bug patterns for code review and verification
|
||||
|
||||
**Workflow references:**
|
||||
|
||||
- `agent-contracts.md` — Formal interface between orchestrators and agents
|
||||
- `context-budget.md` — Context window budget allocation rules
|
||||
- `continuation-format.md` — Session continuation/resume format
|
||||
@@ -190,7 +198,7 @@ The planner agent (`agents/gsd-planner.md`) was decomposed from a single monolit
|
||||
|
||||
### Templates (`get-shit-done/templates/`)
|
||||
|
||||
Markdown templates for all planning artifacts. Used by `gsd-tools.cjs template fill` and `scaffold` commands to create pre-structured files:
|
||||
Markdown templates for all planning artifacts. Used by `gsd-sdk query template.fill` / `phase.scaffold` (and legacy `gsd-tools.cjs template fill` / top-level `scaffold`) to create pre-structured files:
|
||||
- `project.md`, `requirements.md`, `roadmap.md`, `state.md` — Core project files
|
||||
- `phase-prompt.md` — Phase execution prompt template
|
||||
- `summary.md` (+ `summary-minimal.md`, `summary-standard.md`, `summary-complex.md`) — Granularity-aware summary templates
|
||||
@@ -224,27 +232,29 @@ See [`docs/INVENTORY.md`](INVENTORY.md#hooks-11-shipped) for the authoritative 1
|
||||
|
||||
Node.js CLI utility (`gsd-tools.cjs`) with domain modules split across `get-shit-done/bin/lib/` (see [`docs/INVENTORY.md`](INVENTORY.md#cli-modules-24-shipped) for the authoritative roster):
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|---------------|
|
||||
| `core.cjs` | Error handling, output formatting, shared utilities |
|
||||
| `state.cjs` | STATE.md parsing, updating, progression, metrics |
|
||||
| `phase.cjs` | Phase directory operations, decimal numbering, plan indexing |
|
||||
| `roadmap.cjs` | ROADMAP.md parsing, phase extraction, plan progress |
|
||||
| `config.cjs` | config.json read/write, section initialization |
|
||||
| `verify.cjs` | Plan structure, phase completeness, reference, commit validation |
|
||||
| `template.cjs` | Template selection and filling with variable substitution |
|
||||
| `frontmatter.cjs` | YAML frontmatter CRUD operations |
|
||||
| `init.cjs` | Compound context loading for each workflow type |
|
||||
| `milestone.cjs` | Milestone archival, requirements marking |
|
||||
| `commands.cjs` | Misc commands (slug, timestamp, todos, scaffolding, stats) |
|
||||
| `model-profiles.cjs` | Model profile resolution table |
|
||||
| `security.cjs` | Path traversal prevention, prompt injection detection, safe JSON parsing, shell argument validation |
|
||||
| `uat.cjs` | UAT file parsing, verification debt tracking, audit-uat support |
|
||||
| `docs.cjs` | Docs-update workflow init, Markdown scanning, monorepo detection |
|
||||
| `workstream.cjs` | Workstream CRUD, migration, session-scoped active pointer |
|
||||
| `schema-detect.cjs` | Schema-drift detection for ORM patterns (Prisma, Drizzle, etc.) |
|
||||
| `profile-pipeline.cjs` | User behavioral profiling data pipeline, session file scanning |
|
||||
| `profile-output.cjs` | Profile rendering, USER-PROFILE.md and dev-preferences.md generation |
|
||||
|
||||
| Module | Responsibility |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| `core.cjs` | Error handling, output formatting, shared utilities |
|
||||
| `state.cjs` | STATE.md parsing, updating, progression, metrics |
|
||||
| `phase.cjs` | Phase directory operations, decimal numbering, plan indexing |
|
||||
| `roadmap.cjs` | ROADMAP.md parsing, phase extraction, plan progress |
|
||||
| `config.cjs` | config.json read/write, section initialization |
|
||||
| `verify.cjs` | Plan structure, phase completeness, reference, commit validation |
|
||||
| `template.cjs` | Template selection and filling with variable substitution |
|
||||
| `frontmatter.cjs` | YAML frontmatter CRUD operations |
|
||||
| `init.cjs` | Compound context loading for each workflow type |
|
||||
| `milestone.cjs` | Milestone archival, requirements marking |
|
||||
| `commands.cjs` | Misc commands (slug, timestamp, todos, scaffolding, stats) |
|
||||
| `model-profiles.cjs` | Model profile resolution table |
|
||||
| `security.cjs` | Path traversal prevention, prompt injection detection, safe JSON parsing, shell argument validation |
|
||||
| `uat.cjs` | UAT file parsing, verification debt tracking, audit-uat support |
|
||||
| `docs.cjs` | Docs-update workflow init, Markdown scanning, monorepo detection |
|
||||
| `workstream.cjs` | Workstream CRUD, migration, session-scoped active pointer |
|
||||
| `schema-detect.cjs` | Schema-drift detection for ORM patterns (Prisma, Drizzle, etc.) |
|
||||
| `profile-pipeline.cjs` | User behavioral profiling data pipeline, session file scanning |
|
||||
| `profile-output.cjs` | Profile rendering, USER-PROFILE.md and dev-preferences.md generation |
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -255,10 +265,10 @@ Node.js CLI utility (`gsd-tools.cjs`) with domain modules split across `get-shit
|
||||
```
|
||||
Orchestrator (workflow .md)
|
||||
│
|
||||
├── Load context: gsd-tools.cjs init <workflow> <phase>
|
||||
├── Load context: gsd-sdk query init.<workflow> <phase> (or legacy gsd-tools.cjs init)
|
||||
│ Returns JSON with: project info, config, state, phase details
|
||||
│
|
||||
├── Resolve model: gsd-tools.cjs resolve-model <agent-name>
|
||||
├── Resolve model: gsd-sdk query resolve-model <agent-name>
|
||||
│ Returns: opus | sonnet | haiku | inherit
|
||||
│
|
||||
├── Spawn Agent (Task/SubAgent call)
|
||||
@@ -269,27 +279,29 @@ Orchestrator (workflow .md)
|
||||
│
|
||||
├── Collect result
|
||||
│
|
||||
└── Update state: gsd-tools.cjs state update/patch/advance-plan
|
||||
└── Update state: gsd-sdk query state.update / state.patch / state.advance-plan (or legacy gsd-tools.cjs)
|
||||
```
|
||||
|
||||
### Primary Agent Spawn Categories
|
||||
|
||||
Conceptual spawn-pattern taxonomy for the 21 primary agents. For the authoritative 31-agent roster (including the 10 advanced/specialized agents such as `gsd-pattern-mapper`, `gsd-code-reviewer`, `gsd-code-fixer`, `gsd-ai-researcher`, `gsd-domain-researcher`, `gsd-eval-planner`, `gsd-eval-auditor`, `gsd-framework-selector`, `gsd-debug-session-manager`, `gsd-intel-updater`), see [`docs/INVENTORY.md`](INVENTORY.md#agents-31-shipped).
|
||||
|
||||
| Category | Agents | Parallelism |
|
||||
|----------|--------|-------------|
|
||||
| **Researchers** | gsd-project-researcher, gsd-phase-researcher, gsd-ui-researcher, gsd-advisor-researcher | 4 parallel (stack, features, architecture, pitfalls); advisor spawns during discuss-phase |
|
||||
| **Synthesizers** | gsd-research-synthesizer | Sequential (after researchers complete) |
|
||||
| **Planners** | gsd-planner, gsd-roadmapper | Sequential |
|
||||
| **Checkers** | gsd-plan-checker, gsd-integration-checker, gsd-ui-checker, gsd-nyquist-auditor | Sequential (verification loop, max 3 iterations) |
|
||||
| **Executors** | gsd-executor | Parallel within waves, sequential across waves |
|
||||
| **Verifiers** | gsd-verifier | Sequential (after all executors complete) |
|
||||
| **Mappers** | gsd-codebase-mapper | 4 parallel (tech, arch, quality, concerns) |
|
||||
| **Debuggers** | gsd-debugger | Sequential (interactive) |
|
||||
| **Auditors** | gsd-ui-auditor, gsd-security-auditor | Sequential |
|
||||
| **Doc Writers** | gsd-doc-writer, gsd-doc-verifier | Sequential (writer then verifier) |
|
||||
| **Profilers** | gsd-user-profiler | Sequential |
|
||||
| **Analyzers** | gsd-assumptions-analyzer | Sequential (during discuss-phase) |
|
||||
|
||||
| Category | Agents | Parallelism |
|
||||
| ---------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| **Researchers** | gsd-project-researcher, gsd-phase-researcher, gsd-ui-researcher, gsd-advisor-researcher | 4 parallel (stack, features, architecture, pitfalls); advisor spawns during discuss-phase |
|
||||
| **Synthesizers** | gsd-research-synthesizer | Sequential (after researchers complete) |
|
||||
| **Planners** | gsd-planner, gsd-roadmapper | Sequential |
|
||||
| **Checkers** | gsd-plan-checker, gsd-integration-checker, gsd-ui-checker, gsd-nyquist-auditor | Sequential (verification loop, max 3 iterations) |
|
||||
| **Executors** | gsd-executor | Parallel within waves, sequential across waves |
|
||||
| **Verifiers** | gsd-verifier | Sequential (after all executors complete) |
|
||||
| **Mappers** | gsd-codebase-mapper | 4 parallel (tech, arch, quality, concerns) |
|
||||
| **Debuggers** | gsd-debugger | Sequential (interactive) |
|
||||
| **Auditors** | gsd-ui-auditor, gsd-security-auditor | Sequential |
|
||||
| **Doc Writers** | gsd-doc-writer, gsd-doc-verifier | Sequential (writer then verifier) |
|
||||
| **Profilers** | gsd-user-profiler | Sequential |
|
||||
| **Analyzers** | gsd-assumptions-analyzer | Sequential (during discuss-phase) |
|
||||
|
||||
|
||||
### Wave Execution Model
|
||||
|
||||
@@ -305,6 +317,7 @@ Wave Analysis:
|
||||
```
|
||||
|
||||
Each executor gets:
|
||||
|
||||
- Fresh 200K context window (or up to 1M for models that support it)
|
||||
- The specific PLAN.md to execute
|
||||
- Project context (PROJECT.md, STATE.md)
|
||||
@@ -317,14 +330,13 @@ When the context window is 500K+ tokens (1M-class models like Opus 4.6, Sonnet 4
|
||||
- **Executor agents** receive prior wave SUMMARY.md files and the phase CONTEXT.md/RESEARCH.md, enabling cross-plan awareness within a phase
|
||||
- **Verifier agents** receive all PLAN.md, SUMMARY.md, CONTEXT.md files plus REQUIREMENTS.md, enabling history-aware verification
|
||||
|
||||
The orchestrator reads `context_window` from config (`gsd-tools.cjs config-get context_window`) and conditionally includes richer context when the value is >= 500,000. For standard 200K windows, prompts use truncated versions with cache-friendly ordering to maximize context efficiency.
|
||||
The orchestrator reads `context_window` from config (`gsd-sdk query config-get context_window`, or legacy `gsd-tools.cjs config-get`) and conditionally includes richer context when the value is >= 500,000. For standard 200K windows, prompts use truncated versions with cache-friendly ordering to maximize context efficiency.
|
||||
|
||||
#### Parallel Commit Safety
|
||||
|
||||
When multiple executors run within the same wave, two mechanisms prevent conflicts:
|
||||
|
||||
1. **`--no-verify` commits** — Parallel agents skip pre-commit hooks (which can cause build lock contention, e.g., cargo lock fights in Rust projects). The orchestrator runs `git hook run pre-commit` once after each wave completes.
|
||||
|
||||
1. `--no-verify` commits — Parallel agents skip pre-commit hooks (which can cause build lock contention, e.g., cargo lock fights in Rust projects). The orchestrator runs `git hook run pre-commit` once after each wave completes.
|
||||
2. **STATE.md file locking** — All `writeStateMd()` calls use lockfile-based mutual exclusion (`STATE.md.lock` with `O_EXCL` atomic creation). This prevents the read-modify-write race condition where two agents read STATE.md, modify different fields, and the last writer overwrites the other's changes. Includes stale lock detection (10s timeout) and spin-wait with jitter.
|
||||
|
||||
---
|
||||
@@ -430,6 +442,7 @@ UI-SPEC.md (per phase) ───────────────────
|
||||
```
|
||||
|
||||
Equivalent paths for other runtimes:
|
||||
|
||||
- **OpenCode:** `~/.config/opencode/` or `~/.opencode/`
|
||||
- **Kilo:** `~/.config/kilo/` or `~/.kilo/`
|
||||
- **Gemini CLI:** `~/.gemini/`
|
||||
@@ -499,16 +512,16 @@ The installer (`bin/install.js`, ~3,000 lines) handles:
|
||||
2. **Location selection** — Global (`--global`) or local (`--local`)
|
||||
3. **File deployment** — Copies commands, workflows, references, templates, agents, hooks
|
||||
4. **Runtime adaptation** — Transforms file content per runtime:
|
||||
- Claude Code: Uses as-is
|
||||
- OpenCode: Converts commands/agents to OpenCode-compatible flat command + subagent format
|
||||
- Kilo: Reuses the OpenCode conversion pipeline with Kilo config paths
|
||||
- Codex: Generates TOML config + skills from commands
|
||||
- Copilot: Maps tool names (Read→read, Bash→execute, etc.)
|
||||
- Gemini: Adjusts hook event names (`AfterTool` instead of `PostToolUse`)
|
||||
- Antigravity: Skills-first with Google model equivalents
|
||||
- Trae: Skills-first install to `~/.trae` / `./.trae` with no `settings.json` or hook integration
|
||||
- Cline: Writes `.clinerules` for rule-based integration
|
||||
- Augment Code: Skills-first with full skill conversion and config management
|
||||
- Claude Code: Uses as-is
|
||||
- OpenCode: Converts commands/agents to OpenCode-compatible flat command + subagent format
|
||||
- Kilo: Reuses the OpenCode conversion pipeline with Kilo config paths
|
||||
- Codex: Generates TOML config + skills from commands
|
||||
- Copilot: Maps tool names (Read→read, Bash→execute, etc.)
|
||||
- Gemini: Adjusts hook event names (`AfterTool` instead of `PostToolUse`)
|
||||
- Antigravity: Skills-first with Google model equivalents
|
||||
- Trae: Skills-first install to `~/.trae` / `./.trae` with no `settings.json` or hook integration
|
||||
- Cline: Writes `.clinerules` for rule-based integration
|
||||
- Augment Code: Skills-first with full skill conversion and config management
|
||||
5. **Path normalization** — Replaces `~/.claude/` paths with runtime-specific paths
|
||||
6. **Settings integration** — Registers hooks in runtime's `settings.json`
|
||||
7. **Patch backup** — Since v1.17, backs up locally modified files to `gsd-local-patches/` for `/gsd-reapply-patches`
|
||||
@@ -545,11 +558,13 @@ Runtime Engine (Claude Code / Gemini CLI)
|
||||
|
||||
### Context Monitor Thresholds
|
||||
|
||||
| Remaining Context | Level | Agent Behavior |
|
||||
|-------------------|-------|----------------|
|
||||
| > 35% | Normal | No warning injected |
|
||||
| ≤ 35% | WARNING | "Avoid starting new complex work" |
|
||||
| ≤ 25% | CRITICAL | "Context nearly exhausted, inform user" |
|
||||
|
||||
| Remaining Context | Level | Agent Behavior |
|
||||
| ----------------- | -------- | --------------------------------------- |
|
||||
| > 35% | Normal | No warning injected |
|
||||
| ≤ 35% | WARNING | "Avoid starting new complex work" |
|
||||
| ≤ 25% | CRITICAL | "Context nearly exhausted, inform user" |
|
||||
|
||||
|
||||
Debounce: 5 tool uses between repeated warnings. Severity escalation (WARNING→CRITICAL) bypasses debounce.
|
||||
|
||||
@@ -564,12 +579,14 @@ Debounce: 5 tool uses between repeated warnings. Severity escalation (WARNING→
|
||||
### Security Hooks (v1.27)
|
||||
|
||||
**Prompt Guard** (`gsd-prompt-guard.js`):
|
||||
|
||||
- Triggers on Write/Edit to `.planning/` files
|
||||
- Scans content for prompt injection patterns (role override, instruction bypass, system tag injection)
|
||||
- Advisory-only — logs detection, does not block
|
||||
- Patterns are inlined (subset of `security.cjs`) for hook independence
|
||||
|
||||
**Workflow Guard** (`gsd-workflow-guard.js`):
|
||||
|
||||
- Triggers on Write/Edit to non-`.planning/` files
|
||||
- Detects edits outside GSD workflow context (no active `/gsd-` command or Task subagent)
|
||||
- Advises using `/gsd-quick` or `/gsd-fast` for state-tracked changes
|
||||
@@ -581,18 +598,20 @@ Debounce: 5 tool uses between repeated warnings. Severity escalation (WARNING→
|
||||
|
||||
GSD supports multiple AI coding runtimes through a unified command/workflow architecture:
|
||||
|
||||
| Runtime | Command Format | Agent System | Config Location |
|
||||
|---------|---------------|--------------|-----------------|
|
||||
| Claude Code | `/gsd-command` | Task spawning | `~/.claude/` |
|
||||
| OpenCode | `/gsd-command` | Subagent mode | `~/.config/opencode/` |
|
||||
| Kilo | `/gsd-command` | Subagent mode | `~/.config/kilo/` |
|
||||
| Gemini CLI | `/gsd-command` | Task spawning | `~/.gemini/` |
|
||||
| Codex | `$gsd-command` | Skills | `~/.codex/` |
|
||||
| Copilot | `/gsd-command` | Agent delegation | `~/.github/` |
|
||||
| Antigravity | Skills | Skills | `~/.gemini/antigravity/` |
|
||||
| Trae | Skills | Skills | `~/.trae/` |
|
||||
| Cline | Rules | Rules | `.clinerules` |
|
||||
| Augment Code | Skills | Skills | Augment config |
|
||||
|
||||
| Runtime | Command Format | Agent System | Config Location |
|
||||
| ------------ | -------------- | ---------------- | ------------------------ |
|
||||
| Claude Code | `/gsd-command` | Task spawning | `~/.claude/` |
|
||||
| OpenCode | `/gsd-command` | Subagent mode | `~/.config/opencode/` |
|
||||
| Kilo | `/gsd-command` | Subagent mode | `~/.config/kilo/` |
|
||||
| Gemini CLI | `/gsd-command` | Task spawning | `~/.gemini/` |
|
||||
| Codex | `$gsd-command` | Skills | `~/.codex/` |
|
||||
| Copilot | `/gsd-command` | Agent delegation | `~/.github/` |
|
||||
| Antigravity | Skills | Skills | `~/.gemini/antigravity/` |
|
||||
| Trae | Skills | Skills | `~/.trae/` |
|
||||
| Cline | Rules | Rules | `.clinerules` |
|
||||
| Augment Code | Skills | Skills | Augment config |
|
||||
|
||||
|
||||
### Abstraction Points
|
||||
|
||||
@@ -602,4 +621,4 @@ GSD supports multiple AI coding runtimes through a unified command/workflow arch
|
||||
4. **Path conventions** — Each runtime stores config in different directories
|
||||
5. **Model references** — `inherit` profile lets GSD defer to runtime's model selection
|
||||
|
||||
The installer handles all translation at install time. Workflows and agents are written in Claude Code's native format and transformed during deployment.
|
||||
The installer handles all translation at install time. Workflows and agents are written in Claude Code's native format and transformed during deployment.
|
||||
@@ -1,29 +1,71 @@
|
||||
# GSD CLI Tools Reference
|
||||
|
||||
> Programmatic API reference for `gsd-tools.cjs`. Used by workflows and agents internally. For user-facing commands, see [Command Reference](COMMANDS.md).
|
||||
> Surface-area reference for `get-shit-done/bin/gsd-tools.cjs` (legacy Node CLI). Workflows and agents should prefer `gsd-sdk query` or `@gsd-build/sdk` where a handler exists — see [SDK and programmatic access](#sdk-and-programmatic-access). For slash commands and user flows, see [Command Reference](COMMANDS.md).
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
`gsd-tools.cjs` is a Node.js CLI utility that replaces repetitive inline bash patterns across GSD's ~50 command, workflow, and agent files. It centralizes: config parsing, model resolution, phase lookup, git commits, summary verification, state management, and template operations.
|
||||
`gsd-tools.cjs` centralizes config parsing, model resolution, phase lookup, git commits, summary verification, state management, and template operations across GSD commands, workflows, and agents.
|
||||
|
||||
**Preferred for new orchestration:** Many of the same operations are available as `gsd-sdk query <command>` (see `sdk/src/query/index.ts` and `docs/QUERY-HANDLERS.md`). Use that in workflows and examples where the handler exists; keep `node … gsd-tools.cjs` for commands not yet in the registry (for example graphify) or when you need CJS-only flags.
|
||||
|
||||
**Location:** `get-shit-done/bin/gsd-tools.cjs`
|
||||
**Modules:** see the [Module Architecture](#module-architecture) table; the `get-shit-done/bin/lib/` directory is authoritative.
|
||||
| | |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **Shipped path** | `get-shit-done/bin/gsd-tools.cjs` |
|
||||
| **Implementation** | 20 domain modules under `get-shit-done/bin/lib/` (the directory is authoritative) |
|
||||
| **Status** | Maintained for parity tests and CJS-only entrypoints; `gsd-sdk query` / SDK registry are the supported path for new orchestration (see [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md)). |
|
||||
|
||||
|
||||
**Usage (CJS):**
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
node gsd-tools.cjs <command> [args] [--raw] [--cwd <path>]
|
||||
```
|
||||
|
||||
**Global Flags:**
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--raw` | Machine-readable output (JSON or plain text, no formatting) |
|
||||
| `--cwd <path>` | Override working directory (for sandboxed subagents) |
|
||||
| `--ws <name>` | Target a specific workstream context (SDK only) |
|
||||
**Global flags (CJS):**
|
||||
|
||||
|
||||
| Flag | Description |
|
||||
| -------------- | ---------------------------------------------------------------------------- |
|
||||
| `--raw` | Machine-readable output (JSON or plain text, no formatting) |
|
||||
| `--cwd <path>` | Override working directory (for sandboxed subagents) |
|
||||
| `--ws <name>` | Workstream context (also honored when the SDK spawns this binary; see below) |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## SDK and programmatic access
|
||||
|
||||
Use this when authoring workflows, not when you only need the command list below.
|
||||
|
||||
**1. CLI — `gsd-sdk query <argv…>`**
|
||||
|
||||
- Resolves argv with the same **longest-prefix** rules as the typed registry (`resolveQueryArgv` in `sdk/src/query/registry.ts`). Unregistered commands **fail fast** — use `node …/gsd-tools.cjs` only for handlers not in the registry.
|
||||
- Full matrix (CJS command → registry key, CLI-only tools, aliases, golden tiers): [sdk/src/query/QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md).
|
||||
|
||||
**2. TypeScript — `@gsd-build/sdk` (`GSDTools`, `createRegistry`)**
|
||||
|
||||
- `GSDTools` (used by `PhaseRunner`, `InitRunner`, and `GSD.createTools()`) always shells out to `gsd-tools.cjs` via `execFile` — there is no in-process registry path on this class. For typed, in-process dispatch use `createRegistry()` from `sdk/src/query/index.ts`, or invoke `gsd-sdk query` (see [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md)).
|
||||
- Conventions: mutation event wiring, `GSDError` vs `{ data: { error } }`, locks, and stubs — [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md).
|
||||
|
||||
**CJS → SDK examples (same project directory):**
|
||||
|
||||
|
||||
| Legacy CJS | Preferred `gsd-sdk query` (examples) |
|
||||
| ---------------------------------------- | ------------------------------------ |
|
||||
| `node gsd-tools.cjs init phase-op 12` | `gsd-sdk query init phase-op 12` |
|
||||
| `node gsd-tools.cjs phase-plan-index 12` | `gsd-sdk query phase-plan-index 12` |
|
||||
| `node gsd-tools.cjs state json` | `gsd-sdk query state json` |
|
||||
| `node gsd-tools.cjs roadmap analyze` | `gsd-sdk query roadmap analyze` |
|
||||
|
||||
|
||||
**SDK state reads:** `gsd-sdk query state json` / `state.json` and `gsd-sdk query state load` / `state.load` currently share one native handler (rebuilt STATE.md frontmatter — CJS `cmdStateJson`). The legacy CJS `state load` payload (`config`, `state_raw`, existence flags) is still **CLI-only** via `node …/gsd-tools.cjs state load` until a separate registry handler exists. Full routing and golden rules: [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md).
|
||||
|
||||
**CLI-only (not in registry):** e.g. **graphify**, **from-gsd2** / **gsd2-import** — call `gsd-tools.cjs` until registered.
|
||||
|
||||
**Mutation events (SDK):** `QUERY_MUTATION_COMMANDS` in `sdk/src/query/index.ts` lists commands that may emit structured events after a successful dispatch. Exceptions called out in QUERY-HANDLERS: `state validate` (read-only), `skill-manifest` (writes only with `--write`), `intel update` (stub).
|
||||
|
||||
**Golden parity:** Policy and CJS↔SDK test categories are documented under **Golden parity** in [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -373,7 +415,7 @@ node gsd-tools.cjs from-gsd2 [--path <dir>] [--force] [--dry-run]
|
||||
node gsd-tools.cjs commit <message> [--files f1 f2] [--amend] [--no-verify]
|
||||
```
|
||||
|
||||
> **`--no-verify`**: Skips pre-commit hooks. Used by parallel executor agents during wave-based execution to avoid build lock contention (e.g., cargo lock fights in Rust projects). The orchestrator runs hooks once after each wave completes. Do not use `--no-verify` during sequential execution — let hooks run normally.
|
||||
> `--no-verify`: Skips pre-commit hooks. Used by parallel executor agents during wave-based execution to avoid build lock contention (e.g., cargo lock fights in Rust projects). The orchestrator runs hooks once after each wave completes. Do not use `--no-verify` during sequential execution — let hooks run normally.
|
||||
|
||||
# Web search (requires Brave API key)
|
||||
node gsd-tools.cjs websearch <query> [--limit N] [--freshness day|week|month]
|
||||
@@ -430,3 +472,11 @@ User-facing entry point: `/gsd-graphify` (see [Command Reference](COMMANDS.md#gs
|
||||
| Audit | `lib/audit.cjs` | Phase/milestone audit queue handlers; `audit-open` helper |
|
||||
| GSD2 Import | `lib/gsd2-import.cjs` | Reverse-migration importer from GSD-2 projects (backs `/gsd-from-gsd2`) |
|
||||
| Intel | `lib/intel.cjs` | Queryable codebase intelligence index (backs `/gsd-intel`) |
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
- [sdk/src/query/QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md) — registry matrix, routing, golden parity, intentional CJS differences
|
||||
- [Architecture](ARCHITECTURE.md) — where `gsd-sdk query` fits in orchestration
|
||||
- [Command Reference](COMMANDS.md) — user-facing `/gsd:` commands
|
||||
|
||||
@@ -21,7 +21,7 @@ GSD stores project settings in `.planning/config.json`. Created during `/gsd-new
|
||||
"search_gitignored": false,
|
||||
"sub_repos": []
|
||||
},
|
||||
"context_profile": null,
|
||||
"context": null,
|
||||
"workflow": {
|
||||
"research": true,
|
||||
"plan_check": true,
|
||||
@@ -43,6 +43,7 @@ GSD stores project settings in `.planning/config.json`. Created during `/gsd-new
|
||||
"plan_bounce": false,
|
||||
"plan_bounce_script": null,
|
||||
"plan_bounce_passes": 2,
|
||||
"plan_chunked": false,
|
||||
"code_review_command": null,
|
||||
"cross_ai_execution": false,
|
||||
"cross_ai_command": null,
|
||||
@@ -113,6 +114,7 @@ GSD stores project settings in `.planning/config.json`. Created during `/gsd-new
|
||||
| `response_language` | string | language code | (none) | Language for agent responses (e.g., `"pt"`, `"ko"`, `"ja"`). Propagates to all spawned agents for cross-phase language consistency. Added in v1.32 |
|
||||
| `context_profile` | string | `dev`, `research`, `review` | (none) | Execution context preset that applies a pre-configured bundle of mode, model, and workflow settings for the current type of work. Added in v1.34 |
|
||||
| `claude_md_path` | string | any file path | `./CLAUDE.md` | Custom output path for the generated CLAUDE.md file. Useful for monorepos or projects that need CLAUDE.md in a non-root location. Defaults to `./CLAUDE.md` at the project root. Added in v1.36 |
|
||||
| `claude_md_assembly.mode` | enum | `embed`, `link` | `embed` | Controls how managed sections are written into CLAUDE.md. `embed` (default) inlines content between GSD markers. `link` writes `@.planning/<source-path>` instead — Claude Code expands the reference at runtime, reducing CLAUDE.md size by ~65% on typical projects. `link` only applies to sections that have a real source file; `workflow` and fallback sections always embed. Per-block overrides: `claude_md_assembly.blocks.<section>` (e.g. `claude_md_assembly.blocks.architecture: link`). Added in v1.38 |
|
||||
| `context` | string | any text | (none) | Custom context string injected into every agent prompt for the project. Use to provide persistent project-specific guidance (e.g., coding conventions, team practices) that every agent should be aware of |
|
||||
| `phase_naming` | string | any string | (none) | Custom prefix for phase directory names. When set, overrides the auto-generated phase slug (e.g., `"feature"` produces `feature-01-setup/` instead of the roadmap-derived slug) |
|
||||
| `brave_search` | boolean | `true`/`false` | auto-detected | Override auto-detection of Brave Search API availability. When unset, GSD checks for `BRAVE_API_KEY` env var or `~/.gsd/brave_api_key` file |
|
||||
@@ -149,6 +151,7 @@ All workflow toggles follow the **absent = enabled** pattern. If a key is missin
|
||||
| `workflow.plan_bounce` | boolean | `false` | Run external validation script against generated plans. When enabled, the plan-phase orchestrator pipes each PLAN.md through the script specified by `plan_bounce_script` and blocks on non-zero exit. Added in v1.36 |
|
||||
| `workflow.plan_bounce_script` | string | (none) | Path to the external script invoked for plan bounce validation. Receives the PLAN.md path as its first argument. Required when `plan_bounce` is `true`. Added in v1.36 |
|
||||
| `workflow.plan_bounce_passes` | number | `2` | Number of sequential bounce passes to run. Each pass feeds the previous pass's output back into the validator. Higher values increase rigor at the cost of latency. Added in v1.36 |
|
||||
| `workflow.plan_chunked` | boolean | `false` | Enable chunked planning mode. When `true` (or when `--chunked` flag is passed to `/gsd-plan-phase`), the orchestrator splits the single long-lived planner Task into a short outline Task followed by N short per-plan Tasks (~3-5 min each). Each plan is committed individually for crash resilience. If a Task hangs and the terminal is force-killed, rerunning with `--chunked` resumes from the last completed plan. Particularly useful on Windows where long-lived Tasks may hang on stdio. Added in v1.38 |
|
||||
| `workflow.code_review_command` | string | (none) | Shell command for external code review integration in `/gsd-ship`. Receives changed file paths via stdin. Non-zero exit blocks the ship workflow. Added in v1.36 |
|
||||
| `workflow.tdd_mode` | boolean | `false` | Enable TDD pipeline as a first-class execution mode. When `true`, the planner aggressively applies `type: tdd` to eligible tasks (business logic, APIs, validations, algorithms) and the executor enforces RED/GREEN/REFACTOR gate sequence. An end-of-phase collaborative review checkpoint verifies gate compliance. Added in v1.36 |
|
||||
| `workflow.cross_ai_execution` | boolean | `false` | Delegate phase execution to an external AI CLI instead of spawning local executor agents. Useful for leveraging a different model's strengths for specific phases. Added in v1.36 |
|
||||
@@ -247,7 +250,7 @@ Any GSD agent type can receive skills. Common types:
|
||||
|
||||
### How It Works
|
||||
|
||||
At spawn time, workflows call `node gsd-tools.cjs agent-skills <type>` to load configured skills. If skills exist for the agent type, they are injected as an `<agent_skills>` block in the Task() prompt:
|
||||
At spawn time, workflows call `gsd-sdk query agent-skills <type>` (or legacy `node gsd-tools.cjs agent-skills <type>`) to load configured skills. If skills exist for the agent type, they are injected as an `<agent_skills>` block in the Task() prompt:
|
||||
|
||||
```xml
|
||||
<agent_skills>
|
||||
@@ -264,7 +267,7 @@ If no skills are configured, the block is omitted (zero overhead).
|
||||
Set skills via the CLI:
|
||||
|
||||
```bash
|
||||
node gsd-tools.cjs config-set agent_skills.gsd-executor '["skills/my-skill"]'
|
||||
gsd-sdk query config-set agent_skills.gsd-executor '["skills/my-skill"]'
|
||||
```
|
||||
|
||||
---
|
||||
@@ -292,10 +295,10 @@ Toggle optional capabilities via the `features.*` config namespace. Feature flag
|
||||
|
||||
```bash
|
||||
# Enable a feature
|
||||
node gsd-tools.cjs config-set features.global_learnings true
|
||||
gsd-sdk query config-set features.global_learnings true
|
||||
|
||||
# Disable a feature
|
||||
node gsd-tools.cjs config-set features.thinking_partner false
|
||||
gsd-sdk query config-set features.thinking_partner false
|
||||
```
|
||||
|
||||
The `features.*` namespace is a dynamic key pattern — new feature flags can be added without modifying `VALID_CONFIG_KEYS`. Any key matching `features.<name>` is accepted by the config system.
|
||||
@@ -394,6 +397,8 @@ Control confirmation prompts during workflows.
|
||||
|
||||
Settings for the security enforcement feature (v1.31). All follow the **absent = enabled** pattern. These keys live under `workflow.*` in `.planning/config.json` — matching the shipped template and the runtime reads in `workflows/plan-phase.md`, `workflows/execute-phase.md`, `workflows/secure-phase.md`, and `workflows/verify-work.md`.
|
||||
|
||||
These keys live under `workflow.*` — that is where the workflows and installer write and read them. Setting them at the top level of `config.json` is silently ignored.
|
||||
|
||||
| Setting | Type | Default | Description |
|
||||
|---------|------|---------|-------------|
|
||||
| `workflow.security_enforcement` | boolean | `true` | Enable threat-model-anchored security verification via `/gsd-secure-phase`. When `false`, security checks are skipped entirely |
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
"/gsd-ui-phase",
|
||||
"/gsd-ui-review",
|
||||
"/gsd-ultraplan-phase",
|
||||
"/gsd-sync-skills",
|
||||
"/gsd-undo",
|
||||
"/gsd-update",
|
||||
"/gsd-validate-phase",
|
||||
@@ -149,6 +150,7 @@
|
||||
"extract_learnings.md",
|
||||
"fast.md",
|
||||
"forensics.md",
|
||||
"graduation.md",
|
||||
"health.md",
|
||||
"help.md",
|
||||
"import.md",
|
||||
@@ -191,6 +193,7 @@
|
||||
"spike-wrap-up.md",
|
||||
"spike.md",
|
||||
"stats.md",
|
||||
"sync-skills.md",
|
||||
"transition.md",
|
||||
"ui-phase.md",
|
||||
"ui-review.md",
|
||||
@@ -226,6 +229,7 @@
|
||||
"model-profiles.md",
|
||||
"phase-argument-parsing.md",
|
||||
"planner-antipatterns.md",
|
||||
"planner-chunked.md",
|
||||
"planner-gap-closure.md",
|
||||
"planner-reviews.md",
|
||||
"planner-revision.md",
|
||||
@@ -253,6 +257,7 @@
|
||||
"workstream-flag.md"
|
||||
],
|
||||
"cli_modules": [
|
||||
"artifacts.cjs",
|
||||
"audit.cjs",
|
||||
"commands.cjs",
|
||||
"config-schema.cjs",
|
||||
|
||||
@@ -54,7 +54,7 @@ Full roster at `agents/gsd-*.md`. The "Primary doc" column flags whether [`docs/
|
||||
|
||||
---
|
||||
|
||||
## Commands (82 shipped)
|
||||
## Commands (83 shipped)
|
||||
|
||||
Full roster at `commands/gsd/*.md`. The groupings below mirror `docs/COMMANDS.md` section order; each row carries the command name, a one-line role derived from the command's frontmatter `description:`, and a link to the source file. `tests/command-count-sync.test.cjs` locks the count against the filesystem.
|
||||
|
||||
@@ -165,6 +165,7 @@ Full roster at `commands/gsd/*.md`. The groupings below mirror `docs/COMMANDS.md
|
||||
| `/gsd-settings` | Configure GSD workflow toggles and model profile. | [commands/gsd/settings.md](../commands/gsd/settings.md) |
|
||||
| `/gsd-set-profile` | Switch model profile for GSD agents (quality/balanced/budget/inherit). | [commands/gsd/set-profile.md](../commands/gsd/set-profile.md) |
|
||||
| `/gsd-pr-branch` | Create a clean PR branch by filtering out `.planning/` commits. | [commands/gsd/pr-branch.md](../commands/gsd/pr-branch.md) |
|
||||
| `/gsd-sync-skills` | Sync managed GSD skill directories across runtime roots for multi-runtime users. | [commands/gsd/sync-skills.md](../commands/gsd/sync-skills.md) |
|
||||
| `/gsd-update` | Update GSD to latest version with changelog display. | [commands/gsd/update.md](../commands/gsd/update.md) |
|
||||
| `/gsd-reapply-patches` | Reapply local modifications after a GSD update. | [commands/gsd/reapply-patches.md](../commands/gsd/reapply-patches.md) |
|
||||
| `/gsd-help` | Show available GSD commands and usage guide. | [commands/gsd/help.md](../commands/gsd/help.md) |
|
||||
@@ -172,7 +173,7 @@ Full roster at `commands/gsd/*.md`. The groupings below mirror `docs/COMMANDS.md
|
||||
|
||||
---
|
||||
|
||||
## Workflows (79 shipped)
|
||||
## Workflows (81 shipped)
|
||||
|
||||
Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators that commands reference internally; most are not read directly by end users. Rows below map each workflow file to its role (derived from the `<purpose>` block) and, where applicable, to the command that invokes it.
|
||||
|
||||
@@ -206,6 +207,7 @@ Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators
|
||||
| `extract_learnings.md` | Extract decisions, lessons, patterns, and surprises from completed phase artifacts. | `/gsd-extract-learnings` |
|
||||
| `fast.md` | Execute a trivial task inline without subagent overhead. | `/gsd-fast` |
|
||||
| `forensics.md` | Forensics investigation of failed workflows — git, artifacts, and state analysis. | `/gsd-forensics` |
|
||||
| `graduation.md` | Cluster recurring LEARNINGS.md items across phases and surface HITL promotion candidates. | `transition.md` (graduation_scan step) |
|
||||
| `health.md` | Validate `.planning/` directory integrity and report actionable issues. | `/gsd-health` |
|
||||
| `help.md` | Display the complete GSD command reference. | `/gsd-help` |
|
||||
| `import.md` | Ingest external plans with conflict detection against existing project decisions. | `/gsd-import` |
|
||||
@@ -248,6 +250,7 @@ Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators
|
||||
| `spike.md` | Rapid feasibility validation through focused, throwaway experiments. | `/gsd-spike` |
|
||||
| `spike-wrap-up.md` | Curate spike findings and package them as a persistent `spike-findings-[project]` skill. | `/gsd-spike-wrap-up` |
|
||||
| `stats.md` | Project statistics rendering — phases, plans, requirements, git metrics. | `/gsd-stats` |
|
||||
| `sync-skills.md` | Cross-runtime GSD skill sync — diff and apply `gsd-*` skill directories across runtime roots. | `/gsd-sync-skills` |
|
||||
| `transition.md` | Phase-boundary transition workflow — workstream checks, state advancement. | `execute-phase.md`, `/gsd-next` |
|
||||
| `ui-phase.md` | Generate UI-SPEC.md design contract via gsd-ui-researcher. | `/gsd-ui-phase` |
|
||||
| `ui-review.md` | Retroactive 6-pillar visual audit via gsd-ui-auditor. | `/gsd-ui-review` |
|
||||
@@ -262,7 +265,7 @@ Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators
|
||||
|
||||
---
|
||||
|
||||
## References (49 shipped)
|
||||
## References (50 shipped)
|
||||
|
||||
Full roster at `get-shit-done/references/*.md`. References are shared knowledge documents that workflows and agents `@-reference`. The groupings below match [`docs/ARCHITECTURE.md`](ARCHITECTURE.md#references-get-shit-donereferencesmd) — core, workflow, thinking-model clusters, and the modular planner decomposition.
|
||||
|
||||
@@ -341,21 +344,23 @@ The `gsd-planner` agent is decomposed into a core agent plus reference modules t
|
||||
| Reference | Role |
|
||||
|-----------|------|
|
||||
| `planner-antipatterns.md` | Planner anti-patterns and specificity examples. |
|
||||
| `planner-chunked.md` | Chunked mode return formats (`## OUTLINE COMPLETE`, `## PLAN COMPLETE`) for Windows stdio hang mitigation. |
|
||||
| `planner-gap-closure.md` | Gap-closure mode behavior (reads VERIFICATION.md, targeted replanning). |
|
||||
| `planner-reviews.md` | Cross-AI review integration (reads REVIEWS.md from `/gsd-review`). |
|
||||
| `planner-revision.md` | Plan revision patterns for iterative refinement. |
|
||||
| `planner-source-audit.md` | Planner source-audit and authority-limit rules. |
|
||||
|
||||
> **Subdirectory:** `get-shit-done/references/few-shot-examples/` contains additional few-shot examples (`plan-checker.md`, `verifier.md`) that are referenced from specific agents. These are not counted in the 49 top-level references.
|
||||
> **Subdirectory:** `get-shit-done/references/few-shot-examples/` contains additional few-shot examples (`plan-checker.md`, `verifier.md`) that are referenced from specific agents. These are not counted in the 50 top-level references.
|
||||
|
||||
---
|
||||
|
||||
## CLI Modules (25 shipped)
|
||||
## CLI Modules (26 shipped)
|
||||
|
||||
Full listing: `get-shit-done/bin/lib/*.cjs`.
|
||||
|
||||
| Module | Responsibility |
|
||||
|--------|----------------|
|
||||
| `artifacts.cjs` | Canonical artifact registry — known `.planning/` root file names; used by `gsd-health` W019 lint |
|
||||
| `audit.cjs` | Audit dispatch, audit open sessions, audit storage helpers |
|
||||
| `commands.cjs` | Misc CLI commands (slug, timestamp, todos, scaffolding, stats) |
|
||||
| `config-schema.cjs` | Single source of truth for `VALID_CONFIG_KEYS` and dynamic key patterns; imported by both the validator and the config-schema-docs parity test |
|
||||
|
||||
@@ -26,4 +26,4 @@ Language versions: [English](README.md) · [Português (pt-BR)](pt-BR/README.md)
|
||||
- **All commands at a glance:** [Command Reference](COMMANDS.md)
|
||||
- **Configuring GSD:** [Configuration Reference](CONFIGURATION.md)
|
||||
- **How the system works internally:** [Architecture](ARCHITECTURE.md)
|
||||
- **Contributing or extending:** [CLI Tools Reference](CLI-TOOLS.md) + [Agent Reference](AGENTS.md)
|
||||
- **Contributing or extending:** [CLI Tools Reference](CLI-TOOLS.md) + [Agent Reference](AGENTS.md)
|
||||
@@ -165,12 +165,14 @@ By default, `/gsd-discuss-phase` asks open-ended questions about your implementa
|
||||
**Enable:** Set `workflow.discuss_mode` to `'assumptions'` via `/gsd-settings`.
|
||||
|
||||
**How it works:**
|
||||
|
||||
1. Reads PROJECT.md, codebase mapping, and existing conventions
|
||||
2. Generates a structured list of assumptions (tech choices, patterns, file locations)
|
||||
3. Presents assumptions for you to confirm, correct, or expand
|
||||
4. Writes CONTEXT.md from confirmed assumptions
|
||||
|
||||
**When to use:**
|
||||
|
||||
- Experienced developers who already know their codebase well
|
||||
- Rapid iteration where open-ended questions slow you down
|
||||
- Projects where patterns are well-established and predictable
|
||||
@@ -189,16 +191,19 @@ AI-generated frontends are visually inconsistent not because Claude Code is bad
|
||||
|
||||
### Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/gsd-ui-phase [N]` | Generate UI-SPEC.md design contract for a frontend phase |
|
||||
| `/gsd-ui-review [N]` | Retroactive 6-pillar visual audit of implemented UI |
|
||||
|
||||
| Command | Description |
|
||||
| -------------------- | -------------------------------------------------------- |
|
||||
| `/gsd-ui-phase [N]` | Generate UI-SPEC.md design contract for a frontend phase |
|
||||
| `/gsd-ui-review [N]` | Retroactive 6-pillar visual audit of implemented UI |
|
||||
|
||||
|
||||
### Workflow: `/gsd-ui-phase`
|
||||
|
||||
**When to run:** After `/gsd-discuss-phase`, before `/gsd-plan-phase` — for phases with frontend/UI work.
|
||||
|
||||
**Flow:**
|
||||
|
||||
1. Reads CONTEXT.md, RESEARCH.md, REQUIREMENTS.md for existing decisions
|
||||
2. Detects design system state (shadcn components.json, Tailwind config, existing tokens)
|
||||
3. shadcn initialization gate — offers to initialize if React/Next.js/Vite project has none
|
||||
@@ -216,6 +221,7 @@ AI-generated frontends are visually inconsistent not because Claude Code is bad
|
||||
**Standalone:** Works on any project, not just GSD-managed ones. If no UI-SPEC.md exists, audits against abstract 6-pillar standards.
|
||||
|
||||
**6 Pillars (scored 1-4 each):**
|
||||
|
||||
1. Copywriting — CTA labels, empty states, error states
|
||||
2. Visuals — focal points, visual hierarchy, icon accessibility
|
||||
3. Color — accent usage discipline, 60/30/10 compliance
|
||||
@@ -227,10 +233,12 @@ AI-generated frontends are visually inconsistent not because Claude Code is bad
|
||||
|
||||
### Configuration
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `workflow.ui_phase` | `true` | Generate UI design contracts for frontend phases |
|
||||
| `workflow.ui_safety_gate` | `true` | plan-phase prompts to run /gsd-ui-phase for frontend phases |
|
||||
|
||||
| Setting | Default | Description |
|
||||
| ------------------------- | ------- | ----------------------------------------------------------- |
|
||||
| `workflow.ui_phase` | `true` | Generate UI design contracts for frontend phases |
|
||||
| `workflow.ui_safety_gate` | `true` | plan-phase prompts to run /gsd-ui-phase for frontend phases |
|
||||
|
||||
|
||||
Both follow the absent=enabled pattern. Disable via `/gsd-settings`.
|
||||
|
||||
@@ -248,6 +256,7 @@ The preset string becomes a first-class GSD planning artifact, reproducible acro
|
||||
### Registry Safety Gate
|
||||
|
||||
Third-party shadcn registries can inject arbitrary code. The safety gate requires:
|
||||
|
||||
- `npx shadcn view {component}` — inspect before installing
|
||||
- `npx shadcn diff {component}` — compare against official
|
||||
|
||||
@@ -365,12 +374,14 @@ Workstreams let you work on multiple milestone areas concurrently without state
|
||||
|
||||
### Commands
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/gsd-workstreams create <name>` | Create a new workstream with isolated planning state |
|
||||
| `/gsd-workstreams switch <name>` | Switch active context to a different workstream |
|
||||
| `/gsd-workstreams list` | Show all workstreams and which is active |
|
||||
| `/gsd-workstreams complete <name>` | Mark a workstream as done and archive its state |
|
||||
|
||||
| Command | Purpose |
|
||||
| ---------------------------------- | ---------------------------------------------------- |
|
||||
| `/gsd-workstreams create <name>` | Create a new workstream with isolated planning state |
|
||||
| `/gsd-workstreams switch <name>` | Switch active context to a different workstream |
|
||||
| `/gsd-workstreams list` | Show all workstreams and which is active |
|
||||
| `/gsd-workstreams complete <name>` | Mark a workstream as done and archive its state |
|
||||
|
||||
|
||||
### How It Works
|
||||
|
||||
@@ -393,6 +404,7 @@ All user-supplied file paths (`--text-file`, `--prd`) are validated to resolve w
|
||||
The `security.cjs` module scans for known injection patterns (role overrides, instruction bypasses, system tag injections) in user-supplied text before it enters planning artifacts.
|
||||
|
||||
**Runtime Hooks:**
|
||||
|
||||
- `gsd-prompt-guard.js` — Scans Write/Edit calls to `.planning/` for injection patterns (always active, advisory-only)
|
||||
- `gsd-workflow-guard.js` — Warns on file edits outside GSD workflow context (opt-in via `hooks.workflow_guard`)
|
||||
|
||||
@@ -598,11 +610,13 @@ claude --dangerously-skip-permissions
|
||||
|
||||
### Speed vs Quality Presets
|
||||
|
||||
| Scenario | Mode | Granularity | Profile | Research | Plan Check | Verifier |
|
||||
|----------|------|-------|---------|----------|------------|----------|
|
||||
| Prototyping | `yolo` | `coarse` | `budget` | off | off | off |
|
||||
| Normal dev | `interactive` | `standard` | `balanced` | on | on | on |
|
||||
| Production | `interactive` | `fine` | `quality` | on | on | on |
|
||||
|
||||
| Scenario | Mode | Granularity | Profile | Research | Plan Check | Verifier |
|
||||
| ----------- | ------------- | ----------- | ---------- | -------- | ---------- | -------- |
|
||||
| Prototyping | `yolo` | `coarse` | `budget` | off | off | off |
|
||||
| Normal dev | `interactive` | `standard` | `balanced` | on | on | on |
|
||||
| Production | `interactive` | `fine` | `quality` | on | on | on |
|
||||
|
||||
|
||||
**Skipping discuss-phase in autonomous mode:** When running in `yolo` mode with well-established preferences already captured in PROJECT.md, set `workflow.skip_discuss: true` via `/gsd-settings`. This bypasses the discuss-phase entirely and writes a minimal CONTEXT.md derived from the ROADMAP phase goal. Useful when your PROJECT.md and conventions are comprehensive enough that discussion adds no new information.
|
||||
|
||||
@@ -637,6 +651,7 @@ cd ~/gsd-workspaces/feature-b
|
||||
```
|
||||
|
||||
Each workspace gets:
|
||||
|
||||
- Its own `.planning/` directory (fully independent from source repos)
|
||||
- Git worktrees (default) or clones of specified repos
|
||||
- A `WORKSPACE.md` manifest tracking member repos
|
||||
@@ -647,9 +662,9 @@ Each workspace gets:
|
||||
|
||||
### Programmatic CLI (`gsd-sdk query` vs `gsd-tools.cjs`)
|
||||
|
||||
For automation and copy-paste from docs, prefer **`gsd-sdk query`** with a registered subcommand (see [CLI-TOOLS.md](CLI-TOOLS.md) and [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md)). The legacy **`node $HOME/.claude/get-shit-done/bin/gsd-tools.cjs`** CLI remains supported for dual-mode operation.
|
||||
For automation and copy-paste from docs, prefer **`gsd-sdk query`** with a registered subcommand (see [CLI-TOOLS.md — SDK and programmatic access](CLI-TOOLS.md#sdk-and-programmatic-access) and [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md)). The legacy `node $HOME/.claude/get-shit-done/bin/gsd-tools.cjs` CLI remains supported for dual-mode operation.
|
||||
|
||||
**Not yet on `gsd-sdk query` (use CJS):** `state validate`, `state sync`, `audit-open`, `graphify`, `from-gsd2`, and any subcommand not listed in the registry.
|
||||
**CLI-only (not in the query registry):** **graphify**, **from-gsd2** / **gsd2-import** — call `gsd-tools.cjs` (see [QUERY-HANDLERS.md](../sdk/src/query/QUERY-HANDLERS.md)). **Two different `state` JSON shapes in the legacy CLI:** `state json` (frontmatter rebuild) vs `state load` (`config` + `state_raw` + flags). **`gsd-sdk query` today:** both `state.json` and `state.load` resolve to the frontmatter-rebuild handler — use `node …/gsd-tools.cjs state load` when you need the CJS `state load` shape. See [CLI-TOOLS.md](CLI-TOOLS.md#sdk-and-programmatic-access) and QUERY-HANDLERS.
|
||||
|
||||
### STATE.md Out of Sync
|
||||
|
||||
@@ -782,6 +797,7 @@ If `npx get-shit-done-cc` fails due to npm outages or network restrictions, see
|
||||
When a workflow fails in a way that isn't obvious -- plans reference nonexistent files, execution produces unexpected results, or state seems corrupted -- run `/gsd-forensics` to generate a diagnostic report.
|
||||
|
||||
**What it checks:**
|
||||
|
||||
- Git history anomalies (orphaned commits, unexpected branch state, rebase artifacts)
|
||||
- Artifact integrity (missing or malformed planning files, broken cross-references)
|
||||
- State inconsistencies (ROADMAP status vs. actual file presence, config drift)
|
||||
@@ -916,22 +932,24 @@ If the installer crashes with `EPERM: operation not permitted, scandir` on Windo
|
||||
|
||||
## Recovery Quick Reference
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| Lost context / new session | `/gsd-resume-work` or `/gsd-progress` |
|
||||
| Phase went wrong | `git revert` the phase commits, then re-plan |
|
||||
| Need to change scope | `/gsd-add-phase`, `/gsd-insert-phase`, or `/gsd-remove-phase` |
|
||||
| Milestone audit found gaps | `/gsd-plan-milestone-gaps` |
|
||||
| Something broke | `/gsd-debug "description"` (add `--diagnose` for analysis without fixes) |
|
||||
| STATE.md out of sync | `state validate` then `state sync` |
|
||||
| Workflow state seems corrupted | `/gsd-forensics` |
|
||||
| Quick targeted fix | `/gsd-quick` |
|
||||
| Plan doesn't match your vision | `/gsd-discuss-phase [N]` then re-plan |
|
||||
| Costs running high | `/gsd-set-profile budget` and `/gsd-settings` to toggle agents off |
|
||||
| Update broke local changes | `/gsd-reapply-patches` |
|
||||
| Want session summary for stakeholder | `/gsd-session-report` |
|
||||
| Don't know what step is next | `/gsd-next` |
|
||||
| Parallel execution build errors | Update GSD or set `parallelization.enabled: false` |
|
||||
|
||||
| Problem | Solution |
|
||||
| ------------------------------------ | ------------------------------------------------------------------------ |
|
||||
| Lost context / new session | `/gsd-resume-work` or `/gsd-progress` |
|
||||
| Phase went wrong | `git revert` the phase commits, then re-plan |
|
||||
| Need to change scope | `/gsd-add-phase`, `/gsd-insert-phase`, or `/gsd-remove-phase` |
|
||||
| Milestone audit found gaps | `/gsd-plan-milestone-gaps` |
|
||||
| Something broke | `/gsd-debug "description"` (add `--diagnose` for analysis without fixes) |
|
||||
| STATE.md out of sync | `state validate` then `state sync` |
|
||||
| Workflow state seems corrupted | `/gsd-forensics` |
|
||||
| Quick targeted fix | `/gsd-quick` |
|
||||
| Plan doesn't match your vision | `/gsd-discuss-phase [N]` then re-plan |
|
||||
| Costs running high | `/gsd-set-profile budget` and `/gsd-settings` to toggle agents off |
|
||||
| Update broke local changes | `/gsd-reapply-patches` |
|
||||
| Want session summary for stakeholder | `/gsd-session-report` |
|
||||
| Don't know what step is next | `/gsd-next` |
|
||||
| Parallel execution build errors | Update GSD or set `parallelization.enabled: false` |
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -975,3 +993,4 @@ For reference, here is what GSD creates in your project:
|
||||
XX-UI-REVIEW.md # Visual audit scores (from /gsd-ui-review)
|
||||
ui-reviews/ # Screenshots from /gsd-ui-review (gitignored)
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Copy-paste friendly for Discord and GitHub comments.
|
||||
|
||||
---
|
||||
|
||||
**@gsd-build/sdk** replaces the untyped, monolithic `gsd-tools.cjs` subprocess with a typed, tested, registry-based query system and **`gsd-sdk query`**, giving GSD structured results, classified errors (`GSDQueryError`), and golden-verified parity with the old CLI. That gives the framework one stable contract instead of a fragile, very large CLI that every workflow had to spawn and parse by hand.
|
||||
**@gsd-build/sdk** replaces the untyped, monolithic `gsd-tools.cjs` subprocess with a typed, tested, registry-based query system and **`gsd-sdk query`**, giving GSD structured results, classified errors (`GSDError` with `ErrorClassification`), and golden-verified parity with the old CLI. That gives the framework one stable contract instead of a fragile, very large CLI that every workflow had to spawn and parse by hand.
|
||||
|
||||
**What users can expect**
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Get Shit Done(GSD)フレームワークの包括的なドキュメントで
|
||||
| [機能リファレンス](FEATURES.md) | 全ユーザー | 全機能の詳細ドキュメントと要件 |
|
||||
| [コマンドリファレンス](COMMANDS.md) | 全ユーザー | 全コマンドの構文、フラグ、オプション、使用例 |
|
||||
| [設定リファレンス](CONFIGURATION.md) | 全ユーザー | 設定スキーマ、ワークフロートグル、モデルプロファイル、Git ブランチ |
|
||||
| [CLI ツールリファレンス](CLI-TOOLS.md) | コントリビューター、エージェント作成者 | `gsd-tools.cjs` のプログラマティック API(ワークフローおよびエージェント向け) |
|
||||
| [CLI ツールリファレンス](CLI-TOOLS.md) | コントリビューター、エージェント作成者 | CJS `gsd-tools.cjs` と **`gsd-sdk query` / SDK** のガイド |
|
||||
| [エージェントリファレンス](AGENTS.md) | コントリビューター、上級ユーザー | 全18種の専門エージェント — 役割、ツール、スポーンパターン |
|
||||
| [ユーザーガイド](USER-GUIDE.md) | 全ユーザー | ワークフローのウォークスルー、トラブルシューティング、リカバリー |
|
||||
| [コンテキストモニター](context-monitor.md) | 全ユーザー | コンテキストウィンドウ監視フックのアーキテクチャ |
|
||||
|
||||
@@ -12,7 +12,7 @@ Get Shit Done (GSD) 프레임워크의 종합 문서입니다. GSD는 AI 코딩
|
||||
| [Feature Reference](FEATURES.md) | 전체 사용자 | 요구사항이 포함된 전체 기능 및 함수 문서 |
|
||||
| [Command Reference](COMMANDS.md) | 전체 사용자 | 모든 명령어의 구문, 플래그, 옵션 및 예제 |
|
||||
| [Configuration Reference](CONFIGURATION.md) | 전체 사용자 | 전체 설정 스키마, 워크플로우 토글, 모델 프로필, git 브랜칭 |
|
||||
| [CLI Tools Reference](CLI-TOOLS.md) | 기여자, 에이전트 작성자 | 워크플로우 및 에이전트를 위한 `gsd-tools.cjs` 프로그래매틱 API |
|
||||
| [CLI Tools Reference](CLI-TOOLS.md) | 기여자, 에이전트 작성자 | CJS `gsd-tools.cjs` + **`gsd-sdk query`/SDK** 안내 |
|
||||
| [Agent Reference](AGENTS.md) | 기여자, 고급 사용자 | 18개 전문 에이전트의 역할, 도구, 스폰 패턴 |
|
||||
| [User Guide](USER-GUIDE.md) | 전체 사용자 | 워크플로우 안내, 문제 해결, 복구 방법 |
|
||||
| [Context Monitor](context-monitor.md) | 전체 사용자 | 컨텍스트 윈도우 모니터링 훅 아키텍처 |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Referência de Ferramentas CLI
|
||||
|
||||
Resumo em Português das ferramentas CLI do GSD.
|
||||
Para API completa (assinaturas, argumentos e comportamento detalhado), consulte [CLI-TOOLS.md em inglês](../CLI-TOOLS.md).
|
||||
Para API completa (assinaturas, argumentos e comportamento detalhado), consulte [CLI-TOOLS.md em inglês](../CLI-TOOLS.md) — inclui a secção **SDK and programmatic access** (`gsd-sdk query`, `@gsd-build/sdk`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Documentação abrangente do framework Get Shit Done (GSD) — um sistema de met
|
||||
| [Referência de configuração](CONFIGURATION.md) | Todos os usuários | Schema completo de configuração, toggles e perfis |
|
||||
| [Referência de recursos](FEATURES.md) | Todos os usuários | Recursos e requisitos detalhados |
|
||||
| [Referência de agentes](AGENTS.md) | Contribuidores, usuários avançados | Agentes especializados, papéis e padrões de orquestração |
|
||||
| [Ferramentas CLI](CLI-TOOLS.md) | Contribuidores, autores de agentes | API programática `gsd-tools.cjs` |
|
||||
| [Ferramentas CLI](CLI-TOOLS.md) | Contribuidores, autores de agentes | Superfície CJS `gsd-tools.cjs` + guia **`gsd-sdk query`/SDK** |
|
||||
| [Monitor de contexto](context-monitor.md) | Todos os usuários | Arquitetura de monitoramento da janela de contexto |
|
||||
| [Discuss Mode](workflow-discuss-mode.md) | Todos os usuários | Modo suposições vs entrevista no `discuss-phase` |
|
||||
| [Referências](references/) | Todos os usuários | Guias complementares de decisão, verificação e padrões |
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
为紧急插入计算下一个小数阶段编号。
|
||||
|
||||
## 使用 gsd-tools
|
||||
## 使用 gsd-sdk query
|
||||
|
||||
```bash
|
||||
# 获取阶段 6 之后的下一个小数阶段
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" phase next-decimal 6
|
||||
gsd-sdk query phase.next-decimal 6
|
||||
```
|
||||
|
||||
输出:
|
||||
@@ -32,14 +32,13 @@ node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" phase next-decimal 6
|
||||
## 提取值
|
||||
|
||||
```bash
|
||||
DECIMAL_INFO=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" phase next-decimal "${AFTER_PHASE}")
|
||||
DECIMAL_PHASE=$(printf '%s\n' "$DECIMAL_INFO" | jq -r '.next')
|
||||
BASE_PHASE=$(printf '%s\n' "$DECIMAL_INFO" | jq -r '.base_phase')
|
||||
DECIMAL_PHASE=$(gsd-sdk query phase.next-decimal "${AFTER_PHASE}" --pick next)
|
||||
BASE_PHASE=$(gsd-sdk query phase.next-decimal "${AFTER_PHASE}" --pick base_phase)
|
||||
```
|
||||
|
||||
或使用 --raw 标志:
|
||||
```bash
|
||||
DECIMAL_PHASE=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" phase next-decimal "${AFTER_PHASE}" --raw)
|
||||
DECIMAL_PHASE=$(gsd-sdk query phase.next-decimal "${AFTER_PHASE}" --raw)
|
||||
# 返回: 06.1
|
||||
```
|
||||
|
||||
@@ -57,9 +56,9 @@ DECIMAL_PHASE=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" phase next-
|
||||
小数阶段目录使用完整的小数编号:
|
||||
|
||||
```bash
|
||||
SLUG=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" generate-slug "$DESCRIPTION" --raw)
|
||||
SLUG=$(gsd-sdk query generate-slug "$DESCRIPTION" --raw)
|
||||
PHASE_DIR=".planning/phases/${DECIMAL_PHASE}-${SLUG}"
|
||||
mkdir -p "$PHASE_DIR"
|
||||
```
|
||||
|
||||
示例:`.planning/phases/06.1-fix-critical-auth-bug/`
|
||||
示例:`.planning/phases/06.1-fix-critical-auth-bug/`
|
||||
|
||||
@@ -51,7 +51,7 @@ Phases:
|
||||
提交内容:
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs: initialize [project-name] ([N] phases)" --files .planning/
|
||||
gsd-sdk query commit "docs: initialize [project-name] ([N] phases)" .planning/
|
||||
```
|
||||
|
||||
</format>
|
||||
@@ -129,7 +129,7 @@ SUMMARY: .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md
|
||||
提交内容:
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs({phase}-{plan}): complete [plan-name] plan" --files .planning/phases/XX-name/{phase}-{plan}-PLAN.md .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md
|
||||
gsd-sdk query commit "docs({phase}-{plan}): complete [plan-name] plan" .planning/phases/XX-name/{phase}-{plan}-PLAN.md .planning/phases/XX-name/{phase}-{plan}-SUMMARY.md .planning/STATE.md .planning/ROADMAP.md
|
||||
```
|
||||
|
||||
**注意:** 代码文件不包含 - 已按任务提交。
|
||||
@@ -149,7 +149,7 @@ Current: [task name]
|
||||
提交内容:
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "wip: [phase-name] paused at task [X]/[Y]" --files .planning/
|
||||
gsd-sdk query commit "wip: [phase-name] paused at task [X]/[Y]" .planning/
|
||||
```
|
||||
|
||||
</format>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# Git 规划提交
|
||||
|
||||
使用 gsd-tools CLI 提交规划工件,它会自动检查 `commit_docs` 配置和 gitignore 状态。
|
||||
通过 `gsd-sdk query commit` 提交规划工件,它会自动检查 `commit_docs` 配置和 gitignore 状态(与旧版 `gsd-tools.cjs commit` 行为相同)。
|
||||
|
||||
## 通过 CLI 提交
|
||||
|
||||
始终使用 `gsd-tools.cjs commit` 处理 `.planning/` 文件 — 它会自动处理 `commit_docs` 和 gitignore 检查:
|
||||
先传提交说明,再传文件路径(位置参数)。`commit` 不要使用 `--files`(该标志仅用于 `commit-to-subrepo`)。
|
||||
|
||||
对 `.planning/` 文件始终使用此方式 —— 它会自动处理 `commit_docs` 与 gitignore 检查:
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs({scope}): {description}" --files .planning/STATE.md .planning/ROADMAP.md
|
||||
gsd-sdk query commit "docs({scope}): {description}" .planning/STATE.md .planning/ROADMAP.md
|
||||
```
|
||||
|
||||
如果 `commit_docs` 为 `false` 或 `.planning/` 被 gitignore,CLI 会返回 `skipped`(带原因)。无需手动条件检查。
|
||||
@@ -17,7 +19,7 @@ node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs({scope}): {des
|
||||
将 `.planning/` 文件变更合并到上次提交:
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "" --files .planning/codebase/*.md --amend
|
||||
gsd-sdk query commit "" .planning/codebase/*.md --amend
|
||||
```
|
||||
|
||||
## 提交消息模式
|
||||
@@ -35,4 +37,4 @@ node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "" --files .planning
|
||||
|
||||
- config 中 `commit_docs: false`
|
||||
- `.planning/` 被 gitignore
|
||||
- 无变更可提交(用 `git status --porcelain .planning/` 检查)
|
||||
- 无变更可提交(用 `git status --porcelain .planning/` 检查)
|
||||
|
||||
@@ -36,19 +36,19 @@
|
||||
- 用户必须将 `.planning/` 添加到 `.gitignore`
|
||||
- 适用于:OSS 贡献、客户项目、保持规划私有
|
||||
|
||||
**使用 gsd-tools.cjs(推荐):**
|
||||
**使用 `gsd-sdk query`(推荐):**
|
||||
|
||||
```bash
|
||||
# 提交时自动检查 commit_docs + gitignore:
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs: update state" --files .planning/STATE.md
|
||||
gsd-sdk query commit "docs: update state" .planning/STATE.md
|
||||
|
||||
# 通过 state load 加载配置(返回 JSON):
|
||||
INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" state load)
|
||||
INIT=$(gsd-sdk query state.load)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# commit_docs 在 JSON 输出中可用
|
||||
|
||||
# 或使用包含 commit_docs 的 init 命令:
|
||||
INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init execute-phase "1")
|
||||
INIT=$(gsd-sdk query init.execute-phase "1")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# commit_docs 包含在所有 init 命令输出中
|
||||
```
|
||||
@@ -58,7 +58,7 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
**通过 CLI 提交(自动处理检查):**
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs: update state" --files .planning/STATE.md
|
||||
gsd-sdk query commit "docs: update state" .planning/STATE.md
|
||||
```
|
||||
|
||||
CLI 在内部检查 `commit_docs` 配置和 gitignore 状态 —— 无需手动条件判断。
|
||||
@@ -146,14 +146,14 @@ CLI 在内部检查 `commit_docs` 配置和 gitignore 状态 —— 无需手动
|
||||
|
||||
使用 `init execute-phase` 返回所有配置为 JSON:
|
||||
```bash
|
||||
INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init execute-phase "1")
|
||||
INIT=$(gsd-sdk query init.execute-phase "1")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# JSON 输出包含:branching_strategy, phase_branch_template, milestone_branch_template
|
||||
```
|
||||
|
||||
或使用 `state load` 获取配置值:
|
||||
```bash
|
||||
INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" state load)
|
||||
INIT=$(gsd-sdk query state.load)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# 从 JSON 解析 branching_strategy, phase_branch_template, milestone_branch_template
|
||||
```
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
* roadmap get-phase <phase> Extract phase section from ROADMAP.md
|
||||
* roadmap analyze Full roadmap parse with disk status
|
||||
* roadmap update-plan-progress <N> Update progress table row from disk (PLAN vs SUMMARY counts)
|
||||
* roadmap annotate-dependencies <N> Add wave dependency notes + cross-cutting constraints to ROADMAP.md
|
||||
*
|
||||
* Requirements Operations:
|
||||
* requirements mark-complete <ids> Mark requirement IDs as complete in REQUIREMENTS.md
|
||||
@@ -690,8 +691,10 @@ async function runCommand(command, args, cwd, raw, defaultValue) {
|
||||
roadmap.cmdRoadmapAnalyze(cwd, raw);
|
||||
} else if (subcommand === 'update-plan-progress') {
|
||||
roadmap.cmdRoadmapUpdatePlanProgress(cwd, args[2], raw);
|
||||
} else if (subcommand === 'annotate-dependencies') {
|
||||
roadmap.cmdRoadmapAnnotateDependencies(cwd, args[2], raw);
|
||||
} else {
|
||||
error('Unknown roadmap subcommand. Available: get-phase, analyze, update-plan-progress');
|
||||
error('Unknown roadmap subcommand. Available: get-phase, analyze, update-plan-progress, annotate-dependencies');
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -764,7 +767,8 @@ async function runCommand(command, args, cwd, raw, defaultValue) {
|
||||
verify.cmdValidateConsistency(cwd, raw);
|
||||
} else if (subcommand === 'health') {
|
||||
const repairFlag = args.includes('--repair');
|
||||
verify.cmdValidateHealth(cwd, { repair: repairFlag }, raw);
|
||||
const backfillFlag = args.includes('--backfill');
|
||||
verify.cmdValidateHealth(cwd, { repair: repairFlag, backfill: backfillFlag }, raw);
|
||||
} else if (subcommand === 'agents') {
|
||||
verify.cmdValidateAgents(cwd, raw);
|
||||
} else {
|
||||
@@ -1200,10 +1204,6 @@ async function runCommand(command, args, cwd, raw, defaultValue) {
|
||||
'agents',
|
||||
path.join('commands', 'gsd'),
|
||||
'hooks',
|
||||
// OpenCode/Kilo flat command dir
|
||||
'command',
|
||||
// Codex/Copilot skills dir
|
||||
'skills',
|
||||
];
|
||||
|
||||
function walkDir(dir, baseDir) {
|
||||
|
||||
52
get-shit-done/bin/lib/artifacts.cjs
Normal file
52
get-shit-done/bin/lib/artifacts.cjs
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Canonical GSD artifact registry.
|
||||
*
|
||||
* Enumerates the file names that gsd workflows officially produce at the
|
||||
* .planning/ root level. Used by gsd-health (W019) to flag unrecognized files
|
||||
* so stale or misnamed artifacts don't silently mislead agents or reviewers.
|
||||
*
|
||||
* Add entries here whenever a new workflow produces a .planning/ root file.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Exact-match canonical file names at .planning/ root
|
||||
const CANONICAL_EXACT = new Set([
|
||||
'PROJECT.md',
|
||||
'ROADMAP.md',
|
||||
'STATE.md',
|
||||
'REQUIREMENTS.md',
|
||||
'MILESTONES.md',
|
||||
'BACKLOG.md',
|
||||
'LEARNINGS.md',
|
||||
'THREADS.md',
|
||||
'config.json',
|
||||
'CLAUDE.md',
|
||||
]);
|
||||
|
||||
// Pattern-match canonical file names (regex tests on the basename)
|
||||
// Each pattern includes the name of the workflow that produces it as a comment.
|
||||
const CANONICAL_PATTERNS = [
|
||||
/^v\d+\.\d+(?:\.\d+)?-MILESTONE-AUDIT\.md$/i, // gsd-complete-milestone (pre-archive)
|
||||
/^v\d+\.\d+(?:\.\d+)?-.*\.md$/i, // other version-stamped planning docs
|
||||
];
|
||||
|
||||
/**
|
||||
* Return true if `filename` (basename only, no path) matches a canonical
|
||||
* .planning/ root artifact — either an exact name or a known pattern.
|
||||
*
|
||||
* @param {string} filename - Basename of the file (e.g. "STATE.md")
|
||||
*/
|
||||
function isCanonicalPlanningFile(filename) {
|
||||
if (CANONICAL_EXACT.has(filename)) return true;
|
||||
for (const pattern of CANONICAL_PATTERNS) {
|
||||
if (pattern.test(filename)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
CANONICAL_EXACT,
|
||||
CANONICAL_PATTERNS,
|
||||
isCanonicalPlanningFile,
|
||||
};
|
||||
@@ -34,6 +34,7 @@ const VALID_CONFIG_KEYS = new Set([
|
||||
'workflow.plan_bounce',
|
||||
'workflow.plan_bounce_script',
|
||||
'workflow.plan_bounce_passes',
|
||||
'workflow.plan_chunked',
|
||||
'workflow.security_enforcement',
|
||||
'workflow.security_asvs_level',
|
||||
'workflow.security_block_on',
|
||||
@@ -54,6 +55,7 @@ const VALID_CONFIG_KEYS = new Set([
|
||||
'graphify.enabled',
|
||||
'graphify.build_timeout',
|
||||
'claude_md_path',
|
||||
'claude_md_assembly.mode',
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -61,9 +63,10 @@ const VALID_CONFIG_KEYS = new Set([
|
||||
* Each entry has a `test` function and a human-readable `description`.
|
||||
*/
|
||||
const DYNAMIC_KEY_PATTERNS = [
|
||||
{ test: (k) => /^agent_skills\.[a-zA-Z0-9_-]+$/.test(k), description: 'agent_skills.<agent-type>' },
|
||||
{ test: (k) => /^review\.models\.[a-zA-Z0-9_-]+$/.test(k), description: 'review.models.<cli-name>' },
|
||||
{ test: (k) => /^features\.[a-zA-Z0-9_]+$/.test(k), description: 'features.<feature_name>' },
|
||||
{ test: (k) => /^agent_skills\.[a-zA-Z0-9_-]+$/.test(k), description: 'agent_skills.<agent-type>' },
|
||||
{ test: (k) => /^review\.models\.[a-zA-Z0-9_-]+$/.test(k), description: 'review.models.<cli-name>' },
|
||||
{ test: (k) => /^features\.[a-zA-Z0-9_]+$/.test(k), description: 'features.<feature_name>' },
|
||||
{ test: (k) => /^claude_md_assembly\.blocks\.[a-zA-Z0-9_]+$/.test(k), description: 'claude_md_assembly.blocks.<section>' },
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -394,6 +394,7 @@ function loadConfig(cwd) {
|
||||
manager: parsed.manager || {},
|
||||
response_language: get('response_language') || null,
|
||||
claude_md_path: get('claude_md_path') || null,
|
||||
claude_md_assembly: parsed.claude_md_assembly || null,
|
||||
};
|
||||
} catch {
|
||||
// Fall back to ~/.gsd/defaults.json only for truly pre-project contexts (#1683)
|
||||
@@ -1334,9 +1335,19 @@ function getRoadmapPhaseInternal(cwd, phaseNum) {
|
||||
|
||||
try {
|
||||
const content = extractCurrentMilestone(fs.readFileSync(roadmapPath, 'utf-8'), cwd);
|
||||
const escapedPhase = escapeRegex(phaseNum.toString());
|
||||
// Match both numeric (Phase 1:) and custom (Phase PROJ-42:) headers
|
||||
const phasePattern = new RegExp(`#{2,4}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`, 'i');
|
||||
// Strip leading zeros from purely numeric phase numbers so "03" matches "Phase 3:"
|
||||
// in canonical ROADMAP headings. Non-numeric IDs (e.g. "PROJ-42") are kept as-is.
|
||||
const normalized = /^\d+$/.test(String(phaseNum))
|
||||
? String(phaseNum).replace(/^0+(?=\d)/, '')
|
||||
: String(phaseNum);
|
||||
const escapedPhase = escapeRegex(normalized);
|
||||
// Match both numeric and custom (Phase PROJ-42:) headers.
|
||||
// For purely numeric phases allow optional leading zeros so both "Phase 1:" and
|
||||
// "Phase 01:" are matched regardless of whether the ROADMAP uses padded numbers.
|
||||
const isNumeric = /^\d+$/.test(String(phaseNum));
|
||||
const phasePattern = isNumeric
|
||||
? new RegExp(`#{2,4}\\s*Phase\\s+0*${escapedPhase}:\\s*([^\\n]+)`, 'i')
|
||||
: new RegExp(`#{2,4}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`, 'i');
|
||||
const headerMatch = content.match(phasePattern);
|
||||
if (!headerMatch) return null;
|
||||
|
||||
@@ -1509,6 +1520,50 @@ function getMilestoneInfo(cwd) {
|
||||
try {
|
||||
const roadmap = fs.readFileSync(path.join(planningDir(cwd), 'ROADMAP.md'), 'utf-8');
|
||||
|
||||
// 0. Prefer STATE.md milestone: frontmatter as the authoritative source.
|
||||
// This prevents falling through to a regex that may match an old heading
|
||||
// when the active milestone's 🚧 marker is inside a <summary> tag without
|
||||
// **bold** formatting (bug #2409).
|
||||
let stateVersion = null;
|
||||
if (cwd) {
|
||||
try {
|
||||
const statePath = path.join(planningDir(cwd), 'STATE.md');
|
||||
if (fs.existsSync(statePath)) {
|
||||
const stateRaw = fs.readFileSync(statePath, 'utf-8');
|
||||
const m = stateRaw.match(/^milestone:\s*(.+)/m);
|
||||
if (m) stateVersion = m[1].trim();
|
||||
}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
if (stateVersion) {
|
||||
// Look up the name for this version in ROADMAP.md
|
||||
const escapedVer = escapeRegex(stateVersion);
|
||||
// Match heading-format: ## Roadmap v2.9: Name or ## v2.9 Name
|
||||
const headingMatch = roadmap.match(
|
||||
new RegExp(`##[^\\n]*${escapedVer}[:\\s]+([^\\n(]+)`, 'i')
|
||||
);
|
||||
if (headingMatch) {
|
||||
// If the heading line contains ✅ the milestone is already shipped.
|
||||
// Fall through to normal detection so the NEW active milestone is returned
|
||||
// instead of the stale shipped one still recorded in STATE.md.
|
||||
if (!headingMatch[0].includes('✅')) {
|
||||
return { version: stateVersion, name: headingMatch[1].trim() };
|
||||
}
|
||||
// Shipped milestone — do not early-return; fall through to normal detection below.
|
||||
} else {
|
||||
// Match list-format: 🚧 **v2.9 Name** or 🚧 v2.9 Name
|
||||
const listMatch = roadmap.match(
|
||||
new RegExp(`🚧\\s*\\*?\\*?${escapedVer}\\s+([^*\\n]+)`, 'i')
|
||||
);
|
||||
if (listMatch) {
|
||||
return { version: stateVersion, name: listMatch[1].trim() };
|
||||
}
|
||||
// Version found in STATE.md but no name match in ROADMAP — return bare version
|
||||
return { version: stateVersion, name: 'milestone' };
|
||||
}
|
||||
}
|
||||
|
||||
// First: check for list-format roadmaps using 🚧 (in-progress) marker
|
||||
// e.g. "- 🚧 **v2.1 Belgium** — Phases 24-28 (in progress)"
|
||||
// e.g. "- 🚧 **v1.2.1 Tech Debt** — Phases 1-8 (in progress)"
|
||||
@@ -1520,11 +1575,14 @@ function getMilestoneInfo(cwd) {
|
||||
};
|
||||
}
|
||||
|
||||
// Second: heading-format roadmaps — strip shipped milestones in <details> blocks
|
||||
// Second: heading-format roadmaps — strip shipped milestones.
|
||||
// <details> blocks are stripped by stripShippedMilestones; heading-format ✅ markers
|
||||
// are excluded by the negative lookahead below so a stale STATE.md version (or any
|
||||
// shipped ✅ heading) never wins over the first non-shipped milestone heading.
|
||||
const cleaned = stripShippedMilestones(roadmap);
|
||||
// Extract version and name from the same ## heading for consistency
|
||||
// Negative lookahead skips headings that contain ✅ (shipped milestone marker).
|
||||
// Supports 2+ segment versions: v1.2, v1.2.1, v2.0.1, etc.
|
||||
const headingMatch = cleaned.match(/## .*v(\d+(?:\.\d+)+)[:\s]+([^\n(]+)/);
|
||||
const headingMatch = cleaned.match(/## (?!.*✅).*v(\d+(?:\.\d+)+)[:\s]+([^\n(]+)/);
|
||||
if (headingMatch) {
|
||||
return {
|
||||
version: 'v' + headingMatch[1],
|
||||
|
||||
@@ -458,8 +458,11 @@ function cmdInitNewMilestone(cwd, raw) {
|
||||
|
||||
try {
|
||||
if (fs.existsSync(phasesDir)) {
|
||||
// Bug #2445: filter phase dirs to current milestone only so stale dirs
|
||||
// from a prior milestone that were not archived don't inflate the count.
|
||||
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
||||
phaseDirCount = fs.readdirSync(phasesDir, { withFileTypes: true })
|
||||
.filter(entry => entry.isDirectory())
|
||||
.filter(entry => entry.isDirectory() && isDirInMilestone(entry.name))
|
||||
.length;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
@@ -625,7 +625,7 @@ function renameIntegerPhases(phasesDir, removedInt) {
|
||||
const m = dir.match(/^(\d+)([A-Z])?(?:\.(\d+))?-(.+)$/i);
|
||||
if (!m) return null;
|
||||
const dirInt = parseInt(m[1], 10);
|
||||
return dirInt > removedInt ? { dir, oldInt: dirInt, letter: m[2] ? m[2].toUpperCase() : '', decimal: m[3] ? parseInt(m[3], 10) : null, slug: m[4] } : null;
|
||||
return (dirInt > removedInt && dirInt < 999) ? { dir, oldInt: dirInt, letter: m[2] ? m[2].toUpperCase() : '', decimal: m[3] ? parseInt(m[3], 10) : null, slug: m[4] } : null;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => a.oldInt !== b.oldInt ? b.oldInt - a.oldInt : (b.decimal || 0) - (a.decimal || 0));
|
||||
@@ -673,7 +673,7 @@ function updateRoadmapAfterPhaseRemoval(roadmapPath, targetPhase, isDecimal, rem
|
||||
const oldPad = oldStr.padStart(2, '0'), newPad = newStr.padStart(2, '0');
|
||||
content = content.replace(new RegExp(`(#{2,4}\\s*Phase\\s+)${oldStr}(\\s*:)`, 'gi'), `$1${newStr}$2`);
|
||||
content = content.replace(new RegExp(`(Phase\\s+)${oldStr}([:\\s])`, 'g'), `$1${newStr}$2`);
|
||||
content = content.replace(new RegExp(`${oldPad}-(\\d{2})`, 'g'), `${newPad}-$1`);
|
||||
content = content.replace(new RegExp(`(?<![0-9-])${oldPad}-(\\d{2})(?![0-9-])`, 'g'), `${newPad}-$1`);
|
||||
content = content.replace(new RegExp(`(\\|\\s*)${oldStr}\\.\\s`, 'g'), `$1${newStr}. `);
|
||||
content = content.replace(new RegExp(`(Depends on:\\*\\*\\s*Phase\\s+)${oldStr}\\b`, 'gi'), `$1${newStr}`);
|
||||
}
|
||||
@@ -870,9 +870,10 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
||||
const sectionText = phaseSectionMatch ? phaseSectionMatch[1] : '';
|
||||
const reqMatch = sectionText.match(/\*\*Requirements:\*\*\s*([^\n]+)/i);
|
||||
|
||||
let reqContent = fs.readFileSync(reqPath, 'utf-8');
|
||||
|
||||
if (reqMatch) {
|
||||
const reqIds = reqMatch[1].replace(/[\[\]]/g, '').split(/[,\s]+/).map(r => r.trim()).filter(Boolean);
|
||||
let reqContent = fs.readFileSync(reqPath, 'utf-8');
|
||||
|
||||
for (const reqId of reqIds) {
|
||||
const reqEscaped = escapeRegex(reqId);
|
||||
@@ -887,10 +888,40 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
||||
'$1 Complete $2'
|
||||
);
|
||||
}
|
||||
|
||||
atomicWriteFileSync(reqPath, reqContent);
|
||||
requirementsUpdated = true;
|
||||
}
|
||||
|
||||
// Scan body for all **REQ-ID** patterns, warn about any missing from the Traceability table.
|
||||
// Always runs regardless of whether the roadmap has a Requirements: line.
|
||||
const bodyReqIds = [];
|
||||
const bodyReqPattern = /\*\*([A-Z][A-Z0-9]*-\d+)\*\*/g;
|
||||
let bodyMatch;
|
||||
while ((bodyMatch = bodyReqPattern.exec(reqContent)) !== null) {
|
||||
const id = bodyMatch[1];
|
||||
if (!bodyReqIds.includes(id)) bodyReqIds.push(id);
|
||||
}
|
||||
|
||||
// Collect REQ-IDs present in the Traceability section only, to avoid
|
||||
// picking up IDs from other tables in the document.
|
||||
const traceabilityHeadingMatch = reqContent.match(/^#{1,6}\s+Traceability\b/im);
|
||||
const traceabilitySection = traceabilityHeadingMatch
|
||||
? reqContent.slice(traceabilityHeadingMatch.index)
|
||||
: '';
|
||||
const tableReqIds = new Set();
|
||||
const tableRowPattern = /^\|\s*([A-Z][A-Z0-9]*-\d+)\s*\|/gm;
|
||||
let tableMatch;
|
||||
while ((tableMatch = tableRowPattern.exec(traceabilitySection)) !== null) {
|
||||
tableReqIds.add(tableMatch[1]);
|
||||
}
|
||||
|
||||
const unregistered = bodyReqIds.filter(id => !tableReqIds.has(id));
|
||||
if (unregistered.length > 0) {
|
||||
warnings.push(
|
||||
`REQUIREMENTS.md: ${unregistered.length} REQ-ID(s) found in body but missing from Traceability table: ${unregistered.join(', ')} — add them manually to keep traceability in sync`
|
||||
);
|
||||
}
|
||||
|
||||
atomicWriteFileSync(reqPath, reqContent);
|
||||
requirementsUpdated = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ function generateProjectSection(cwd) {
|
||||
const projectPath = path.join(cwd, '.planning', 'PROJECT.md');
|
||||
const content = safeReadFile(projectPath);
|
||||
if (!content) {
|
||||
return { content: CLAUDE_MD_FALLBACKS.project, source: 'PROJECT.md', hasFallback: true };
|
||||
return { content: CLAUDE_MD_FALLBACKS.project, source: 'PROJECT.md', linkPath: null, hasFallback: true };
|
||||
}
|
||||
const parts = [];
|
||||
const h1Match = content.match(/^# (.+)$/m);
|
||||
@@ -306,9 +306,9 @@ function generateProjectSection(cwd) {
|
||||
if (body) parts.push(`### Constraints\n\n${body}`);
|
||||
}
|
||||
if (parts.length === 0) {
|
||||
return { content: CLAUDE_MD_FALLBACKS.project, source: 'PROJECT.md', hasFallback: true };
|
||||
return { content: CLAUDE_MD_FALLBACKS.project, source: 'PROJECT.md', linkPath: null, hasFallback: true };
|
||||
}
|
||||
return { content: parts.join('\n\n'), source: 'PROJECT.md', hasFallback: false };
|
||||
return { content: parts.join('\n\n'), source: 'PROJECT.md', linkPath: '.planning/PROJECT.md', hasFallback: false };
|
||||
}
|
||||
|
||||
function generateStackSection(cwd) {
|
||||
@@ -316,12 +316,14 @@ function generateStackSection(cwd) {
|
||||
const researchPath = path.join(cwd, '.planning', 'research', 'STACK.md');
|
||||
let content = safeReadFile(codebasePath);
|
||||
let source = 'codebase/STACK.md';
|
||||
let linkPath = '.planning/codebase/STACK.md';
|
||||
if (!content) {
|
||||
content = safeReadFile(researchPath);
|
||||
source = 'research/STACK.md';
|
||||
linkPath = '.planning/research/STACK.md';
|
||||
}
|
||||
if (!content) {
|
||||
return { content: CLAUDE_MD_FALLBACKS.stack, source: 'STACK.md', hasFallback: true };
|
||||
return { content: CLAUDE_MD_FALLBACKS.stack, source: 'STACK.md', linkPath: null, hasFallback: true };
|
||||
}
|
||||
const lines = content.split('\n');
|
||||
const summaryLines = [];
|
||||
@@ -336,14 +338,14 @@ function generateStackSection(cwd) {
|
||||
if (line.startsWith('- ') || line.startsWith('* ')) summaryLines.push(line);
|
||||
}
|
||||
const summary = summaryLines.length > 0 ? summaryLines.join('\n') : content.trim();
|
||||
return { content: summary, source, hasFallback: false };
|
||||
return { content: summary, source, linkPath, hasFallback: false };
|
||||
}
|
||||
|
||||
function generateConventionsSection(cwd) {
|
||||
const conventionsPath = path.join(cwd, '.planning', 'codebase', 'CONVENTIONS.md');
|
||||
const content = safeReadFile(conventionsPath);
|
||||
if (!content) {
|
||||
return { content: CLAUDE_MD_FALLBACKS.conventions, source: 'CONVENTIONS.md', hasFallback: true };
|
||||
return { content: CLAUDE_MD_FALLBACKS.conventions, source: 'CONVENTIONS.md', linkPath: null, hasFallback: true };
|
||||
}
|
||||
const lines = content.split('\n');
|
||||
const summaryLines = [];
|
||||
@@ -352,14 +354,14 @@ function generateConventionsSection(cwd) {
|
||||
if (line.startsWith('- ') || line.startsWith('* ') || line.startsWith('|')) summaryLines.push(line);
|
||||
}
|
||||
const summary = summaryLines.length > 0 ? summaryLines.join('\n') : content.trim();
|
||||
return { content: summary, source: 'CONVENTIONS.md', hasFallback: false };
|
||||
return { content: summary, source: 'CONVENTIONS.md', linkPath: '.planning/codebase/CONVENTIONS.md', hasFallback: false };
|
||||
}
|
||||
|
||||
function generateArchitectureSection(cwd) {
|
||||
const architecturePath = path.join(cwd, '.planning', 'codebase', 'ARCHITECTURE.md');
|
||||
const content = safeReadFile(architecturePath);
|
||||
if (!content) {
|
||||
return { content: CLAUDE_MD_FALLBACKS.architecture, source: 'ARCHITECTURE.md', hasFallback: true };
|
||||
return { content: CLAUDE_MD_FALLBACKS.architecture, source: 'ARCHITECTURE.md', linkPath: null, hasFallback: true };
|
||||
}
|
||||
const lines = content.split('\n');
|
||||
const summaryLines = [];
|
||||
@@ -368,13 +370,14 @@ function generateArchitectureSection(cwd) {
|
||||
if (line.startsWith('- ') || line.startsWith('* ') || line.startsWith('|') || line.startsWith('```')) summaryLines.push(line);
|
||||
}
|
||||
const summary = summaryLines.length > 0 ? summaryLines.join('\n') : content.trim();
|
||||
return { content: summary, source: 'ARCHITECTURE.md', hasFallback: false };
|
||||
return { content: summary, source: 'ARCHITECTURE.md', linkPath: '.planning/codebase/ARCHITECTURE.md', hasFallback: false };
|
||||
}
|
||||
|
||||
function generateWorkflowSection() {
|
||||
return {
|
||||
content: CLAUDE_MD_WORKFLOW_ENFORCEMENT,
|
||||
source: 'GSD defaults',
|
||||
linkPath: null,
|
||||
hasFallback: false,
|
||||
};
|
||||
}
|
||||
@@ -948,19 +951,35 @@ function cmdGenerateClaudeMd(cwd, options, raw) {
|
||||
}
|
||||
}
|
||||
|
||||
let assemblyConfig = {};
|
||||
let configClaudeMdPath = './CLAUDE.md';
|
||||
try {
|
||||
const config = loadConfig(cwd);
|
||||
if (config.claude_md_path) configClaudeMdPath = config.claude_md_path;
|
||||
if (config.claude_md_assembly) assemblyConfig = config.claude_md_assembly;
|
||||
} catch { /* use default */ }
|
||||
|
||||
let outputPath = options.output;
|
||||
if (!outputPath) {
|
||||
// Read claude_md_path from config, default to ./CLAUDE.md
|
||||
let configClaudeMdPath = './CLAUDE.md';
|
||||
try {
|
||||
const config = loadConfig(cwd);
|
||||
if (config.claude_md_path) configClaudeMdPath = config.claude_md_path;
|
||||
} catch { /* use default */ }
|
||||
outputPath = path.isAbsolute(configClaudeMdPath) ? configClaudeMdPath : path.join(cwd, configClaudeMdPath);
|
||||
} else if (!path.isAbsolute(outputPath)) {
|
||||
outputPath = path.join(cwd, outputPath);
|
||||
}
|
||||
|
||||
const globalAssemblyMode = assemblyConfig.mode || 'embed';
|
||||
const blockModes = assemblyConfig.blocks || {};
|
||||
|
||||
// Return the assembled content for a section, respecting link vs embed mode.
|
||||
// "link" mode writes `@<linkPath>` when the generator has a real source file.
|
||||
// Falls back to "embed" for sections without a linkable source (workflow, fallbacks).
|
||||
function buildSectionContent(name, gen, heading) {
|
||||
const effectiveMode = blockModes[name] || globalAssemblyMode;
|
||||
if (effectiveMode === 'link' && gen.linkPath && !gen.hasFallback) {
|
||||
return buildSection(name, gen.source, `${heading}\n\n@${gen.linkPath}`);
|
||||
}
|
||||
return buildSection(name, gen.source, `${heading}\n\n${gen.content}`);
|
||||
}
|
||||
|
||||
let existingContent = safeReadFile(outputPath);
|
||||
let action;
|
||||
|
||||
@@ -969,8 +988,7 @@ function cmdGenerateClaudeMd(cwd, options, raw) {
|
||||
for (const name of MANAGED_SECTIONS) {
|
||||
const gen = generated[name];
|
||||
const heading = sectionHeadings[name];
|
||||
const body = `${heading}\n\n${gen.content}`;
|
||||
sections.push(buildSection(name, gen.source, body));
|
||||
sections.push(buildSectionContent(name, gen, heading));
|
||||
}
|
||||
sections.push('');
|
||||
sections.push(CLAUDE_MD_PROFILE_PLACEHOLDER);
|
||||
@@ -985,13 +1003,15 @@ function cmdGenerateClaudeMd(cwd, options, raw) {
|
||||
for (const name of MANAGED_SECTIONS) {
|
||||
const gen = generated[name];
|
||||
const heading = sectionHeadings[name];
|
||||
const body = `${heading}\n\n${gen.content}`;
|
||||
const fullSection = buildSection(name, gen.source, body);
|
||||
const fullSection = buildSectionContent(name, gen, heading);
|
||||
const hasMarkers = fileContent.indexOf(`<!-- GSD:${name}-start`) !== -1;
|
||||
|
||||
if (hasMarkers) {
|
||||
if (options.auto) {
|
||||
const expectedBody = `${heading}\n\n${gen.content}`;
|
||||
const effectiveMode = blockModes[name] || globalAssemblyMode;
|
||||
const expectedBody = (effectiveMode === 'link' && gen.linkPath && !gen.hasFallback)
|
||||
? `${heading}\n\n@${gen.linkPath}`
|
||||
: `${heading}\n\n${gen.content}`;
|
||||
if (detectManualEdit(fileContent, name, expectedBody)) {
|
||||
sectionsSkipped.push(name);
|
||||
const genIdx = sectionsGenerated.indexOf(name);
|
||||
|
||||
@@ -353,8 +353,171 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
||||
}, raw, `${summaryCount}/${planCount} ${status}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotate the ROADMAP.md plan list for a phase with wave dependency notes
|
||||
* and a cross-cutting constraints subsection derived from PLAN frontmatter.
|
||||
*
|
||||
* Wave dependency notes: "Wave 2 — blocked on Wave 1 completion" inserted as
|
||||
* bold headers before each wave group in the plan checklist.
|
||||
*
|
||||
* Cross-cutting constraints: must_haves.truths strings that appear in 2+ plans
|
||||
* are surfaced in a "Cross-cutting constraints" subsection below the plan list.
|
||||
*
|
||||
* The operation is idempotent: if wave headers already exist in the section
|
||||
* the function returns without modifying the file.
|
||||
*/
|
||||
function cmdRoadmapAnnotateDependencies(cwd, phaseNum, raw) {
|
||||
if (!phaseNum) {
|
||||
error('phase number required for roadmap annotate-dependencies');
|
||||
}
|
||||
|
||||
const roadmapPath = planningPaths(cwd).roadmap;
|
||||
if (!fs.existsSync(roadmapPath)) {
|
||||
output({ updated: false, reason: 'ROADMAP.md not found' }, raw, 'no roadmap');
|
||||
return;
|
||||
}
|
||||
|
||||
const phaseInfo = findPhaseInternal(cwd, phaseNum);
|
||||
if (!phaseInfo || phaseInfo.plans.length === 0) {
|
||||
output({ updated: false, reason: 'no plans found for phase', phase: phaseNum }, raw, 'no plans');
|
||||
return;
|
||||
}
|
||||
|
||||
const { extractFrontmatter, parseMustHavesBlock } = require('./frontmatter.cjs');
|
||||
|
||||
// Read each PLAN.md and extract wave + must_haves.truths
|
||||
const planData = [];
|
||||
for (const planFile of phaseInfo.plans) {
|
||||
const planPath = path.join(path.resolve(cwd, phaseInfo.directory), planFile);
|
||||
try {
|
||||
const content = fs.readFileSync(planPath, 'utf-8');
|
||||
const fm = extractFrontmatter(content);
|
||||
const wave = parseInt(fm.wave, 10) || 1;
|
||||
const planId = planFile.replace(/-PLAN\.md$/i, '').replace(/PLAN\.md$/i, '');
|
||||
const truths = parseMustHavesBlock(content, 'truths') || [];
|
||||
planData.push({ planFile, planId, wave, truths });
|
||||
} catch { /* skip unreadable plans */ }
|
||||
}
|
||||
|
||||
if (planData.length === 0) {
|
||||
output({ updated: false, reason: 'could not read plan frontmatter' }, raw, 'no frontmatter');
|
||||
return;
|
||||
}
|
||||
|
||||
// Group plans by wave (sorted)
|
||||
const waveGroups = new Map();
|
||||
for (const p of planData) {
|
||||
if (!waveGroups.has(p.wave)) waveGroups.set(p.wave, []);
|
||||
waveGroups.get(p.wave).push(p);
|
||||
}
|
||||
const waves = [...waveGroups.keys()].sort((a, b) => a - b);
|
||||
|
||||
// Find cross-cutting truths: appear in 2+ plans (de-duplicated, case-insensitive)
|
||||
const truthCounts = new Map();
|
||||
for (const { truths } of planData) {
|
||||
const seen = new Set();
|
||||
for (const t of truths) {
|
||||
const key = t.trim().toLowerCase();
|
||||
if (!key || seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
truthCounts.set(key, (truthCounts.get(key) || { count: 0, text: t.trim() }));
|
||||
truthCounts.get(key).count++;
|
||||
}
|
||||
}
|
||||
const crossCuttingTruths = [...truthCounts.values()]
|
||||
.filter(v => v.count >= 2)
|
||||
.map(v => v.text);
|
||||
|
||||
// Patch ROADMAP.md
|
||||
let updated = false;
|
||||
withPlanningLock(cwd, () => {
|
||||
let content = fs.readFileSync(roadmapPath, 'utf-8');
|
||||
|
||||
// Find the phase section
|
||||
const phaseEscaped = escapeRegex(phaseNum);
|
||||
const phaseHeaderPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}:[^\\n]*)`, 'i');
|
||||
const phaseMatch = content.match(phaseHeaderPattern);
|
||||
if (!phaseMatch) return;
|
||||
|
||||
const phaseStart = phaseMatch.index;
|
||||
const restAfterHeader = content.slice(phaseStart);
|
||||
const nextPhaseOffset = restAfterHeader.slice(1).search(/\n#{2,4}\s+Phase\s+\d/i);
|
||||
const phaseEnd = nextPhaseOffset >= 0 ? phaseStart + 1 + nextPhaseOffset : content.length;
|
||||
const phaseSection = content.slice(phaseStart, phaseEnd);
|
||||
|
||||
// Idempotency: skip if annotation markers already present
|
||||
if (
|
||||
/\*\*Wave\s+\d+/i.test(phaseSection) ||
|
||||
/\*\*Cross-cutting constraints:\*\*/i.test(phaseSection)
|
||||
) return;
|
||||
|
||||
// Find the Plans: section within the phase section
|
||||
const plansBlockMatch = phaseSection.match(/(Plans:\s*\n)((?:\s*-\s*\[[ x]\][^\n]*\n?)*)/i);
|
||||
if (!plansBlockMatch) return;
|
||||
|
||||
const plansHeader = plansBlockMatch[1];
|
||||
const existingList = plansBlockMatch[2];
|
||||
const listLines = existingList.split('\n').filter(l => /^\s*-\s*\[/.test(l));
|
||||
|
||||
if (listLines.length === 0) return;
|
||||
|
||||
// Build wave-annotated plan list
|
||||
const linesByWave = new Map();
|
||||
for (const line of listLines) {
|
||||
// Match plan ID from line: "- [ ] 01-01-PLAN.md — ..." or "- [ ] 01-01: ..."
|
||||
const idMatch = line.match(/\[\s*[x ]\s*\]\s*([\w-]+?)(?:-PLAN\.md|\.md|:|\s—)/i);
|
||||
const planId = idMatch ? idMatch[1] : null;
|
||||
const planEntry = planId ? planData.find(p => p.planId === planId) : null;
|
||||
const wave = planEntry ? planEntry.wave : 1;
|
||||
if (!linesByWave.has(wave)) linesByWave.set(wave, []);
|
||||
linesByWave.get(wave).push(line);
|
||||
}
|
||||
|
||||
const annotatedLines = [];
|
||||
const sortedWaves = [...linesByWave.keys()].sort((a, b) => a - b);
|
||||
for (let i = 0; i < sortedWaves.length; i++) {
|
||||
const w = sortedWaves[i];
|
||||
const waveLines = linesByWave.get(w);
|
||||
if (sortedWaves.length > 1) {
|
||||
const dep = i > 0 ? ` *(blocked on Wave ${sortedWaves[i - 1]} completion)*` : '';
|
||||
annotatedLines.push(`**Wave ${w}**${dep}`);
|
||||
}
|
||||
annotatedLines.push(...waveLines);
|
||||
if (i < sortedWaves.length - 1) annotatedLines.push('');
|
||||
}
|
||||
|
||||
// Append cross-cutting constraints subsection if any found
|
||||
if (crossCuttingTruths.length > 0) {
|
||||
annotatedLines.push('');
|
||||
annotatedLines.push('**Cross-cutting constraints:**');
|
||||
for (const t of crossCuttingTruths) {
|
||||
annotatedLines.push(`- ${t}`);
|
||||
}
|
||||
}
|
||||
|
||||
const newListBlock = annotatedLines.join('\n') + '\n';
|
||||
const newPhaseSection = phaseSection.replace(
|
||||
plansBlockMatch[0],
|
||||
plansHeader + newListBlock
|
||||
);
|
||||
|
||||
const nextContent = content.slice(0, phaseStart) + newPhaseSection + content.slice(phaseEnd);
|
||||
if (nextContent === content) return;
|
||||
atomicWriteFileSync(roadmapPath, nextContent);
|
||||
updated = true;
|
||||
});
|
||||
|
||||
output({
|
||||
updated,
|
||||
phase: phaseNum,
|
||||
waves: waves.length,
|
||||
cross_cutting_constraints: crossCuttingTruths.length,
|
||||
}, raw, updated ? `annotated ${waves.length} wave(s), ${crossCuttingTruths.length} constraint(s)` : 'skipped (already annotated or no plan list)');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
cmdRoadmapGetPhase,
|
||||
cmdRoadmapAnalyze,
|
||||
cmdRoadmapUpdatePlanProgress,
|
||||
cmdRoadmapAnnotateDependencies,
|
||||
};
|
||||
|
||||
@@ -245,14 +245,15 @@ function sanitizeForPrompt(text) {
|
||||
// Neutralize XML/HTML tags that mimic system boundaries
|
||||
// Replace < > with full-width equivalents to prevent tag interpretation
|
||||
// Note: <instructions> is excluded — GSD uses it as legitimate prompt structure
|
||||
sanitized = sanitized.replace(/<(\/?)(?:system|assistant|human)>/gi,
|
||||
// Matches system|assistant|human|user with optional whitespace before the closing >
|
||||
sanitized = sanitized.replace(/<(\/?)\s*(?:system|assistant|human|user)\s*>/gi,
|
||||
(_, slash) => `<${slash || ''}system-text>`);
|
||||
|
||||
// Neutralize [SYSTEM] / [INST] / [/INST] markers
|
||||
// Neutralize [SYSTEM] / [INST] / [/INST] markers — both opening and closing variants
|
||||
sanitized = sanitized.replace(/\[(\/?)(SYSTEM|INST)\]/gi, (_, slash, tag) => `[${slash}${tag.toUpperCase()}-TEXT]`);
|
||||
|
||||
// Neutralize <<SYS>> markers
|
||||
sanitized = sanitized.replace(/<<\s*SYS\s*>>/gi, '«SYS-TEXT»');
|
||||
// Neutralize <<SYS>> and <</SYS>> markers (Llama-style delimiters)
|
||||
sanitized = sanitized.replace(/<<\/?\s*SYS\s*>>/gi, '«SYS-TEXT»');
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
@@ -29,12 +29,13 @@ process.on('exit', () => {
|
||||
|
||||
// Shared helper: extract a field value from STATE.md content.
|
||||
// Supports both **Field:** bold and plain Field: format.
|
||||
// Horizontal whitespace only after ':' so YAML keys like `progress:` do not match as `Progress:` (parity with sdk/helpers stateExtractField).
|
||||
function stateExtractField(content, fieldName) {
|
||||
const escaped = escapeRegex(fieldName);
|
||||
const boldPattern = new RegExp(`\\*\\*${escaped}:\\*\\*\\s*(.+)`, 'i');
|
||||
const boldPattern = new RegExp(`\\*\\*${escaped}:\\*\\*[ \\t]*(.+)`, 'i');
|
||||
const boldMatch = content.match(boldPattern);
|
||||
if (boldMatch) return boldMatch[1].trim();
|
||||
const plainPattern = new RegExp(`^${escaped}:\\s*(.+)`, 'im');
|
||||
const plainPattern = new RegExp(`^${escaped}:[ \\t]*(.+)`, 'im');
|
||||
const plainMatch = content.match(plainPattern);
|
||||
return plainMatch ? plainMatch[1].trim() : null;
|
||||
}
|
||||
@@ -720,7 +721,13 @@ function buildStateFrontmatter(bodyContent, cwd) {
|
||||
const status = stateExtractField(bodyContent, 'Status');
|
||||
const progressRaw = stateExtractField(bodyContent, 'Progress');
|
||||
const lastActivity = stateExtractField(bodyContent, 'Last Activity');
|
||||
const stoppedAt = stateExtractField(bodyContent, 'Stopped At') || stateExtractField(bodyContent, 'Stopped at');
|
||||
// Bug #2444: scope Stopped At extraction to the ## Session section so that
|
||||
// historical "Stopped at:" prose elsewhere in the body (e.g. in a
|
||||
// Session Continuity Archive section) never overwrites the current value.
|
||||
// Fall back to full-body search only when no ## Session section exists.
|
||||
const sessionSectionMatch = bodyContent.match(/##\s*Session\s*\n([\s\S]*?)(?=\n##|$)/i);
|
||||
const sessionBodyScope = sessionSectionMatch ? sessionSectionMatch[1] : bodyContent;
|
||||
const stoppedAt = stateExtractField(sessionBodyScope, 'Stopped At') || stateExtractField(sessionBodyScope, 'Stopped at');
|
||||
const pausedAt = stateExtractField(bodyContent, 'Paused At');
|
||||
|
||||
let milestone = null;
|
||||
@@ -747,9 +754,33 @@ function buildStateFrontmatter(bodyContent, cwd) {
|
||||
let cached = _diskScanCache.get(cwd);
|
||||
if (!cached) {
|
||||
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
||||
const phaseDirs = fs.readdirSync(phasesDir, { withFileTypes: true })
|
||||
const allMatchingDirs = fs.readdirSync(phasesDir, { withFileTypes: true })
|
||||
.filter(e => e.isDirectory()).map(e => e.name)
|
||||
.filter(isDirInMilestone);
|
||||
|
||||
// Bug #2445: when stale phase dirs from a prior milestone remain in
|
||||
// .planning/phases/ alongside new dirs with the same phase number,
|
||||
// de-duplicate by normalized phase number keeping the most recently
|
||||
// modified dir. This prevents double-counting (e.g. two "Phase 1" dirs).
|
||||
const seenPhaseNums = new Map(); // normalizedNum -> dirName
|
||||
for (const dir of allMatchingDirs) {
|
||||
const m = dir.match(/^0*(\d+[A-Za-z]?(?:\.\d+)*)/);
|
||||
const key = m ? m[1].toLowerCase() : dir;
|
||||
if (!seenPhaseNums.has(key)) {
|
||||
seenPhaseNums.set(key, dir);
|
||||
} else {
|
||||
// Keep the dir that is newer on disk (more likely current milestone)
|
||||
try {
|
||||
const existing = path.join(phasesDir, seenPhaseNums.get(key));
|
||||
const candidate = path.join(phasesDir, dir);
|
||||
if (fs.statSync(candidate).mtimeMs > fs.statSync(existing).mtimeMs) {
|
||||
seenPhaseNums.set(key, dir);
|
||||
}
|
||||
} catch { /* keep existing on stat error */ }
|
||||
}
|
||||
}
|
||||
const phaseDirs = [...seenPhaseNums.values()];
|
||||
|
||||
let diskTotalPlans = 0;
|
||||
let diskTotalSummaries = 0;
|
||||
let diskCompletedPhases = 0;
|
||||
|
||||
@@ -225,6 +225,11 @@ function parseVerificationItems(content, status) {
|
||||
const numberedMatch = line.match(/^(\d+)\.\s+(.+)/);
|
||||
|
||||
if (tableMatch) {
|
||||
// Skip rows that already have a passing result (PASS, pass, resolved, etc.)
|
||||
const rowRemainder = line.slice(tableMatch.index + tableMatch[0].length);
|
||||
const cellValues = rowRemainder.split('|').map(c => c.trim());
|
||||
const hasPassResult = cellValues.some(c => /^pass$/i.test(c) || /^resolved$/i.test(c));
|
||||
if (hasPassResult) continue;
|
||||
items.push({
|
||||
test: parseInt(tableMatch[1], 10),
|
||||
name: tableMatch[2].trim(),
|
||||
|
||||
@@ -871,6 +871,54 @@ function cmdValidateHealth(cwd, options, raw) {
|
||||
}
|
||||
} catch { /* git worktree not available or not a git repo — skip silently */ }
|
||||
|
||||
// ─── Check 12: MILESTONES.md / archive snapshot drift (#2446) ─────────────
|
||||
const milestonesPath = path.join(planBase, 'MILESTONES.md');
|
||||
const milestonesArchiveDir = path.join(planBase, 'milestones');
|
||||
const missingFromRegistry = [];
|
||||
try {
|
||||
if (fs.existsSync(milestonesArchiveDir)) {
|
||||
const archiveFiles = fs.readdirSync(milestonesArchiveDir);
|
||||
const archivedVersions = archiveFiles
|
||||
.map(f => f.match(/^(v\d+\.\d+(?:\.\d+)?)-ROADMAP\.md$/))
|
||||
.filter(Boolean)
|
||||
.map(m => m[1]);
|
||||
|
||||
if (archivedVersions.length > 0) {
|
||||
const registryContent = fs.existsSync(milestonesPath)
|
||||
? fs.readFileSync(milestonesPath, 'utf-8')
|
||||
: '';
|
||||
for (const ver of archivedVersions) {
|
||||
if (!registryContent.includes(`## ${ver}`)) {
|
||||
missingFromRegistry.push(ver);
|
||||
}
|
||||
}
|
||||
if (missingFromRegistry.length > 0) {
|
||||
addIssue('warning', 'W018',
|
||||
`MILESTONES.md missing ${missingFromRegistry.length} archived milestone(s): ${missingFromRegistry.join(', ')}`,
|
||||
'Run /gsd-health --backfill to synthesize missing entries from archive snapshots',
|
||||
true);
|
||||
repairs.push('backfillMilestones');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { /* intentionally empty — milestone sync check is advisory */ }
|
||||
|
||||
// ─── Check 13: Unrecognized .planning/ root files (W019) ──────────────────
|
||||
try {
|
||||
const { isCanonicalPlanningFile } = require('./artifacts.cjs');
|
||||
const entries = fs.readdirSync(planBase, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile()) continue;
|
||||
if (!entry.name.endsWith('.md')) continue;
|
||||
if (!isCanonicalPlanningFile(entry.name)) {
|
||||
addIssue('warning', 'W019',
|
||||
`Unrecognized .planning/ file: ${entry.name} — not a canonical GSD artifact`,
|
||||
'Move to .planning/milestones/ archive subdir or delete if stale. See templates/README.md for the canonical artifact list.',
|
||||
false);
|
||||
}
|
||||
}
|
||||
} catch { /* artifact check is advisory — skip on error */ }
|
||||
|
||||
// ─── Perform repairs if requested ─────────────────────────────────────────
|
||||
const repairActions = [];
|
||||
if (options.repair && repairs.length > 0) {
|
||||
@@ -960,6 +1008,39 @@ function cmdValidateHealth(cwd, options, raw) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'backfillMilestones': {
|
||||
if (!options.backfill && !options.repair) break;
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
let backfilled = 0;
|
||||
for (const ver of missingFromRegistry) {
|
||||
try {
|
||||
const snapshotPath = path.join(milestonesArchiveDir, `${ver}-ROADMAP.md`);
|
||||
const snapshot = fs.existsSync(snapshotPath) ? fs.readFileSync(snapshotPath, 'utf-8') : null;
|
||||
// Build minimal entry from snapshot title or version
|
||||
const titleMatch = snapshot && snapshot.match(/^#\s+(.+)$/m);
|
||||
const milestoneName = titleMatch ? titleMatch[1].replace(/^Milestone\s+/i, '').replace(/^v[\d.]+\s*/, '').trim() : ver;
|
||||
const entry = `## ${ver}${milestoneName && milestoneName !== ver ? ` ${milestoneName}` : ''} (Backfilled: ${today})\n\n**Note:** Synthesized from archive snapshot by \`/gsd-health --backfill\`. Original completion date unknown.\n\n---\n\n`;
|
||||
const milestonesContent = fs.existsSync(milestonesPath)
|
||||
? fs.readFileSync(milestonesPath, 'utf-8')
|
||||
: '';
|
||||
if (!milestonesContent.trim()) {
|
||||
fs.writeFileSync(milestonesPath, `# Milestones\n\n${entry}`, 'utf-8');
|
||||
} else {
|
||||
const headerMatch = milestonesContent.match(/^(#{1,3}\s+[^\n]*\n\n?)/);
|
||||
if (headerMatch) {
|
||||
const header = headerMatch[1];
|
||||
const rest = milestonesContent.slice(header.length);
|
||||
fs.writeFileSync(milestonesPath, header + entry + rest, 'utf-8');
|
||||
} else {
|
||||
fs.writeFileSync(milestonesPath, entry + milestonesContent, 'utf-8');
|
||||
}
|
||||
}
|
||||
backfilled++;
|
||||
} catch { /* intentionally empty — partial backfill is acceptable */ }
|
||||
}
|
||||
repairActions.push({ action: repair, success: true, detail: `Backfilled ${backfilled} milestone(s) into MILESTONES.md` });
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
repairActions.push({ action: repair, success: false, error: err.message });
|
||||
@@ -980,14 +1061,16 @@ function cmdValidateHealth(cwd, options, raw) {
|
||||
const repairableCount = errors.filter(e => e.repairable).length +
|
||||
warnings.filter(w => w.repairable).length;
|
||||
|
||||
output({
|
||||
const result = {
|
||||
status,
|
||||
errors,
|
||||
warnings,
|
||||
info,
|
||||
repairable_count: repairableCount,
|
||||
repairs_performed: repairActions.length > 0 ? repairActions : undefined,
|
||||
}, raw);
|
||||
};
|
||||
output(result, raw);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ Every workflow that spawns agents or reads significant content must follow these
|
||||
|
||||
1. **Never** read agent definition files (`agents/*.md`) -- `subagent_type` auto-loads them
|
||||
2. **Never** inline large files into subagent prompts -- tell agents to read files from disk instead
|
||||
3. **Read depth scales with context window** -- check `context_window_tokens` in `.planning/config.json`:
|
||||
3. **Read depth scales with context window** -- check `context_window` in `.planning/config.json`:
|
||||
- At < 500000 tokens (default 200k): read only frontmatter, status fields, or summaries. Never read full SUMMARY.md, VERIFICATION.md, or RESEARCH.md bodies.
|
||||
- At >= 500000 tokens (1M model): MAY read full subagent output bodies when the content is needed for inline presentation or decision-making. Still avoid unnecessary reads.
|
||||
4. **Delegate** heavy work to subagents -- the orchestrator routes, it doesn't execute
|
||||
@@ -25,7 +25,7 @@ Every workflow that spawns agents or reads significant content must follow these
|
||||
| < 500k (200k model) | Frontmatter only | Frontmatter only | Frontmatter only | Current phase only |
|
||||
| >= 500k (1M model) | Full body permitted | Full body permitted | Full body permitted | Current phase only |
|
||||
|
||||
**How to check:** Read `.planning/config.json` and inspect `context_window_tokens`. If the field is absent, treat as 200k (conservative default).
|
||||
**How to check:** Read `.planning/config.json` and inspect `context_window`. If the field is absent, treat as 200k (conservative default).
|
||||
|
||||
## Context Degradation Tiers
|
||||
|
||||
|
||||
49
get-shit-done/references/planner-chunked.md
Normal file
49
get-shit-done/references/planner-chunked.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Chunked Mode Return Formats
|
||||
|
||||
Used when `plan-phase` spawns `gsd-planner` with `CHUNKED_MODE=true` (triggered by `--chunked`
|
||||
flag or `workflow.plan_chunked: true` config). Splits the single long-lived planner Task into
|
||||
shorter-lived Tasks to bound the blast radius of Windows stdio hangs.
|
||||
|
||||
## Modes
|
||||
|
||||
### outline-only
|
||||
|
||||
Write **only** `{PHASE_DIR}/{PADDED_PHASE}-PLAN-OUTLINE.md`. Do not write any PLAN.md files.
|
||||
Return:
|
||||
|
||||
```markdown
|
||||
## OUTLINE COMPLETE
|
||||
|
||||
**Phase:** {phase-name}
|
||||
**Plans:** {N} plan(s) in {M} wave(s)
|
||||
|
||||
| Plan ID | Objective | Wave | Depends On | Requirements |
|
||||
|---------|-----------|------|-----------|-------------|
|
||||
| {padded_phase}-01 | [brief objective] | 1 | none | REQ-001, REQ-002 |
|
||||
| {padded_phase}-02 | [brief objective] | 1 | none | REQ-003 |
|
||||
```
|
||||
|
||||
The orchestrator reads this table, then spawns one single-plan Task per row.
|
||||
|
||||
### single-plan
|
||||
|
||||
Write **exactly one** `{PHASE_DIR}/{plan_id}-PLAN.md`. Do not write any other plan files.
|
||||
Return:
|
||||
|
||||
```markdown
|
||||
## PLAN COMPLETE
|
||||
|
||||
**Plan:** {plan-id}
|
||||
**Objective:** {brief}
|
||||
**File:** {PHASE_DIR}/{plan-id}-PLAN.md
|
||||
**Tasks:** {N}
|
||||
```
|
||||
|
||||
The orchestrator verifies the file exists on disk after each return, commits it, then moves
|
||||
to the next plan entry from the outline.
|
||||
|
||||
## Resume Behaviour
|
||||
|
||||
If the orchestrator detects that `PLAN-OUTLINE.md` already exists (from a prior interrupted
|
||||
run), it skips the outline-only Task and goes directly to single-plan Tasks, skipping any
|
||||
`{plan_id}-PLAN.md` files that already exist on disk.
|
||||
@@ -54,7 +54,7 @@ Configuration options for `.planning/` directory behavior.
|
||||
- User must add `.planning/` to `.gitignore`
|
||||
- Useful for: OSS contributions, client projects, keeping planning private
|
||||
|
||||
**Using gsd-tools.cjs (preferred):**
|
||||
**Using `gsd-sdk query` (preferred):**
|
||||
|
||||
```bash
|
||||
# Commit with automatic commit_docs + gitignore checks:
|
||||
|
||||
@@ -8,13 +8,13 @@ Rules that apply to ALL workflows and agents. Individual workflows may have addi
|
||||
|
||||
1. **Never** read agent definition files (`agents/*.md`) -- `subagent_type` auto-loads them. Reading agent definitions into the orchestrator wastes context for content automatically injected into subagent sessions.
|
||||
2. **Never** inline large files into subagent prompts -- tell agents to read files from disk instead. Agents have their own context windows.
|
||||
3. **Read depth scales with context window** -- check `context_window_tokens` in `.planning/config.json`. At < 500000: read only frontmatter, status fields, or summaries. At >= 500000 (1M model): full body reads permitted when content is needed for inline decisions. See `references/context-budget.md` for the complete table.
|
||||
3. **Read depth scales with context window** -- check `context_window` in `.planning/config.json`. At < 500000: read only frontmatter, status fields, or summaries. At >= 500000 (1M model): full body reads permitted when content is needed for inline decisions. See `references/context-budget.md` for the complete table.
|
||||
4. **Delegate** heavy work to subagents -- the orchestrator routes, it does not build, analyze, research, investigate, or verify.
|
||||
5. **Proactive pause warning**: If you have already consumed significant context (large file reads, multiple subagent results), warn the user: "Context budget is getting heavy. Consider checkpointing progress."
|
||||
|
||||
## File Reading Rules
|
||||
|
||||
6. **SUMMARY.md read depth scales with context window** -- at context_window_tokens < 500000: read frontmatter only from prior phase SUMMARYs. At >= 500000: full body reads permitted for direct-dependency phases. Transitive dependencies (2+ phases back) remain frontmatter-only regardless.
|
||||
6. **SUMMARY.md read depth scales with context window** -- at context_window < 500000: read frontmatter only from prior phase SUMMARYs. At >= 500000: full body reads permitted for direct-dependency phases. Transitive dependencies (2+ phases back) remain frontmatter-only regardless.
|
||||
7. **Never** read full PLAN.md files from other phases -- only current phase plans.
|
||||
8. **Never** read `.planning/logs/` files -- only the health workflow reads these.
|
||||
9. **Do not** re-read full file contents when frontmatter is sufficient -- frontmatter contains status, key_files, commits, and provides fields. Exception: at >= 500000, re-reading full body is acceptable when semantic content is needed.
|
||||
@@ -34,7 +34,7 @@ Reference: `references/questioning.md` for the full anti-pattern list.
|
||||
|
||||
## State Management Anti-Patterns
|
||||
|
||||
15. **No direct Write/Edit to STATE.md or ROADMAP.md for mutations.** Always use `gsd-tools.cjs` CLI commands (`state update`, `state advance-plan`, `roadmap update-status`) for mutations. Direct Write tool usage bypasses safe update logic and is unsafe in multi-session environments. Exception: first-time creation of STATE.md from template is allowed.
|
||||
15. **No direct Write/Edit to STATE.md or ROADMAP.md for mutations.** Always use `gsd-sdk query` for registered state/roadmap handlers (e.g. `state.update`, `state.advance-plan`, `roadmap.update-plan-progress`), or legacy `node …/gsd-tools.cjs` for CLI-only commands. Direct Write tool usage bypasses safe update logic and is unsafe in multi-session environments. Exception: first-time creation of STATE.md from template is allowed.
|
||||
|
||||
## Behavioral Rules
|
||||
|
||||
@@ -53,7 +53,7 @@ Reference: `references/questioning.md` for the full anti-pattern list.
|
||||
## GSD-Specific Rules
|
||||
|
||||
24. **Do not** check for `mode === 'auto'` or `mode === 'autonomous'` -- GSD uses `yolo` config flag. Check `yolo: true` for autonomous mode, absence or `false` for interactive mode.
|
||||
25. **Always use `gsd-tools.cjs`** (not `gsd-tools.js` or any other variant) -- GSD uses CommonJS for Node.js CLI compatibility.
|
||||
25. **Prefer `gsd-sdk query`** for orchestration when a handler exists; when shelling out to the legacy CLI, use **`gsd-tools.cjs`** (not `gsd-tools.js` or any other filename) — GSD ships the programmatic API as CommonJS for Node.js CLI compatibility.
|
||||
26. **Plan files MUST follow `{padded_phase}-{NN}-PLAN.md` pattern** (e.g., `01-01-PLAN.md`). Never use `PLAN-01.md`, `plan-01.md`, or any other variation -- gsd-tools detection depends on this exact pattern.
|
||||
27. **Do not start executing the next plan before writing the SUMMARY.md for the current plan** -- downstream plans may reference it via `@` includes.
|
||||
|
||||
|
||||
76
get-shit-done/templates/README.md
Normal file
76
get-shit-done/templates/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# GSD Canonical Artifact Registry
|
||||
|
||||
This directory contains the template files for every artifact that GSD workflows officially produce. The table below is the authoritative index: **if a `.planning/` root file is not listed here, `gsd-health` will flag it as W019** (unrecognized artifact).
|
||||
|
||||
Agents should query this file before treating a `.planning/` file as authoritative. If the file name does not appear below, it is not a canonical GSD artifact.
|
||||
|
||||
---
|
||||
|
||||
## `.planning/` Root Artifacts
|
||||
|
||||
These files live directly at `.planning/` — not inside phase subdirectories.
|
||||
|
||||
| File | Template | Produced by | Purpose |
|
||||
|------|----------|-------------|---------|
|
||||
| `PROJECT.md` | `project.md` | `/gsd-new-project` | Project identity, goals, requirements summary |
|
||||
| `ROADMAP.md` | `roadmap.md` | `/gsd-new-milestone`, `/gsd-new-project` | Phase plan with milestones and progress tracking |
|
||||
| `STATE.md` | `state.md` | `/gsd-new-project`, `/gsd-health --repair` | Current session state, active phase, last activity |
|
||||
| `REQUIREMENTS.md` | `requirements.md` | `/gsd-new-milestone` | Functional requirements with traceability |
|
||||
| `MILESTONES.md` | `milestone.md` | `/gsd-complete-milestone` | Log of completed milestones with accomplishments |
|
||||
| `BACKLOG.md` | *(inline)* | `/gsd-add-backlog` | Pending ideas and deferred work |
|
||||
| `LEARNINGS.md` | *(inline)* | `/gsd-extract-learnings`, `/gsd-execute-phase` | Phase retrospective learnings for future plans |
|
||||
| `THREADS.md` | *(inline)* | `/gsd-thread` | Persistent discussion threads |
|
||||
| `config.json` | `config.json` | `/gsd-new-project`, `/gsd-health --repair` | Project-specific GSD configuration |
|
||||
| `CLAUDE.md` | `claude-md.md` | `/gsd-profile` | Auto-assembled Claude Code context file |
|
||||
|
||||
### Version-stamped artifacts (pattern: `vX.Y-*.md`)
|
||||
|
||||
| Pattern | Produced by | Purpose |
|
||||
|---------|-------------|---------|
|
||||
| `vX.Y-MILESTONE-AUDIT.md` | `/gsd-audit-milestone` | Milestone audit report before archiving |
|
||||
|
||||
These files are archived to `.planning/milestones/` by `/gsd-complete-milestone`. Finding them at the `.planning/` root after completion indicates the archive step was skipped.
|
||||
|
||||
---
|
||||
|
||||
## Phase Subdirectory Artifacts (`.planning/phases/NN-name/`)
|
||||
|
||||
These files live inside a phase directory. They are NOT checked by W019 (which only inspects the `.planning/` root).
|
||||
|
||||
| File Pattern | Template | Produced by | Purpose |
|
||||
|-------------|----------|-------------|---------|
|
||||
| `NN-MM-PLAN.md` | `phase-prompt.md` | `/gsd-plan-phase` | Executable implementation plan |
|
||||
| `NN-MM-SUMMARY.md` | `summary.md` | `/gsd-execute-phase` | Post-execution summary with learnings |
|
||||
| `NN-CONTEXT.md` | `context.md` | `/gsd-discuss-phase` | Scoped discussion decisions for the phase |
|
||||
| `NN-RESEARCH.md` | `research.md` | `/gsd-research-phase`, `/gsd-plan-phase` | Technical research for the phase |
|
||||
| `NN-VALIDATION.md` | `VALIDATION.md` | `/gsd-research-phase` (Nyquist) | Validation architecture (Nyquist method) |
|
||||
| `NN-UAT.md` | `UAT.md` | `/gsd-validate-phase` | User acceptance test results |
|
||||
| `NN-PATTERNS.md` | *(inline)* | `/gsd-plan-phase` (pattern mapper) | Analog file mapping for the phase |
|
||||
| `NN-UI-SPEC.md` | `UI-SPEC.md` | `/gsd-ui-phase` | UI design contract |
|
||||
| `NN-SECURITY.md` | `SECURITY.md` | `/gsd-secure-phase` | Security threat model |
|
||||
| `NN-AI-SPEC.md` | `AI-SPEC.md` | `/gsd-ai-integration-phase` | AI integration spec with eval strategy |
|
||||
| `NN-DEBUG.md` | `DEBUG.md` | `/gsd-debug` | Debug session log |
|
||||
| `NN-REVIEWS.md` | *(inline)* | `/gsd-review` | Cross-AI review feedback |
|
||||
|
||||
---
|
||||
|
||||
## Milestone Archive (`.planning/milestones/`)
|
||||
|
||||
Files archived by `/gsd-complete-milestone`. These are never checked by W019.
|
||||
|
||||
| File Pattern | Source |
|
||||
|-------------|--------|
|
||||
| `vX.Y-ROADMAP.md` | Snapshot of ROADMAP.md at milestone close |
|
||||
| `vX.Y-REQUIREMENTS.md` | Snapshot of REQUIREMENTS.md at milestone close |
|
||||
| `vX.Y-MILESTONE-AUDIT.md` | Moved from `.planning/` root |
|
||||
| `vX.Y-phases/` | Archived phase directories (if `--archive-phases` used) |
|
||||
|
||||
---
|
||||
|
||||
## Adding a New Canonical Artifact
|
||||
|
||||
When a new workflow produces a `.planning/` root file:
|
||||
|
||||
1. Add the file name to `CANONICAL_EXACT` in `get-shit-done/bin/lib/artifacts.cjs`
|
||||
2. Add a row to the **`.planning/` Root Artifacts** table above
|
||||
3. Add the template to `get-shit-done/templates/` if one exists
|
||||
@@ -103,7 +103,25 @@ Task(
|
||||
|
||||
**b. Run tests:**
|
||||
```bash
|
||||
npm test 2>&1 | tail -20
|
||||
AUDIT_TEST_CMD=$(gsd-sdk query config-get workflow.test_command --default "" 2>/dev/null || true)
|
||||
if [ -z "$AUDIT_TEST_CMD" ]; then
|
||||
if [ -f "Makefile" ] && grep -q "^test:" Makefile; then
|
||||
AUDIT_TEST_CMD="make test"
|
||||
elif [ -f "Justfile" ] || [ -f "justfile" ]; then
|
||||
AUDIT_TEST_CMD="just test"
|
||||
elif [ -f "package.json" ]; then
|
||||
AUDIT_TEST_CMD="npm test"
|
||||
elif [ -f "Cargo.toml" ]; then
|
||||
AUDIT_TEST_CMD="cargo test"
|
||||
elif [ -f "go.mod" ]; then
|
||||
AUDIT_TEST_CMD="go test ./..."
|
||||
elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then
|
||||
AUDIT_TEST_CMD="python -m pytest -x -q --tb=short"
|
||||
else
|
||||
AUDIT_TEST_CMD="true"
|
||||
fi
|
||||
fi
|
||||
eval "$AUDIT_TEST_CMD" 2>&1 | tail -20
|
||||
```
|
||||
|
||||
**c. If tests pass** — commit atomically:
|
||||
|
||||
@@ -40,10 +40,8 @@ When a milestone completes:
|
||||
<step name="pre_close_artifact_audit">
|
||||
Before proceeding with milestone close, run the comprehensive open artifact audit.
|
||||
|
||||
`audit-open` is not registered on `gsd-sdk query` yet; use the installed CJS CLI:
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" audit-open 2>/dev/null
|
||||
gsd-sdk query audit-open 2>/dev/null
|
||||
```
|
||||
|
||||
If the output contains open items (any section with count > 0):
|
||||
@@ -59,7 +57,7 @@ These items are open. Choose an action:
|
||||
```
|
||||
|
||||
If user chooses [A] (Acknowledge):
|
||||
1. Re-run `audit-open --json` to get structured data
|
||||
1. Re-run `gsd-sdk query audit-open --json` to get structured data
|
||||
2. Write acknowledged items to STATE.md under `## Deferred Items` section:
|
||||
```markdown
|
||||
## Deferred Items
|
||||
@@ -78,7 +76,7 @@ If user chooses [A] (Acknowledge):
|
||||
|
||||
If output shows all clear (no open items): print `All artifact types clear.` and proceed.
|
||||
|
||||
SECURITY: Audit JSON output is structured data from `audit-open` (gsd-tools.cjs) — validated and sanitized at source. When writing to STATE.md, item slugs and descriptions are sanitized via `sanitizeForDisplay()` before inclusion. Never inject raw user-supplied content into STATE.md without sanitization.
|
||||
SECURITY: Audit JSON output is structured data from the `audit-open` query handler (same JSON contract as legacy `gsd-tools.cjs audit-open`) — validated and sanitized at source. When writing to STATE.md, item slugs and descriptions are sanitized via `sanitizeForDisplay()` before inclusion. Never inject raw user-supplied content into STATE.md without sanitization.
|
||||
</step>
|
||||
|
||||
<step name="verify_readiness">
|
||||
|
||||
@@ -622,21 +622,20 @@ Check for auto-advance trigger:
|
||||
gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null
|
||||
fi
|
||||
```
|
||||
3. Read chain flag and user preference:
|
||||
3. Read consolidated auto-mode (`active` = chain flag OR user preference):
|
||||
```bash
|
||||
AUTO_CHAIN=$(gsd-sdk query config-get workflow._auto_chain_active 2>/dev/null || echo "false")
|
||||
AUTO_CFG=$(gsd-sdk query config-get workflow.auto_advance 2>/dev/null || echo "false")
|
||||
AUTO_MODE=$(gsd-sdk query check auto-mode --pick active 2>/dev/null || echo "false")
|
||||
```
|
||||
|
||||
**If `--auto` flag present AND `AUTO_CHAIN` is not true:**
|
||||
**If `--auto` flag present AND `AUTO_MODE` is not true:**
|
||||
```bash
|
||||
gsd-sdk query config-set workflow._auto_chain_active true
|
||||
```
|
||||
|
||||
**If `--auto` flag present OR `AUTO_CHAIN` is true OR `AUTO_CFG` is true:**
|
||||
**If `--auto` flag present OR `AUTO_MODE` is true:**
|
||||
|
||||
Display banner:
|
||||
```
|
||||
```text
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTO-ADVANCING TO PLAN
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
@@ -1223,18 +1223,17 @@ Check for auto-advance trigger:
|
||||
gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null
|
||||
fi
|
||||
```
|
||||
3. Read both the chain flag and user preference:
|
||||
3. Read consolidated auto-mode (`active` = chain flag OR user preference):
|
||||
```bash
|
||||
AUTO_CHAIN=$(gsd-sdk query config-get workflow._auto_chain_active 2>/dev/null || echo "false")
|
||||
AUTO_CFG=$(gsd-sdk query config-get workflow.auto_advance 2>/dev/null || echo "false")
|
||||
AUTO_MODE=$(gsd-sdk query check auto-mode --pick active 2>/dev/null || echo "false")
|
||||
```
|
||||
|
||||
**If `--auto` or `--chain` flag present AND `AUTO_CHAIN` is not true:** Persist chain flag to config (handles direct usage without new-project):
|
||||
**If `--auto` or `--chain` flag present AND `AUTO_MODE` is not true:** Persist chain flag to config (handles direct usage without new-project):
|
||||
```bash
|
||||
gsd-sdk query config-set workflow._auto_chain_active true
|
||||
```
|
||||
|
||||
**If `--auto` flag present OR `--chain` flag present OR `AUTO_CHAIN` is true OR `AUTO_CFG` is true:**
|
||||
**If `--auto` flag present OR `--chain` flag present OR `AUTO_MODE` is true:**
|
||||
|
||||
Display banner:
|
||||
```
|
||||
|
||||
@@ -74,6 +74,8 @@ AGENT_SKILLS=$(gsd-sdk query agent-skills gsd-executor 2>/dev/null)
|
||||
|
||||
Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`, `phase_req_ids`, `response_language`.
|
||||
|
||||
**Model resolution:** If `executor_model` is `"inherit"`, omit the `model=` parameter from all `Task()` calls — do NOT pass `model="inherit"` to Task. Omitting the `model=` parameter causes Claude Code to inherit the current orchestrator model automatically. Only set `model=` when `executor_model` is an explicit model name (e.g., `"claude-sonnet-4-6"`, `"claude-opus-4-7"`).
|
||||
|
||||
**If `response_language` is set:** Include `response_language: {value}` in all spawned subagent prompts so any user-facing output stays in the configured language.
|
||||
|
||||
Read worktree config:
|
||||
@@ -421,7 +423,10 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
|
||||
Task(
|
||||
subagent_type="gsd-executor",
|
||||
description="Execute plan {plan_number} of phase {phase_number}",
|
||||
model="{executor_model}",
|
||||
# Only include model= when executor_model is an explicit model name.
|
||||
# When executor_model is "inherit", omit this parameter entirely so
|
||||
# Claude Code inherits the orchestrator model automatically.
|
||||
model="{executor_model}", # omit this line when executor_model == "inherit"
|
||||
isolation="worktree",
|
||||
prompt="
|
||||
<objective>
|
||||
@@ -623,6 +628,21 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
|
||||
break
|
||||
}
|
||||
|
||||
# Post-merge deletion audit: detect bulk file deletions in merge commit (#2384)
|
||||
# --diff-filter=D HEAD~1 HEAD shows files deleted by the merge commit itself.
|
||||
# Exclude .planning/ — orchestrator-owned deletions there are expected (resurrections
|
||||
# are handled below). Require ALLOW_BULK_DELETE=1 to bypass for intentional large refactors.
|
||||
MERGE_DEL_COUNT=$(git diff --diff-filter=D --name-only HEAD~1 HEAD 2>/dev/null | grep -vc '^\.planning/' || true)
|
||||
if [ "$MERGE_DEL_COUNT" -gt 5 ] && [ "${ALLOW_BULK_DELETE:-0}" != "1" ]; then
|
||||
MERGE_DELETIONS=$(git diff --diff-filter=D --name-only HEAD~1 HEAD 2>/dev/null | grep -v '^\.planning/' || true)
|
||||
echo "⚠ BLOCKED: Merge of $WT_BRANCH deleted $MERGE_DEL_COUNT files outside .planning/ — reverting to protect repository integrity (#2384)"
|
||||
echo "$MERGE_DELETIONS"
|
||||
echo " If these deletions are intentional, re-run with ALLOW_BULK_DELETE=1"
|
||||
git reset --hard HEAD~1 2>/dev/null || true
|
||||
rm -f "$STATE_BACKUP" "$ROADMAP_BACKUP"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Restore orchestrator-owned files (main always wins)
|
||||
if [ -s "$STATE_BACKUP" ]; then
|
||||
cp "$STATE_BACKUP" .planning/STATE.md
|
||||
@@ -634,10 +654,15 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
|
||||
|
||||
# Detect files deleted on main but re-added by worktree merge
|
||||
# (e.g., archived phase directories that were intentionally removed)
|
||||
# A "resurrected" file must have a deletion event in main's ancestry —
|
||||
# brand-new files (e.g. SUMMARY.md just created by the executor) have no
|
||||
# such history and must NOT be removed (#2501).
|
||||
DELETED_FILES=$(git diff --diff-filter=A --name-only HEAD~1 -- .planning/ 2>/dev/null || true)
|
||||
for RESURRECTED in $DELETED_FILES; do
|
||||
# Check if this file was NOT in main's pre-merge tree
|
||||
if ! echo "$PRE_MERGE_FILES" | grep -qxF "$RESURRECTED"; then
|
||||
# Only delete if this file was previously tracked on main and then
|
||||
# deliberately removed (has a deletion event in git history).
|
||||
WAS_DELETED=$(git log --follow --diff-filter=D --name-only --format="" HEAD~1 -- "$RESURRECTED" 2>/dev/null | grep -c . || true)
|
||||
if [ "${WAS_DELETED:-0}" -gt 0 ]; then
|
||||
git rm -f "$RESURRECTED" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
@@ -665,7 +690,19 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
|
||||
fi
|
||||
|
||||
# Remove the worktree
|
||||
git worktree remove "$WT" --force 2>/dev/null || true
|
||||
if ! git worktree remove "$WT" --force; then
|
||||
WT_NAME=$(basename "$WT")
|
||||
if [ -f ".git/worktrees/${WT_NAME}/locked" ]; then
|
||||
echo "⚠ Worktree $WT is locked — attempting to unlock and retry"
|
||||
git worktree unlock "$WT" 2>/dev/null || true
|
||||
if ! git worktree remove "$WT" --force; then
|
||||
echo "⚠ Residual worktree at $WT — manual cleanup required after session exits:"
|
||||
echo " git worktree unlock \"$WT\" && git worktree remove \"$WT\" --force && git branch -D \"$WT_BRANCH\""
|
||||
fi
|
||||
else
|
||||
echo "⚠ Residual worktree at $WT (remove failed) — investigate manually"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Delete the temporary branch
|
||||
git branch -D "$WT_BRANCH" 2>/dev/null || true
|
||||
@@ -688,22 +725,29 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
|
||||
merging their work creates failures.
|
||||
|
||||
```bash
|
||||
# Resolve test command: project config > Makefile > language sniff
|
||||
TEST_CMD=$(gsd-sdk query config-get workflow.test_command --default "" 2>/dev/null || true)
|
||||
if [ -z "$TEST_CMD" ]; then
|
||||
if [ -f "Makefile" ] && grep -q "^test:" Makefile; then
|
||||
TEST_CMD="make test"
|
||||
elif [ -f "Justfile" ] || [ -f "justfile" ]; then
|
||||
TEST_CMD="just test"
|
||||
elif [ -f "package.json" ]; then
|
||||
TEST_CMD="npm test"
|
||||
elif [ -f "Cargo.toml" ]; then
|
||||
TEST_CMD="cargo test"
|
||||
elif [ -f "go.mod" ]; then
|
||||
TEST_CMD="go test ./..."
|
||||
elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then
|
||||
TEST_CMD="python -m pytest -x -q --tb=short 2>&1 || uv run python -m pytest -x -q --tb=short"
|
||||
else
|
||||
TEST_CMD="true"
|
||||
echo "⚠ No test runner detected — skipping post-merge test gate"
|
||||
fi
|
||||
fi
|
||||
# Detect test runner and run quick smoke test (timeout: 5 minutes)
|
||||
TEST_EXIT=0
|
||||
timeout 300 bash -c '
|
||||
if [ -f "package.json" ]; then
|
||||
npm test 2>&1
|
||||
elif [ -f "Cargo.toml" ]; then
|
||||
cargo test 2>&1
|
||||
elif [ -f "go.mod" ]; then
|
||||
go test ./... 2>&1
|
||||
elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then
|
||||
python -m pytest -x -q --tb=short 2>&1 || uv run python -m pytest -x -q --tb=short 2>&1
|
||||
else
|
||||
echo "⚠ No test runner detected — skipping post-merge test gate"
|
||||
exit 0
|
||||
fi
|
||||
'
|
||||
timeout 300 bash -c "$TEST_CMD" 2>&1
|
||||
TEST_EXIT=$?
|
||||
if [ "${TEST_EXIT}" -eq 0 ]; then
|
||||
echo "✓ Post-merge test gate passed — no cross-plan conflicts"
|
||||
@@ -846,13 +890,12 @@ Plans with `autonomous: false` require user interaction.
|
||||
|
||||
**Auto-mode checkpoint handling:**
|
||||
|
||||
Read auto-advance config (chain flag + user preference):
|
||||
Read auto-advance config (chain flag OR user preference — same boolean as `check.auto-mode`):
|
||||
```bash
|
||||
AUTO_CHAIN=$(gsd-sdk query config-get workflow._auto_chain_active 2>/dev/null || echo "false")
|
||||
AUTO_CFG=$(gsd-sdk query config-get workflow.auto_advance 2>/dev/null || echo "false")
|
||||
AUTO_MODE=$(gsd-sdk query check auto-mode --pick active 2>/dev/null || echo "false")
|
||||
```
|
||||
|
||||
When executor returns a checkpoint AND (`AUTO_CHAIN` is `"true"` OR `AUTO_CFG` is `"true"`):
|
||||
When executor returns a checkpoint AND `AUTO_MODE` is `true`:
|
||||
- **human-verify** → Auto-spawn continuation agent with `{user_response}` = `"approved"`. Log `⚡ Auto-approved checkpoint`.
|
||||
- **decision** → Auto-spawn continuation agent with `{user_response}` = first option from checkpoint details. Log `⚡ Auto-selected: [option]`.
|
||||
- **human-action** → Present to user (existing behavior below). Auth gates cannot be automated.
|
||||
@@ -1111,16 +1154,27 @@ Collect all unique test file paths into `REGRESSION_FILES`.
|
||||
**Step 3: Run regression tests (if any found)**
|
||||
|
||||
```bash
|
||||
# Detect test runner and run prior phase tests
|
||||
if [ -f "package.json" ]; then
|
||||
npm test 2>&1
|
||||
elif [ -f "Cargo.toml" ]; then
|
||||
cargo test 2>&1
|
||||
elif [ -f "go.mod" ]; then
|
||||
go test ./... 2>&1
|
||||
elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
|
||||
python -m pytest ${REGRESSION_FILES} -q --tb=short 2>&1
|
||||
# Resolve test command: project config > Makefile > language sniff
|
||||
REG_TEST_CMD=$(gsd-sdk query config-get workflow.test_command --default "" 2>/dev/null || true)
|
||||
if [ -z "$REG_TEST_CMD" ]; then
|
||||
if [ -f "Makefile" ] && grep -q "^test:" Makefile; then
|
||||
REG_TEST_CMD="make test"
|
||||
elif [ -f "Justfile" ] || [ -f "justfile" ]; then
|
||||
REG_TEST_CMD="just test"
|
||||
elif [ -f "package.json" ]; then
|
||||
REG_TEST_CMD="npm test"
|
||||
elif [ -f "Cargo.toml" ]; then
|
||||
REG_TEST_CMD="cargo test"
|
||||
elif [ -f "go.mod" ]; then
|
||||
REG_TEST_CMD="go test ./..."
|
||||
elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
|
||||
REG_TEST_CMD="python -m pytest ${REGRESSION_FILES} -q --tb=short"
|
||||
else
|
||||
REG_TEST_CMD="true"
|
||||
fi
|
||||
fi
|
||||
# Detect test runner and run prior phase tests
|
||||
eval "$REG_TEST_CMD" 2>&1
|
||||
```
|
||||
|
||||
**Step 4: Report results**
|
||||
@@ -1402,6 +1456,38 @@ gsd-sdk query learnings.copy 2>/dev/null || echo "⚠ Learnings copy failed —
|
||||
Copy failure must NOT block phase completion.
|
||||
</step>
|
||||
|
||||
<step name="close_phase_todos">
|
||||
**Auto-close pending todos tagged for this phase (#2433).**
|
||||
|
||||
This step runs AFTER `update_roadmap` marks the phase complete. It moves any pending todos that carry `resolves_phase: <current-phase-number>` to the completed directory.
|
||||
|
||||
```bash
|
||||
PHASE_NUM="${PHASE_NUMBER}"
|
||||
PENDING_DIR=".planning/todos/pending"
|
||||
COMPLETED_DIR=".planning/todos/completed"
|
||||
mkdir -p "$COMPLETED_DIR"
|
||||
|
||||
CLOSED=()
|
||||
for TODO_FILE in "$PENDING_DIR"/*.md; do
|
||||
[ -f "$TODO_FILE" ] || continue
|
||||
# Extract resolves_phase from YAML frontmatter (first --- block only)
|
||||
RP=$(awk '/^---/{c++;next} c==1 && /^resolves_phase:/{print $2;exit} c==2{exit}' "$TODO_FILE" 2>/dev/null || true)
|
||||
if [ "$RP" = "$PHASE_NUM" ] || [ "$RP" = "\"$PHASE_NUM\"" ]; then
|
||||
mv "$TODO_FILE" "$COMPLETED_DIR/"
|
||||
CLOSED+=("$(basename "$TODO_FILE")")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#CLOSED[@]} -gt 0 ]; then
|
||||
gsd-sdk query commit "docs(phase-${PHASE_NUMBER}): auto-close ${#CLOSED[@]} todo(s) resolved by this phase" .planning/todos/completed/ .planning/STATE.md || true
|
||||
echo "◆ Closed ${#CLOSED[@]} todo(s) resolved by Phase ${PHASE_NUMBER}:"
|
||||
for f in "${CLOSED[@]}"; do echo " ✓ $f"; done
|
||||
fi
|
||||
```
|
||||
|
||||
**If no todos have `resolves_phase: <this-phase>`:** Skip silently — this step is always additive and never blocks phase completion.
|
||||
</step>
|
||||
|
||||
<step name="update_project_md">
|
||||
**Evolve PROJECT.md to reflect phase completion (prevents planning document drift — #956):**
|
||||
|
||||
@@ -1454,13 +1540,12 @@ STOP. Do not proceed to auto-advance or transition.
|
||||
**Auto-advance detection:**
|
||||
|
||||
1. Parse `--auto` flag from $ARGUMENTS
|
||||
2. Read both the chain flag and user preference (chain flag already synced in init step):
|
||||
2. Read consolidated auto-mode (`active` = chain flag OR user preference; chain flag already synced in init step):
|
||||
```bash
|
||||
AUTO_CHAIN=$(gsd-sdk query config-get workflow._auto_chain_active 2>/dev/null || echo "false")
|
||||
AUTO_CFG=$(gsd-sdk query config-get workflow.auto_advance 2>/dev/null || echo "false")
|
||||
AUTO_MODE=$(gsd-sdk query check auto-mode --pick active 2>/dev/null || echo "false")
|
||||
```
|
||||
|
||||
**If `--auto` flag present OR `AUTO_CHAIN` is true OR `AUTO_CFG` is true (AND verification passed with no gaps):**
|
||||
**If `--auto` flag present OR `AUTO_MODE` is true (AND verification passed with no gaps):**
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════╗
|
||||
@@ -1473,7 +1558,7 @@ Execute the transition workflow inline (do NOT use Task — orchestrator context
|
||||
|
||||
Read and follow `~/.claude/get-shit-done/workflows/transition.md`, passing through the `--auto` flag so it propagates to the next phase invocation.
|
||||
|
||||
**If none of `--auto`, `AUTO_CHAIN`, or `AUTO_CFG` is true:**
|
||||
**If neither `--auto` nor `AUTO_MODE` is true:**
|
||||
|
||||
**STOP. Do not auto-advance. Do not execute transition. Do not plan next phase. Present options to the user and wait.**
|
||||
|
||||
|
||||
@@ -135,6 +135,12 @@ missing_artifacts:
|
||||
---
|
||||
```
|
||||
|
||||
Individual items may carry an optional `graduated:` annotation (added by `graduation.md` when a cluster is promoted):
|
||||
```markdown
|
||||
**Graduated:** {target-file}:{ISO_DATE}
|
||||
```
|
||||
This annotation is appended after the item's existing fields and prevents the item from being re-surfaced in future graduation scans. Do not add this field during extraction — it is written only by the graduation workflow.
|
||||
|
||||
The body follows this structure:
|
||||
```markdown
|
||||
# Phase {PHASE_NUMBER} Learnings: {PHASE_NAME}
|
||||
|
||||
195
get-shit-done/workflows/graduation.md
Normal file
195
get-shit-done/workflows/graduation.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# graduation.md — LEARNINGS.md Cross-Phase Graduation Helper
|
||||
|
||||
**Invoked by:** `transition.md` step `graduation_scan`. Never invoked directly by users.
|
||||
|
||||
This workflow clusters recurring items across the last N phases' LEARNINGS.md files and surfaces promotion candidates to the developer via HITL. No item is promoted without explicit developer approval.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Read from project config (`config.json`):
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `features.graduation` | `true` | Master on/off switch. `false` skips silently. |
|
||||
| `features.graduation_window` | `5` | How many prior phases to scan |
|
||||
| `features.graduation_threshold` | `3` | Minimum cluster size to surface |
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Guard Checks
|
||||
|
||||
```bash
|
||||
GRADUATION_ENABLED=$(gsd-sdk query config-get features.graduation 2>/dev/null || echo "true")
|
||||
GRADUATION_WINDOW=$(gsd-sdk query config-get features.graduation_window 2>/dev/null || echo "5")
|
||||
GRADUATION_THRESHOLD=$(gsd-sdk query config-get features.graduation_threshold 2>/dev/null || echo "3")
|
||||
```
|
||||
|
||||
**Skip silently (print nothing) if:**
|
||||
- `features.graduation` is `false`
|
||||
- Fewer than `graduation_threshold` completed prior phases exist (not enough data)
|
||||
|
||||
**Skip silently (print nothing) if total items across all LEARNINGS.md files in the window is fewer than 5.**
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Collect LEARNINGS.md Files
|
||||
|
||||
Find LEARNINGS.md files from the last N completed phases (excluding the phase currently completing):
|
||||
|
||||
```bash
|
||||
find .planning/phases -name "*-LEARNINGS.md" | sort | tail -n "$GRADUATION_WINDOW"
|
||||
```
|
||||
|
||||
For each file found:
|
||||
1. Parse the four category sections: `## Decisions`, `## Lessons`, `## Patterns`, `## Surprises`
|
||||
2. Extract each `### Item Title` + body as a single item record: `{ category, title, body, source_phase, source_file }`
|
||||
3. **Skip items that already contain `**Graduated:**`** — they have been promoted and must not re-surface
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Cluster by Lexical Similarity
|
||||
|
||||
For each category independently, cluster items using Jaccard similarity on tokenized title+body:
|
||||
|
||||
**Tokenization:** lowercase, strip punctuation, split on whitespace, remove stop words (a, an, the, is, was, in, on, at, to, for, of, and, or, but, with, from, that, this, by, as).
|
||||
|
||||
**Jaccard similarity:** `|A ∩ B| / |A ∪ B|` where A and B are token sets. Two items are in the same cluster if similarity ≥ 0.25.
|
||||
|
||||
**Clustering algorithm:** single-pass greedy — process items in phase order; add to the first cluster whose centroid (union of all cluster tokens) has similarity ≥ 0.25 with the new item; otherwise start a new cluster.
|
||||
|
||||
**Cluster size filter:** only surface clusters with distinct source phases ≥ `graduation_threshold` (not just total items — same item repeated in one phase still counts as 1 distinct phase).
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Check graduation_backlog in STATE.md
|
||||
|
||||
Read `.planning/STATE.md` `graduation_backlog` section (if present). Format:
|
||||
|
||||
```yaml
|
||||
graduation_backlog:
|
||||
- cluster_id: "{sha256-of-cluster-title}"
|
||||
status: "dismissed" # or "deferred"
|
||||
deferred_until: "phase-N" # only for deferred entries
|
||||
cluster_title: "{representative title}"
|
||||
```
|
||||
|
||||
**Skip any cluster whose `cluster_id` matches a `dismissed` entry.**
|
||||
|
||||
**Skip any cluster whose `cluster_id` matches a `deferred` entry where `deferred_until` phase has not yet completed.**
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Surface Promotion Candidates
|
||||
|
||||
For each qualifying cluster, determine the suggested target file:
|
||||
|
||||
| Category | Suggested Target |
|
||||
|----------|-----------------|
|
||||
| `decisions` | `PROJECT.md` — append under `## Validated Decisions` (create section if absent) |
|
||||
| `patterns` | `PATTERNS.md` — append under the appropriate category section (create file if absent) |
|
||||
| `lessons` | `PROJECT.md` — append under `## Invariants` (create section if absent) |
|
||||
| `surprises` | Flag for human review — if genuinely surprising 3+ times, something structural is wrong |
|
||||
|
||||
Print the graduation report:
|
||||
|
||||
```text
|
||||
📚 Graduation scan across phases {M}–{N}:
|
||||
|
||||
HIGH RECURRENCE ({K}/{WINDOW} phases)
|
||||
├─ Cluster: "{representative title}"
|
||||
├─ Category: {category}
|
||||
├─ Sources: {list of NN-LEARNINGS filenames}
|
||||
└─ Suggested target: {target file} § {section}
|
||||
|
||||
[repeat for each qualifying cluster, ordered HIGH→LOW recurrence]
|
||||
|
||||
For each cluster above, choose an action:
|
||||
P = Promote now D = Defer (re-surface next transition) X = Dismiss (never re-surface) A = Defer all remaining
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 6: HITL — Process Each Cluster
|
||||
|
||||
For each cluster (in order from Step 5), ask the developer:
|
||||
|
||||
```text
|
||||
Cluster: "{title}" [{category}, {K} phases] → {target}
|
||||
Action [P/D/X/A]:
|
||||
```
|
||||
|
||||
Use `AskUserQuestion` (or equivalent HITL primitive for the current runtime). If `TEXT_MODE` is true, display the cluster question as plain text and accept typed input. Accept single-character input: `P`, `D`, `X`, `A` (case-insensitive).
|
||||
|
||||
**On `P` (Promote now):**
|
||||
|
||||
1. Read the target file (or create it with a standard header if absent)
|
||||
2. Append the cluster entry under the suggested section:
|
||||
```markdown
|
||||
### {Cluster representative title}
|
||||
{Merged body — combine unique sentences across cluster items}
|
||||
|
||||
**Sources:** Phase {A}, Phase {B}, Phase {C}
|
||||
**Promoted:** {ISO_DATE}
|
||||
```
|
||||
3. For each source LEARNINGS.md item in the cluster, append `**Graduated:** {target-file}:{ISO_DATE}` after its last existing field
|
||||
4. Commit both the target file and all annotated LEARNINGS.md files in a single atomic commit:
|
||||
`docs(learnings): graduate "{cluster title}" to {target-file}`
|
||||
|
||||
**On `D` (Defer):**
|
||||
|
||||
Write to `.planning/STATE.md` under `graduation_backlog`:
|
||||
```yaml
|
||||
- cluster_id: "{sha256}"
|
||||
status: "deferred"
|
||||
deferred_until: "phase-{NEXT_PHASE_NUMBER}"
|
||||
cluster_title: "{title}"
|
||||
```
|
||||
|
||||
**On `X` (Dismiss):**
|
||||
|
||||
Write to `.planning/STATE.md` under `graduation_backlog`:
|
||||
```yaml
|
||||
- cluster_id: "{sha256}"
|
||||
status: "dismissed"
|
||||
cluster_title: "{title}"
|
||||
```
|
||||
|
||||
**On `A` (Defer all):**
|
||||
|
||||
Defer the current cluster (same as `D`) and skip all remaining clusters for this run, deferring each to the next transition. Print:
|
||||
```text
|
||||
[graduation: deferred all remaining clusters to next transition]
|
||||
```
|
||||
Then proceed directly to Step 7.
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Completion Report
|
||||
|
||||
After processing all clusters, print:
|
||||
|
||||
```text
|
||||
Graduation complete: {promoted} promoted, {deferred} deferred, {dismissed} dismissed.
|
||||
```
|
||||
|
||||
If no clusters qualified (all filtered by backlog or threshold), print:
|
||||
```text
|
||||
[graduation: no qualifying clusters in phases {M}–{N}]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## First-Run Behaviour
|
||||
|
||||
On the first transition after upgrading to a version that includes this workflow, all extant LEARNINGS.md files may produce a large batch of candidates at once. A `[Defer all]` shorthand is available: if the developer enters `A` at any cluster prompt, all remaining clusters for this run are deferred to the next transition.
|
||||
|
||||
---
|
||||
|
||||
## No-Op Conditions (silent skip)
|
||||
|
||||
- `features.graduation = false`
|
||||
- Fewer than `graduation_threshold` prior phases with LEARNINGS.md
|
||||
- Total items < 5 across the window
|
||||
- All qualifying clusters are in `graduation_backlog` as dismissed
|
||||
@@ -11,13 +11,17 @@ Read all files referenced by the invoking prompt's execution_context before star
|
||||
<step name="parse_args">
|
||||
**Parse arguments:**
|
||||
|
||||
Check if `--repair` flag is present in the command arguments.
|
||||
Check if `--repair` or `--backfill` flags are present in the command arguments.
|
||||
|
||||
```
|
||||
REPAIR_FLAG=""
|
||||
BACKFILL_FLAG=""
|
||||
if arguments contain "--repair"; then
|
||||
REPAIR_FLAG="--repair"
|
||||
fi
|
||||
if arguments contain "--backfill"; then
|
||||
BACKFILL_FLAG="--backfill"
|
||||
fi
|
||||
```
|
||||
</step>
|
||||
|
||||
@@ -25,7 +29,7 @@ fi
|
||||
**Run health validation:**
|
||||
|
||||
```bash
|
||||
gsd-sdk query validate.health $REPAIR_FLAG
|
||||
gsd-sdk query validate.health $REPAIR_FLAG $BACKFILL_FLAG
|
||||
```
|
||||
|
||||
Parse JSON output:
|
||||
@@ -138,6 +142,8 @@ Report final status.
|
||||
| W007 | warning | Phase on disk but not in ROADMAP | No |
|
||||
| W008 | warning | config.json: workflow.nyquist_validation absent (defaults to enabled but agents may skip) | Yes |
|
||||
| W009 | warning | Phase has Validation Architecture in RESEARCH.md but no VALIDATION.md | No |
|
||||
| W018 | warning | MILESTONES.md missing entry for archived milestone snapshot | Yes (`--backfill`) |
|
||||
| W019 | warning | Unrecognized .planning/ root file — not a canonical GSD artifact | No |
|
||||
| I001 | info | Plan without SUMMARY (may be in progress) | No |
|
||||
|
||||
</error_codes>
|
||||
@@ -150,6 +156,7 @@ Report final status.
|
||||
| resetConfig | Delete + recreate config.json | Loses custom settings |
|
||||
| regenerateState | Create STATE.md from ROADMAP structure when it is missing | Loses session history |
|
||||
| addNyquistKey | Add workflow.nyquist_validation: true to config.json | None — matches existing default |
|
||||
| backfillMilestones | Synthesize missing MILESTONES.md entries from `.planning/milestones/vX.Y-ROADMAP.md` snapshots | None — additive only; triggered by `--backfill` flag |
|
||||
|
||||
**Not repairable (too risky):**
|
||||
- PROJECT.md, ROADMAP.md content
|
||||
|
||||
@@ -41,6 +41,8 @@ if [ -n "{MANIFEST_PATH}" ]; then
|
||||
fi
|
||||
```
|
||||
|
||||
**Containment (required):** After resolving `SCAN_PATH` and `MANIFEST_PATH` relative to the repo root, canonicalize each with `realpath` (or platform equivalent) and assert the result is under `realpath("$REPO_ROOT")`. Reject absolute paths outside the repo (e.g. `/tmp`, `C:\Windows`) even when they do not contain `..`.
|
||||
|
||||
If `PATH_NOT_FOUND` or `MANIFEST_NOT_FOUND`: display error and exit.
|
||||
|
||||
</step>
|
||||
|
||||
@@ -66,7 +66,11 @@ Extract from result: `phase_number`, `after_phase`, `name`, `slug`, `directory`.
|
||||
Update STATE.md to reflect the inserted phase:
|
||||
|
||||
1. Read `.planning/STATE.md`
|
||||
2. Under "## Accumulated Context" → "### Roadmap Evolution" add entry:
|
||||
2. Update STATE.md's next-phase pointers to the newly inserted phase `{decimal_phase}`:
|
||||
- Update structured field(s) used by tooling (e.g. `current_phase:`) to `{decimal_phase}`.
|
||||
- Update human-readable recommendation text (e.g. `## Current Phase`, `Next recommended run:`) to `{decimal_phase}`.
|
||||
- If multiple pointer locations exist, update all of them in the same edit.
|
||||
3. Under "## Accumulated Context" → "### Roadmap Evolution" add entry:
|
||||
```
|
||||
- Phase {decimal_phase} inserted after Phase {after_phase}: {description} (URGENT)
|
||||
```
|
||||
|
||||
@@ -208,7 +208,21 @@ AGENT_SKILLS_SYNTHESIZER=$(gsd-sdk query agent-skills gsd-synthesizer 2>/dev/nul
|
||||
AGENT_SKILLS_ROADMAPPER=$(gsd-sdk query agent-skills gsd-roadmapper 2>/dev/null)
|
||||
```
|
||||
|
||||
Extract from init JSON: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `research_enabled`, `current_milestone`, `project_exists`, `roadmap_exists`, `latest_completed_milestone`, `phase_dir_count`, `phase_archive_path`.
|
||||
Extract from init JSON: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `research_enabled`, `current_milestone`, `project_exists`, `roadmap_exists`, `latest_completed_milestone`, `phase_dir_count`, `phase_archive_path`, `agents_installed`, `missing_agents`.
|
||||
|
||||
**If `agents_installed` is false:** Display a warning before proceeding:
|
||||
```
|
||||
⚠ GSD agents not installed. The following agents are missing from your agents directory:
|
||||
{missing_agents joined with newline}
|
||||
|
||||
Subagent spawns (gsd-project-researcher, gsd-research-synthesizer, gsd-roadmapper) will fail
|
||||
with "agent type not found". Run the installer with --global to make agents available:
|
||||
|
||||
npx get-shit-done-cc@latest --global
|
||||
|
||||
Proceeding without research subagents — roadmap will be generated inline.
|
||||
```
|
||||
Skip the parallel research spawn step and generate the roadmap inline.
|
||||
|
||||
## 7.5 Reset-phase safety (only when `--reset-phase-numbers`)
|
||||
|
||||
@@ -496,6 +510,56 @@ Success criteria:
|
||||
gsd-sdk query commit "docs: create milestone v[X.Y] roadmap ([N] phases)" .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md
|
||||
```
|
||||
|
||||
## 10.5. Link Pending Todos to Roadmap Phases
|
||||
|
||||
After roadmap approval, scan pending todos against the newly approved phases. For each todo whose scope matches a phase, tag it with `resolves_phase: N` in its YAML frontmatter.
|
||||
|
||||
**Check for pending todos:**
|
||||
```bash
|
||||
PENDING_TODOS=$(ls .planning/todos/pending/*.md 2>/dev/null | head -50)
|
||||
```
|
||||
|
||||
**If no pending todos exist:** Skip this step silently.
|
||||
|
||||
**If pending todos exist:**
|
||||
|
||||
Read the approved ROADMAP.md and extract the phase list: phase number, phase name, goal, and requirement IDs.
|
||||
|
||||
For each pending todo, compare:
|
||||
- The todo's `title` and `area` frontmatter fields
|
||||
- The todo body (Problem and Solution sections)
|
||||
|
||||
Against each phase's:
|
||||
- Phase goal
|
||||
- Requirement IDs and descriptions
|
||||
|
||||
**Match criteria (best-effort — do not over-match):** A todo is considered resolved by a phase if the phase's goal or requirements directly describe implementing the same feature, area, or capability as the todo. Narrow, specific todos with concrete scopes are the best candidates. Vague or cross-cutting todos should be left unlinked.
|
||||
|
||||
**For each matched todo**, add `resolves_phase: [N]` to the YAML frontmatter block (after the existing fields):
|
||||
```yaml
|
||||
---
|
||||
created: [existing]
|
||||
title: [existing]
|
||||
area: [existing]
|
||||
resolves_phase: [N]
|
||||
files: [existing]
|
||||
---
|
||||
```
|
||||
|
||||
**Only modify todos that have a clear, confident match.** Leave unmatched todos unmodified.
|
||||
|
||||
**If any todos were linked:**
|
||||
```bash
|
||||
gsd-sdk query commit "docs: tag [count] pending todos with resolves_phase after milestone v[X.Y] roadmap" .planning/todos/pending/*.md
|
||||
```
|
||||
|
||||
Print a summary:
|
||||
```
|
||||
◆ Linked [N] pending todos to roadmap phases:
|
||||
→ [todo title] → Phase [N]: [Phase Name]
|
||||
(Leave [M] unmatched todos in pending/)
|
||||
```
|
||||
|
||||
## 11. Done
|
||||
|
||||
```
|
||||
@@ -539,6 +603,7 @@ Also: `/gsd-plan-phase [N] ${GSD_WS}` — skip discussion, plan directly
|
||||
- [ ] User feedback incorporated (if any)
|
||||
- [ ] Phase numbering mode respected (continued or reset)
|
||||
- [ ] All commits made (if planning docs committed)
|
||||
- [ ] Pending todos scanned for phase matches; matched todos tagged with `resolves_phase: N`
|
||||
- [ ] User knows next step: `/gsd-discuss-phase [N] ${GSD_WS}`
|
||||
|
||||
**Atomic commits:** Each phase commits its artifacts immediately.
|
||||
|
||||
@@ -64,7 +64,21 @@ AGENT_SKILLS_SYNTHESIZER=$(gsd-sdk query agent-skills gsd-synthesizer 2>/dev/nul
|
||||
AGENT_SKILLS_ROADMAPPER=$(gsd-sdk query agent-skills gsd-roadmapper 2>/dev/null)
|
||||
```
|
||||
|
||||
Parse JSON for: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `project_exists`, `has_codebase_map`, `planning_exists`, `has_existing_code`, `has_package_file`, `is_brownfield`, `needs_codebase_map`, `has_git`, `project_path`.
|
||||
Parse JSON for: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `commit_docs`, `project_exists`, `has_codebase_map`, `planning_exists`, `has_existing_code`, `has_package_file`, `is_brownfield`, `needs_codebase_map`, `has_git`, `project_path`, `agents_installed`, `missing_agents`.
|
||||
|
||||
**If `agents_installed` is false:** Display a warning before proceeding:
|
||||
```
|
||||
⚠ GSD agents not installed. The following agents are missing from your agents directory:
|
||||
{missing_agents joined with newline}
|
||||
|
||||
Subagent spawns (gsd-project-researcher, gsd-research-synthesizer, gsd-roadmapper) will fail
|
||||
with "agent type not found". Run the installer with --global to make agents available:
|
||||
|
||||
npx get-shit-done-cc@latest --global
|
||||
|
||||
Proceeding without research subagents — roadmap will be generated inline.
|
||||
```
|
||||
Skip Steps 6–7 (parallel research and synthesis) and proceed directly to roadmap creation in Step 8.
|
||||
|
||||
**Detect runtime and set instruction file name:**
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
|
||||
|
||||
<process>
|
||||
|
||||
## 0. Git Branch Invariant
|
||||
|
||||
**Do not create, rename, or switch git branches during plan-phase.** Branch identity is established at discuss-phase and is owned by the user's git workflow. A phase rename in ROADMAP.md is a plan-level change only — it does not mutate git branch names. If `phase_slug` in the init JSON differs from the current branch name, that is expected and correct; leave the branch unchanged.
|
||||
|
||||
## 1. Initialize
|
||||
|
||||
Load all context in one call (paths only to minimize orchestrator context):
|
||||
@@ -50,7 +54,7 @@ Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_
|
||||
|
||||
## 2. Parse and Normalize Arguments
|
||||
|
||||
Extract from $ARGUMENTS: phase number (integer or decimal like `2.1`), flags (`--research`, `--skip-research`, `--gaps`, `--skip-verify`, `--skip-ui`, `--prd <filepath>`, `--reviews`, `--text`, `--bounce`, `--skip-bounce`).
|
||||
Extract from $ARGUMENTS: phase number (integer or decimal like `2.1`), flags (`--research`, `--skip-research`, `--gaps`, `--skip-verify`, `--skip-ui`, `--prd <filepath>`, `--reviews`, `--text`, `--bounce`, `--skip-bounce`, `--chunked`).
|
||||
|
||||
Set `TEXT_MODE=true` if `--text` is present in $ARGUMENTS OR `text_mode` from init JSON is `true`. When `TEXT_MODE` is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for Claude Code remote sessions (`/rc` mode) where TUI menus don't work through the Claude App.
|
||||
|
||||
@@ -65,6 +69,15 @@ mkdir -p ".planning/phases/${padded_phase}-${phase_slug}"
|
||||
|
||||
**Existing artifacts from init:** `has_research`, `has_plans`, `plan_count`.
|
||||
|
||||
Set `CHUNKED_MODE` from flag or config:
|
||||
```bash
|
||||
CHUNKED_CFG=$(gsd-sdk query config-get workflow.plan_chunked 2>/dev/null || echo "false")
|
||||
CHUNKED_MODE=false
|
||||
if [[ "$ARGUMENTS" =~ --chunked ]] || [[ "$CHUNKED_CFG" == "true" ]]; then
|
||||
CHUNKED_MODE=true
|
||||
fi
|
||||
```
|
||||
|
||||
## 2.5. Validate `--reviews` Prerequisite
|
||||
|
||||
**Skip if:** No `--reviews` flag.
|
||||
@@ -471,9 +484,9 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
||||
|
||||
**If UI-SPEC.md missing AND `UI_GATE_CFG` is `true`:**
|
||||
|
||||
Read auto-chain state:
|
||||
Read ephemeral chain flag (same field as `check.auto-mode` → `auto_chain_active`):
|
||||
```bash
|
||||
AUTO_CHAIN=$(gsd-sdk query config-get workflow._auto_chain_active 2>/dev/null || echo "false")
|
||||
AUTO_CHAIN=$(gsd-sdk query check auto-mode --pick auto_chain_active 2>/dev/null || echo "false")
|
||||
```
|
||||
|
||||
**If `AUTO_CHAIN` is `true` (running inside a `--chain` or `--auto` pipeline):**
|
||||
@@ -716,7 +729,8 @@ ${CONTEXT_WINDOW >= 500000 ? `
|
||||
**Cross-phase context (1M model enrichment):**
|
||||
- CONTEXT.md files from the 3 most recent completed phases (locked decisions — maintain consistency)
|
||||
- SUMMARY.md files from the 3 most recent completed phases (what was built — reuse patterns, avoid duplication)
|
||||
- CONTEXT.md and SUMMARY.md from any phases listed in the current phase's "Depends on:" field in ROADMAP.md (regardless of recency — explicit dependencies always load, deduplicated against the 3 most recent)
|
||||
- LEARNINGS.md files from the 3 most recent completed phases (structured decisions, patterns, lessons, surprises — skip silently if a phase has no LEARNINGS.md; prefix each block with \`[from Phase N LEARNINGS]\` for source attribution; if total size exceeds 15% of context budget, drop oldest first)
|
||||
- CONTEXT.md, SUMMARY.md, and LEARNINGS.md from any phases listed in the current phase's "Depends on:" field in ROADMAP.md (regardless of recency — explicit dependencies always load, deduplicated against the 3 most recent)
|
||||
- Skip all other prior phases to stay within context budget
|
||||
` : ''}
|
||||
</files_to_read>
|
||||
@@ -790,6 +804,8 @@ Every task MUST include these fields — they are NOT optional:
|
||||
</quality_gate>
|
||||
```
|
||||
|
||||
**If `CHUNKED_MODE` is `false` (default):** Spawn the planner as a single long-lived Task:
|
||||
|
||||
```
|
||||
Task(
|
||||
prompt=filled_prompt,
|
||||
@@ -799,6 +815,112 @@ Task(
|
||||
)
|
||||
```
|
||||
|
||||
**If `CHUNKED_MODE` is `true`:** Skip the Task() call above — proceed to step 8.5 instead.
|
||||
|
||||
## 8.5. Chunked Planning Mode
|
||||
|
||||
**Skip if `CHUNKED_MODE` is `false`.**
|
||||
|
||||
Chunked mode splits the single long-lived planner Task into a short outline Task followed by
|
||||
N short per-plan Tasks. Each Task is bounded to ~3–5 min; each plan is committed individually
|
||||
for crash resilience. If any Task hangs and the terminal is force-killed, rerunning
|
||||
`/gsd-plan-phase {N} --chunked` resumes from the last successfully committed plan.
|
||||
|
||||
**Intended for new or in-progress chunked runs.** To recover plans already written by a prior
|
||||
*non-chunked* run, use step 6's "Add more plans" or proceed directly to `/gsd-execute-phase`
|
||||
— don't start a fresh chunked run over existing non-chunked plans.
|
||||
|
||||
### 8.5.1 Outline Phase (outline-only mode, ~2 min)
|
||||
|
||||
**Resume detection:** If `${PHASE_DIR}/${PADDED_PHASE}-PLAN-OUTLINE.md` already exists **and
|
||||
is valid** (contains the `## OUTLINE COMPLETE` marker), skip this sub-step — the outline
|
||||
already exists from a previous run. Proceed directly to 8.5.2.
|
||||
|
||||
```bash
|
||||
OUTLINE_FILE="${PHASE_DIR}/${PADDED_PHASE}-PLAN-OUTLINE.md"
|
||||
if [[ -f "$OUTLINE_FILE" ]] && grep -q "^## OUTLINE COMPLETE" "$OUTLINE_FILE"; then
|
||||
# reuse existing outline — skip to 8.5.2
|
||||
fi
|
||||
```
|
||||
|
||||
Display:
|
||||
```text
|
||||
◆ Chunked mode: spawning outline planner...
|
||||
```
|
||||
|
||||
Spawn the planner in **outline-only** mode — it must write only the outline manifest, not any
|
||||
PLAN.md files:
|
||||
|
||||
```javascript
|
||||
Task(
|
||||
prompt="{same planning_context as step 8, plus:}
|
||||
|
||||
**Chunked mode: outline-only.**
|
||||
Do NOT write any PLAN.md files in this Task.
|
||||
Write only: {PHASE_DIR}/{PADDED_PHASE}-PLAN-OUTLINE.md
|
||||
|
||||
The outline must be a markdown table with columns:
|
||||
Plan ID | Objective | Wave | Depends On | Requirements
|
||||
|
||||
Return: ## OUTLINE COMPLETE with plan count.",
|
||||
subagent_type="gsd-planner",
|
||||
model="{planner_model}",
|
||||
description="Outline Phase {phase} (chunked)"
|
||||
)
|
||||
```
|
||||
|
||||
Handle return:
|
||||
- **`## OUTLINE COMPLETE`:** Read `PLAN-OUTLINE.md`, extract plan list. Continue to 8.5.2.
|
||||
- **Any other return or empty:** Display error. Offer: 1) Retry outline, 2) Stop.
|
||||
|
||||
### 8.5.2 Per-Plan Tasks (single-plan mode, ~3-5 min each)
|
||||
|
||||
For each plan entry extracted from `PLAN-OUTLINE.md`:
|
||||
|
||||
1. **Resume check:** If `${PHASE_DIR}/{plan_id}-PLAN.md` already exists on disk **and has
|
||||
valid YAML frontmatter** (opening `---` delimiter present), skip this plan (do not
|
||||
overwrite completed work — resume safety).
|
||||
|
||||
```bash
|
||||
PLAN_FILE="${PHASE_DIR}/${plan_id}-PLAN.md"
|
||||
if [[ -f "$PLAN_FILE" ]] && head -1 "$PLAN_FILE" | grep -q '^---'; then
|
||||
continue # plan already written, skip
|
||||
fi
|
||||
```
|
||||
|
||||
2. Display:
|
||||
```text
|
||||
◆ Chunked mode: planning {plan_id} ({k}/{N})...
|
||||
```
|
||||
|
||||
3. Spawn the planner in **single-plan** mode — it must write exactly one PLAN.md file:
|
||||
```javascript
|
||||
Task(
|
||||
prompt="{same planning_context as step 8, plus:}
|
||||
|
||||
**Chunked mode: single-plan.**
|
||||
Write exactly ONE plan file: {PHASE_DIR}/{plan_id}-PLAN.md
|
||||
Plan to write: {plan_id} — {objective}
|
||||
Wave: {wave} | Depends on: {depends_on}
|
||||
Phase requirement IDs to cover in this plan: {plan_requirements}
|
||||
|
||||
Return: ## PLAN COMPLETE with the plan ID.",
|
||||
subagent_type="gsd-planner",
|
||||
model="{planner_model}",
|
||||
description="Plan {plan_id} (chunked {k}/{N})"
|
||||
)
|
||||
```
|
||||
|
||||
4. **Verify disk:** Check `${PHASE_DIR}/{plan_id}-PLAN.md` exists. If missing: offer 1) Retry, 2) Stop.
|
||||
|
||||
5. **Commit per-plan:**
|
||||
```bash
|
||||
gsd-sdk query commit "docs(${PADDED_PHASE}): plan ${plan_id} (chunked)" "${PHASE_DIR}/${plan_id}-PLAN.md"
|
||||
```
|
||||
|
||||
After all N plans are written and committed, treat this as `## PLANNING COMPLETE` and continue
|
||||
to step 9.
|
||||
|
||||
## 9. Handle Planner Return
|
||||
|
||||
- **`## PLANNING COMPLETE`:** Display plan count. If `--skip-verify` or `plan_checker_enabled` is false (from init): skip to step 13. Otherwise: step 10.
|
||||
@@ -806,6 +928,35 @@ Task(
|
||||
- **`## ⚠ Source Audit: Unplanned Items Found`:** The planner's multi-source coverage audit found items from REQUIREMENTS.md, RESEARCH.md, ROADMAP goal, or CONTEXT.md decisions that are not covered by any plan. Handle in step 9c.
|
||||
- **`## CHECKPOINT REACHED`:** Present to user, get response, spawn continuation (step 12)
|
||||
- **`## PLANNING INCONCLUSIVE`:** Show attempts, offer: Add context / Retry / Manual
|
||||
- **Empty / truncated / no recognized marker:** → Filesystem fallback (step 9a).
|
||||
|
||||
## 9a. Filesystem Fallback (Planner)
|
||||
|
||||
**Triggered when:** Task() returns but the return contains no recognized marker (`## PLANNING COMPLETE`, `## PHASE SPLIT RECOMMENDED`, `## ⚠ Source Audit`, `## CHECKPOINT REACHED`, `## PLANNING INCONCLUSIVE`).
|
||||
|
||||
```bash
|
||||
DISK_PLANS=$(ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null | wc -l | tr -d ' ')
|
||||
```
|
||||
|
||||
**If `DISK_PLANS` > 0:** The planner wrote plans to disk but the Task() return was empty or
|
||||
truncated (the Windows stdio hang pattern — the subagent finished but the return never
|
||||
arrived). Display:
|
||||
|
||||
```text
|
||||
◆ Planner wrote {DISK_PLANS} plan(s) to disk but did not emit a PLANNING COMPLETE marker.
|
||||
This is a known Windows stdio hang pattern — work is likely recoverable.
|
||||
|
||||
Plans found on disk:
|
||||
{ls output of *-PLAN.md}
|
||||
```
|
||||
|
||||
Offer 3 options:
|
||||
1. **Accept plans** — treat as `## PLANNING COMPLETE` and continue through step 9 `## PLANNING COMPLETE` handling (so `--skip-verify` / `plan_checker_enabled=false` are honored — may skip to step 13 rather than step 10)
|
||||
2. **Retry planner** — re-spawn the planner with the same prompt (return to step 8)
|
||||
3. **Stop** — exit; user can re-run `/gsd-plan-phase {N}` to resume
|
||||
|
||||
**If `DISK_PLANS` is 0 and no marker:** The planner produced no output. Treat as
|
||||
`## PLANNING INCONCLUSIVE` and handle accordingly.
|
||||
|
||||
## 9b. Handle Phase Split Recommendation
|
||||
|
||||
@@ -920,6 +1071,7 @@ Task(
|
||||
|
||||
- **`## VERIFICATION PASSED`:** Display confirmation, proceed to step 13.
|
||||
- **`## ISSUES FOUND`:** Display issues, check iteration count, proceed to step 12.
|
||||
- **Empty / truncated / no recognized marker:** → Filesystem fallback (step 11a).
|
||||
|
||||
**Thinking partner for architectural tradeoffs (conditional):**
|
||||
If `features.thinking_partner` is enabled, scan the checker's issues for architectural tradeoff keywords
|
||||
@@ -940,6 +1092,29 @@ Apply this to the revision? [Yes] / [No, I'll decide]
|
||||
If yes: include the recommendation in the revision prompt. If no: proceed to revision loop as normal.
|
||||
If thinking_partner disabled: skip this block entirely.
|
||||
|
||||
## 11a. Filesystem Fallback (Checker)
|
||||
|
||||
**Triggered when:** Checker Task() returns but the return contains neither `## VERIFICATION PASSED` nor `## ISSUES FOUND`.
|
||||
|
||||
```bash
|
||||
DISK_PLANS=$(ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null | wc -l | tr -d ' ')
|
||||
```
|
||||
|
||||
**If `DISK_PLANS` > 0:** Plans exist on disk; the checker return was empty or truncated (the
|
||||
Windows stdio hang pattern — the subagent finished but the return never arrived). Display:
|
||||
|
||||
```text
|
||||
◆ Checker return was empty or truncated. {DISK_PLANS} plan(s) exist on disk.
|
||||
This is a known Windows stdio hang pattern — checker may have completed without returning.
|
||||
```
|
||||
|
||||
Offer 3 options:
|
||||
1. **Accept verification** — treat as `## VERIFICATION PASSED` and continue to step 13
|
||||
2. **Retry checker** — re-spawn the checker with the same prompt (return to step 10)
|
||||
3. **Stop** — exit; user can re-run `/gsd-plan-phase {N}` to resume
|
||||
|
||||
**If `DISK_PLANS` is 0:** No plans on disk — something is seriously wrong. Display error and stop.
|
||||
|
||||
## 12. Revision Loop (Max 3 Iterations)
|
||||
|
||||
Track `iteration_count` (starts at 1 after initial plan + check).
|
||||
@@ -1145,6 +1320,30 @@ gsd-sdk query state.planned-phase --phase "${PHASE_NUMBER}" --name "${PHASE_NAME
|
||||
|
||||
This updates STATUS to "Ready to execute", sets the correct plan count, and timestamps Last Activity.
|
||||
|
||||
## 13c. Annotate ROADMAP with Wave Dependencies and Cross-cutting Constraints
|
||||
|
||||
After plans are finalized, annotate the ROADMAP.md plan list for this phase with:
|
||||
- **Wave dependency notes** — a bold header before each wave group ("Wave 2 *(blocked on Wave 1 completion)*")
|
||||
- **Cross-cutting constraints** — a "Cross-cutting constraints:" subsection listing `must_haves.truths` entries that appear in 2 or more plans
|
||||
|
||||
This step is derived entirely from existing PLAN frontmatter — no extra LLM pass is required.
|
||||
|
||||
```bash
|
||||
gsd-sdk query roadmap.annotate-dependencies "${PHASE_NUMBER}"
|
||||
```
|
||||
|
||||
This operation is idempotent: if wave headers or cross-cutting constraints already exist in the ROADMAP phase section, the command returns without modifying the file. Skip this step if `plan_count` is 0.
|
||||
|
||||
## 13d. Commit Plans if commit_docs is true
|
||||
|
||||
If `commit_docs` is true (from the init JSON parsed in step 1), commit the generated plan artifacts (including any ROADMAP.md annotations from step 13c):
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "docs(${PADDED_PHASE}): create phase plan" --files "${PHASE_DIR}"/*-PLAN.md .planning/STATE.md .planning/ROADMAP.md
|
||||
```
|
||||
|
||||
This commits all PLAN.md files for the phase plus the updated STATE.md and ROADMAP.md to version-control the planning artifacts. Skip this step if `commit_docs` is false.
|
||||
|
||||
## 14. Present Final Status
|
||||
|
||||
Route to `<offer_next>` OR `auto_advance` depending on flags/config.
|
||||
|
||||
@@ -567,6 +567,24 @@ Offer: 1) Force proceed, 2) Abort
|
||||
|
||||
---
|
||||
|
||||
**Step 5.6: Pre-dispatch plan commit (worktree mode only)**
|
||||
|
||||
When `USE_WORKTREES !== "false"`, commit PLAN.md to the current branch **before** spawning the executor. This ensures the worktree inherits PLAN.md at its branch HEAD so the executor can read it via a worktree-rooted path — avoiding the main-repo path priming that triggers CC #36182 path-resolution drift.
|
||||
|
||||
Skip this step entirely if `USE_WORKTREES === "false"` (non-worktree mode: PLAN.md is committed in Step 8 as usual).
|
||||
|
||||
```bash
|
||||
if [ "${USE_WORKTREES}" != "false" ]; then
|
||||
COMMIT_DOCS=$(gsd-sdk query config-get commit_docs 2>/dev/null || echo "true")
|
||||
if [ "$COMMIT_DOCS" != "false" ]; then
|
||||
git add "${QUICK_DIR}/${quick_id}-PLAN.md"
|
||||
git commit --no-verify -m "docs(${quick_id}): pre-dispatch plan for ${DESCRIPTION}" -- "${QUICK_DIR}/${quick_id}-PLAN.md" || true
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Step 6: Spawn executor**
|
||||
|
||||
Capture current HEAD before spawning (used for worktree branch check):
|
||||
@@ -682,7 +700,19 @@ After executor returns:
|
||||
git merge "$WT_BRANCH" --no-edit -m "chore: merge rescued SUMMARY.md from executor worktree ($WT_BRANCH)" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
git worktree remove "$WT" --force 2>/dev/null || true
|
||||
if ! git worktree remove "$WT" --force; then
|
||||
WT_NAME=$(basename "$WT")
|
||||
if [ -f ".git/worktrees/${WT_NAME}/locked" ]; then
|
||||
echo "⚠ Worktree $WT is locked — attempting to unlock and retry"
|
||||
git worktree unlock "$WT" 2>/dev/null || true
|
||||
if ! git worktree remove "$WT" --force; then
|
||||
echo "⚠ Residual worktree at $WT — manual cleanup required after session exits:"
|
||||
echo " git worktree unlock \"$WT\" && git worktree remove \"$WT\" --force && git branch -D \"$WT_BRANCH\""
|
||||
fi
|
||||
else
|
||||
echo "⚠ Residual worktree at $WT (remove failed) — investigate manually"
|
||||
fi
|
||||
fi
|
||||
git branch -D "$WT_BRANCH" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
@@ -862,6 +892,7 @@ Build file list:
|
||||
- If `$DISCUSS_MODE` and context file exists: `${QUICK_DIR}/${quick_id}-CONTEXT.md`
|
||||
- If `$RESEARCH_MODE` and research file exists: `${QUICK_DIR}/${quick_id}-RESEARCH.md`
|
||||
- If `$VALIDATE_MODE` and verification file exists: `${QUICK_DIR}/${quick_id}-VERIFICATION.md`
|
||||
- If `${QUICK_DIR}/${quick_id}-deferred-items.md` exists: `${QUICK_DIR}/${quick_id}-deferred-items.md`
|
||||
|
||||
```bash
|
||||
# Explicitly stage all artifacts before commit — PLAN.md may be untracked
|
||||
|
||||
@@ -13,13 +13,21 @@ Ensure config exists and load current state:
|
||||
|
||||
```bash
|
||||
gsd-sdk query config-ensure-section
|
||||
GSD_CONFIG_PATH=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-path)
|
||||
INIT=$(gsd-sdk query state.load)
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
# `state.load` returns STATE frontmatter JSON from the SDK — it does not include `config_path`. Orchestrators may set `GSD_CONFIG_PATH` from init phase-op JSON; otherwise resolve the same path gsd-tools uses for flat vs active workstream (#2282).
|
||||
if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then
|
||||
if [[ -f .planning/active-workstream ]]; then
|
||||
WS=$(tr -d '\n\r' < .planning/active-workstream)
|
||||
GSD_CONFIG_PATH=".planning/workstreams/${WS}/config.json"
|
||||
else
|
||||
GSD_CONFIG_PATH=".planning/config.json"
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
Creates config.json (at the workstream-aware path) with defaults if missing and loads current config values.
|
||||
Store `$GSD_CONFIG_PATH` — all subsequent reads and writes use this path, not the hardcoded `.planning/config.json`, so active-workstream installs write to the correct location (#2282).
|
||||
Creates `config.json` (at the resolved path) with defaults if missing. `INIT` still holds `state.load` output for any step that needs STATE fields.
|
||||
Store `$GSD_CONFIG_PATH` — all subsequent reads and writes use this path, not a hardcoded `.planning/config.json`, so active-workstream installs target the correct file (#2282).
|
||||
</step>
|
||||
|
||||
<step name="read_current">
|
||||
@@ -43,6 +51,17 @@ Parse current values (default to `true` if not present):
|
||||
<step name="present_settings">
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
|
||||
**Non-Claude runtime note:** If `TEXT_MODE` is active (i.e. the runtime is non-Claude), prepend the following notice before the model profile question:
|
||||
|
||||
```
|
||||
Note: Quality, Balanced, and Budget profiles select Claude model tiers (Opus/Sonnet/Haiku).
|
||||
On non-Claude runtimes (Codex, Gemini CLI, etc.) these profiles have no effect on actual
|
||||
model selection — GSD agents will use the runtime's default model.
|
||||
Choose "Inherit" to use the session model for all agents, or configure model_overrides
|
||||
manually in .planning/config.json to target specific models for this runtime.
|
||||
```
|
||||
|
||||
Use AskUserQuestion with current values pre-selected:
|
||||
|
||||
```
|
||||
@@ -52,10 +71,10 @@ AskUserQuestion([
|
||||
header: "Model",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Quality", description: "Opus everywhere except verification (highest cost)" },
|
||||
{ label: "Balanced (Recommended)", description: "Opus for planning, Sonnet for research/execution/verification" },
|
||||
{ label: "Budget", description: "Sonnet for writing, Haiku for research/verification (lowest cost)" },
|
||||
{ label: "Inherit", description: "Use current session model for all agents (best for OpenRouter, local models, or runtime model switching)" }
|
||||
{ label: "Quality", description: "Opus everywhere except verification (highest cost) — Claude only" },
|
||||
{ label: "Balanced (Recommended)", description: "Opus for planning, Sonnet for research/execution/verification — Claude only" },
|
||||
{ label: "Budget", description: "Sonnet for writing, Haiku for research/verification (lowest cost) — Claude only" },
|
||||
{ label: "Inherit", description: "Use current session model for all agents (required for non-Claude runtimes: Codex, Gemini CLI, OpenRouter, local models)" }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -255,15 +255,16 @@ The sketch-findings skill will auto-load when building the UI.
|
||||
|
||||
## ▶ Next Up
|
||||
|
||||
**Start building** — implement the validated design
|
||||
**Explore frontier sketches** — see what else is worth sketching based on what we've explored
|
||||
|
||||
`/gsd-plan-phase`
|
||||
`/gsd-sketch` (run with no argument — its frontier mode analyzes the sketch landscape and proposes consistency and frontier sketches)
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
**Also available:**
|
||||
- `/gsd-plan-phase` — start building the real UI
|
||||
- `/gsd-ui-phase` — generate a UI design contract for a frontend phase
|
||||
- `/gsd-sketch` — sketch additional design areas
|
||||
- `/gsd-sketch [idea]` — sketch a specific new design area
|
||||
- `/gsd-explore` — continue exploring
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
@@ -279,5 +280,6 @@ The sketch-findings skill will auto-load when building the UI.
|
||||
- [ ] Reference files contain design decisions, CSS patterns, HTML structures, anti-patterns
|
||||
- [ ] `.planning/sketches/WRAP-UP-SUMMARY.md` written for project history
|
||||
- [ ] Project CLAUDE.md has auto-load routing line
|
||||
- [ ] Summary presented with next-step routing
|
||||
- [ ] Summary presented
|
||||
- [ ] Next-step options presented (including frontier sketch exploration via `/gsd-sketch`)
|
||||
</success_criteria>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Explore design directions through throwaway HTML mockups before committing to implementation.
|
||||
Each sketch produces 2-3 variants for comparison. Saves artifacts to `.planning/sketches/`.
|
||||
Companion to `/gsd-sketch-wrap-up`.
|
||||
|
||||
Supports two modes:
|
||||
- **Idea mode** (default) — user describes a design idea to sketch
|
||||
- **Frontier mode** — no argument or "frontier" / "what should I sketch?" — analyzes existing sketch landscape and proposes consistency and frontier sketches
|
||||
</purpose>
|
||||
|
||||
<required_reading>
|
||||
@@ -25,9 +29,60 @@ Read all files referenced by the invoking prompt's execution_context before star
|
||||
Parse `$ARGUMENTS` for:
|
||||
- `--quick` flag → set `QUICK_MODE=true`
|
||||
- `--text` flag → set `TEXT_MODE=true`
|
||||
- `frontier` or empty → set `FRONTIER_MODE=true`
|
||||
- Remaining text → the design idea to sketch
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
**Text mode:** If TEXT_MODE is enabled, replace AskUserQuestion calls with plain-text numbered lists.
|
||||
</step>
|
||||
|
||||
<step name="route">
|
||||
## Routing
|
||||
|
||||
- **FRONTIER_MODE is true** → Jump to `frontier_mode`
|
||||
- **Otherwise** → Continue to `setup_directory`
|
||||
</step>
|
||||
|
||||
<step name="frontier_mode">
|
||||
## Frontier Mode — Propose What to Sketch Next
|
||||
|
||||
### Load the Sketch Landscape
|
||||
|
||||
If no `.planning/sketches/` directory exists, tell the user there's nothing to analyze and offer to start fresh with an idea instead.
|
||||
|
||||
Otherwise, load in this order:
|
||||
|
||||
**a. MANIFEST.md** — the design direction, reference points, and sketch table with winners.
|
||||
|
||||
**b. Findings skills** — glob `./.claude/skills/sketch-findings-*/SKILL.md` and read any that exist, plus their `references/*.md`. These contain curated design decisions from prior wrap-ups.
|
||||
|
||||
**c. All sketch READMEs** — read `.planning/sketches/*/README.md` for design questions, winners, and tags.
|
||||
|
||||
### Analyze for Consistency Sketches
|
||||
|
||||
Review winning variants across all sketches. Look for:
|
||||
|
||||
- **Visual consistency gaps:** Two sketches made independent design choices that haven't been tested together.
|
||||
- **State combinations:** Individual states validated but not seen in sequence.
|
||||
- **Responsive gaps:** Validated at one viewport but the real app needs multiple.
|
||||
- **Theme coherence:** Individual components look good but haven't been composed into a full-page view.
|
||||
|
||||
If consistency risks exist, present them as concrete proposed sketches with names and design questions. If no meaningful gaps, say so and skip.
|
||||
|
||||
### Analyze for Frontier Sketches
|
||||
|
||||
Think laterally about the design direction from MANIFEST.md and what's been explored:
|
||||
|
||||
- **Unsketched screens:** UI surfaces assumed but unexplored.
|
||||
- **Interaction patterns:** Static layouts validated but transitions, loading, drag-and-drop need feeling.
|
||||
- **Edge case UI:** 0 items, 1000 items, errors, slow connections.
|
||||
- **Alternative directions:** Fresh takes on "fine but not great" sketches.
|
||||
- **Polish passes:** Typography, spacing, micro-interactions, empty states.
|
||||
|
||||
Present frontier sketches as concrete proposals numbered from the highest existing sketch number.
|
||||
|
||||
### Get Alignment and Execute
|
||||
|
||||
Present all consistency and frontier candidates, then ask which to run. When the user picks sketches, update `.planning/sketches/MANIFEST.md` and proceed directly to building them starting at `build_sketches`.
|
||||
</step>
|
||||
|
||||
<step name="setup_directory">
|
||||
@@ -49,27 +104,45 @@ COMMIT_DOCS=$(gsd-sdk query config-get commit_docs 2>/dev/null || echo "true")
|
||||
</step>
|
||||
|
||||
<step name="mood_intake">
|
||||
**If `QUICK_MODE` is true:** Skip mood intake. Use whatever the user provided in `$ARGUMENTS` as the design direction. Jump to `decompose`.
|
||||
**If `QUICK_MODE` is true:** Skip mood intake. Use whatever the user provided in `$ARGUMENTS` as the design direction. Jump to `load_spike_context`.
|
||||
|
||||
**Otherwise:**
|
||||
|
||||
**Text mode:** If TEXT_MODE is enabled (set in the banner step), replace AskUserQuestion calls with plain-text numbered lists — emit the options and ask the user to type the number of their choice.
|
||||
|
||||
Before sketching anything, explore the design intent through conversation. Ask one question at a time — using AskUserQuestion in normal mode, or a plain-text numbered list if TEXT_MODE is active — with a paragraph of context and reasoning for each.
|
||||
Before sketching anything, explore the design intent through conversation. Ask one question at a time — using AskUserQuestion in normal mode, or a plain-text numbered list if TEXT_MODE is active.
|
||||
|
||||
**Questions to cover (adapt to what the user has already shared):**
|
||||
|
||||
1. **Feel:** "What should this feel like? Give me adjectives, emotions, or a vibe." (e.g., "clean and clinical", "warm and playful", "dense and powerful")
|
||||
2. **References:** "What apps, sites, or products have a similar feel to what you're imagining?" (gives concrete visual anchors)
|
||||
3. **Core action:** "What's the single most important thing a user does here?" (focuses the sketch on what matters)
|
||||
1. **Feel:** "What should this feel like? Give me adjectives, emotions, or a vibe."
|
||||
2. **References:** "What apps, sites, or products have a similar feel to what you're imagining?"
|
||||
3. **Core action:** "What's the single most important thing a user does here?"
|
||||
|
||||
You may need more or fewer questions depending on how much the user shares upfront. After each answer, briefly reflect what you heard and how it shapes your thinking.
|
||||
After each answer, briefly reflect what you heard and how it shapes your thinking.
|
||||
|
||||
When you have enough signal, ask: **"I think I have a good sense of the direction. Ready for me to sketch, or want to keep discussing?"**
|
||||
|
||||
Only proceed when the user says go.
|
||||
</step>
|
||||
|
||||
<step name="load_spike_context">
|
||||
## Load Spike Context
|
||||
|
||||
If spikes exist for this project, read them to ground the sketches in reality. Mockups are still pure HTML, but they should reflect what's actually been proven — real data shapes, real component names, real interaction patterns.
|
||||
|
||||
**a.** Glob for `./.claude/skills/spike-findings-*/SKILL.md` and read any that exist, plus their `references/*.md`. These contain validated patterns and requirements.
|
||||
|
||||
**b.** Read `.planning/spikes/MANIFEST.md` if it exists — check the Requirements section for non-negotiable design constraints (e.g., "must support streaming", "must render markdown"). These requirements should be visible in the mockup even though the mockup doesn't implement them for real.
|
||||
|
||||
**c.** Read `.planning/spikes/CONVENTIONS.md` if it exists — the established stack informs what's buildable and what interaction patterns are idiomatic.
|
||||
|
||||
**How spike context improves sketches:**
|
||||
- Use real field names and data shapes from spike findings instead of generic placeholders
|
||||
- Show realistic UI states that match what the spikes proved (e.g., if streaming was validated, show a streaming message state)
|
||||
- Reference real component names and patterns from the target stack
|
||||
- Include interaction states that reflect what the spikes discovered (loading, error, reconnection states)
|
||||
|
||||
**If no spikes exist**, skip this step.
|
||||
</step>
|
||||
|
||||
<step name="decompose">
|
||||
Break the idea into 2-5 design questions. Present as a table:
|
||||
|
||||
@@ -92,6 +165,28 @@ Bad sketches:
|
||||
Present the table and get alignment before building.
|
||||
</step>
|
||||
|
||||
<step name="research_stack">
|
||||
## Research the Target Stack
|
||||
|
||||
Before sketching, ground the design in what's actually buildable. Sketches are HTML, but they should reflect real constraints of the target implementation.
|
||||
|
||||
**a. Identify the target stack.** Check for package.json, Cargo.toml, etc. If the user mentioned a framework (React, SwiftUI, Flutter, etc.), note it.
|
||||
|
||||
**b. Check component/pattern availability.** Use context7 (resolve-library-id → query-docs) or web search to answer:
|
||||
- What layout primitives does the target framework provide?
|
||||
- Are there existing component libraries in use? What components are available?
|
||||
- What interaction patterns are idiomatic?
|
||||
|
||||
**c. Note constraints that affect design:**
|
||||
- Platform conventions (iOS nav patterns, desktop menu bars, terminal grid constraints)
|
||||
- Framework limitations (what's easy vs requires custom work)
|
||||
- Existing design tokens or theme systems already in the project
|
||||
|
||||
**d. Let research inform variants.** At least one variant should follow the path of least resistance for the target stack.
|
||||
|
||||
**Skip when unnecessary.** Greenfield project with no stack, or user says "just explore visually." The point is grounding, not gatekeeping.
|
||||
</step>
|
||||
|
||||
<step name="create_manifest">
|
||||
Create or update `.planning/sketches/MANIFEST.md`:
|
||||
|
||||
@@ -124,26 +219,24 @@ Build each sketch in order.
|
||||
|
||||
### For Each Sketch:
|
||||
|
||||
**a.** Find next available number by checking existing `.planning/sketches/NNN-*/` directories.
|
||||
Format: three-digit zero-padded + hyphenated descriptive name.
|
||||
**a.** Find next available number. Format: three-digit zero-padded + hyphenated descriptive name.
|
||||
|
||||
**b.** Create the sketch directory: `.planning/sketches/NNN-descriptive-name/`
|
||||
|
||||
**c.** Build `index.html` with 2-3 variants:
|
||||
|
||||
**First round — dramatic differences:** Build 2-3 meaningfully different approaches to the design question. Different layouts, different visual structures, different interaction models.
|
||||
|
||||
**Subsequent rounds — refinements:** Once the user has picked a direction or cherry-picked elements, build subtler variations within that direction.
|
||||
**First round — dramatic differences:** 2-3 meaningfully different approaches.
|
||||
**Subsequent rounds — refinements:** Subtler variations within the chosen direction.
|
||||
|
||||
Each variant is a page/tab in the same HTML file. Include:
|
||||
- Tab navigation to switch between variants (see `sketch-variant-patterns.md`)
|
||||
- Clear labels: "Variant A: Sidebar Layout", "Variant B: Top Nav", etc.
|
||||
- The sketch toolbar (see `sketch-tooling.md`)
|
||||
- All interactive elements functional (see `sketch-interactivity.md`)
|
||||
- Real-ish content, not lorem ipsum
|
||||
- Real-ish content, not lorem ipsum (use real field names from spike context if available)
|
||||
- Link to `../themes/default.css` for shared theme variables
|
||||
|
||||
**All sketches are plain HTML with inline CSS and JS.** No build step, no npm, no framework. Opens instantly in a browser.
|
||||
**All sketches are plain HTML with inline CSS and JS.** No build step, no npm, no framework.
|
||||
|
||||
**d.** Write `README.md`:
|
||||
|
||||
@@ -190,16 +283,16 @@ Compare: {what to look for between variants}
|
||||
──────────────────────────────────────────────────────────────
|
||||
|
||||
**f.** Handle feedback:
|
||||
- **Pick a direction:** "I like variant B" → mark winner in README, move to next sketch
|
||||
- **Cherry-pick elements:** "Rounded edges from A, color treatment from C" → build a synthesis as a new variant, show again
|
||||
- **Want more exploration:** "None of these feel right, try X instead" → build new variants
|
||||
- **Pick a direction:** mark winner, move to next sketch
|
||||
- **Cherry-pick elements:** build synthesis as new variant, show again
|
||||
- **Want more exploration:** build new variants
|
||||
|
||||
Iterate until the user is satisfied with a direction for this sketch.
|
||||
Iterate until satisfied.
|
||||
|
||||
**g.** Finalize:
|
||||
1. Mark the winning variant in the README frontmatter (`winner: "B"`)
|
||||
2. Add ★ indicator to the winning tab in the HTML
|
||||
3. Update `.planning/sketches/MANIFEST.md` with the sketch row
|
||||
1. Mark winning variant in README frontmatter (`winner: "B"`)
|
||||
2. Add ★ indicator to winning tab in HTML
|
||||
3. Update `.planning/sketches/MANIFEST.md`
|
||||
|
||||
**h.** Commit (if `COMMIT_DOCS` is true):
|
||||
```bash
|
||||
@@ -215,7 +308,7 @@ gsd-sdk query commit "docs(sketch-NNN): [winning direction] — [key visual insi
|
||||
</step>
|
||||
|
||||
<step name="report">
|
||||
After all sketches complete, present the summary:
|
||||
After all sketches complete:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
@@ -243,8 +336,8 @@ After all sketches complete, present the summary:
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
**Also available:**
|
||||
- `/gsd-sketch` — sketch more (or run with no argument for frontier mode)
|
||||
- `/gsd-plan-phase` — start building the real UI
|
||||
- `/gsd-explore` — continue exploring the concept
|
||||
- `/gsd-spike` — spike technical feasibility of a design pattern
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
@@ -255,7 +348,9 @@ After all sketches complete, present the summary:
|
||||
<success_criteria>
|
||||
- [ ] `.planning/sketches/` created (auto-creates if needed, no project init required)
|
||||
- [ ] Design direction explored conversationally before any code (unless --quick)
|
||||
- [ ] Each sketch has 2-3 variants for comparison
|
||||
- [ ] Spike context loaded — real data shapes, requirements, and conventions inform mockups
|
||||
- [ ] Target stack researched — component availability, constraints, idioms (unless greenfield/skipped)
|
||||
- [ ] Each sketch has 2-3 variants for comparison (at least one follows path of least resistance)
|
||||
- [ ] User can open and interact with sketches in a browser
|
||||
- [ ] Winning variant selected and marked for each sketch
|
||||
- [ ] All variants preserved (winner marked, not others deleted)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<purpose>
|
||||
Curate spike experiment findings and package them into a persistent project skill for future
|
||||
build conversations. Reads from `.planning/spikes/`, writes skill to `./.claude/skills/spike-findings-[project]/`
|
||||
(project-local) and summary to `.planning/spikes/WRAP-UP-SUMMARY.md`.
|
||||
Companion to `/gsd-spike`.
|
||||
Package spike experiment findings into a persistent project skill — an implementation blueprint
|
||||
for future build conversations. Reads from `.planning/spikes/`, writes skill to
|
||||
`./.claude/skills/spike-findings-[project]/` (project-local) and summary to
|
||||
`.planning/spikes/WRAP-UP-SUMMARY.md`. Companion to `/gsd-spike`.
|
||||
</purpose>
|
||||
|
||||
<required_reading>
|
||||
@@ -22,7 +22,7 @@ Read all files referenced by the invoking prompt's execution_context before star
|
||||
<step name="gather">
|
||||
## Gather Spike Inventory
|
||||
|
||||
1. Read `.planning/spikes/MANIFEST.md` for the overall idea context
|
||||
1. Read `.planning/spikes/MANIFEST.md` for the overall idea context and requirements
|
||||
2. Glob `.planning/spikes/*/README.md` and parse YAML frontmatter from each
|
||||
3. Check if `./.claude/skills/spike-findings-*/SKILL.md` exists for this project
|
||||
- If yes: read its `processed_spikes` list from the metadata section and filter those out
|
||||
@@ -41,53 +41,28 @@ COMMIT_DOCS=$(gsd-sdk query config-get commit_docs 2>/dev/null || echo "true")
|
||||
```
|
||||
</step>
|
||||
|
||||
<step name="curate">
|
||||
## Curate Spikes One-at-a-Time
|
||||
<step name="auto_include">
|
||||
## Auto-Include All Spikes
|
||||
|
||||
Present each unprocessed spike in ascending order. For each spike, show:
|
||||
Include all unprocessed spikes automatically. Present a brief inventory showing what's being processed:
|
||||
|
||||
- **Spike number and name**
|
||||
- **Validates:** the Given/When/Then from frontmatter
|
||||
- **Verdict:** VALIDATED / INVALIDATED / PARTIAL
|
||||
- **Tags:** from frontmatter
|
||||
- **Key findings:** summarize the Results section from the README
|
||||
- **Grey areas:** anything uncertain or partially proven
|
||||
```
|
||||
Processing N spikes:
|
||||
001 — name (VALIDATED)
|
||||
002 — name (PARTIAL)
|
||||
003 — name (INVALIDATED)
|
||||
```
|
||||
|
||||
Then ask the user:
|
||||
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Decision Required ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
|
||||
Spike {NNN}: {name} — {verdict}
|
||||
|
||||
{key findings summary}
|
||||
|
||||
──────────────────────────────────────────────────────────────
|
||||
→ Include / Exclude / Partial / Help me UAT this
|
||||
──────────────────────────────────────────────────────────────
|
||||
|
||||
**If "Help me UAT this":**
|
||||
1. Read the spike's README "How to Run" and "What to Expect" sections
|
||||
2. Present step-by-step instructions
|
||||
3. Ask: "Does this match what you expected?"
|
||||
4. After UAT, return to the include/exclude/partial decision
|
||||
|
||||
**If "Partial":**
|
||||
Ask what specifically to include or exclude. Record their notes alongside the spike.
|
||||
Every spike carries forward:
|
||||
- **VALIDATED** spikes provide proven patterns
|
||||
- **PARTIAL** spikes provide constrained patterns
|
||||
- **INVALIDATED** spikes provide landmines and dead ends
|
||||
</step>
|
||||
|
||||
<step name="group">
|
||||
## Auto-Group by Feature Area
|
||||
|
||||
After all spikes are curated:
|
||||
|
||||
1. Read all included spikes' tags, names, `related` fields, and content
|
||||
2. Propose feature-area groupings, e.g.:
|
||||
- "**WebSocket Streaming** — spikes 001, 004, 007"
|
||||
- "**Foo API Integration** — spikes 002, 003"
|
||||
- "**PDF Parsing** — spike 005"
|
||||
3. Present the grouping for approval — user may merge, split, rename, or rearrange
|
||||
Group spikes by feature area based on tags, names, `related` fields, and content. Proceed directly into synthesis.
|
||||
|
||||
Each group becomes one reference file in the generated skill.
|
||||
</step>
|
||||
@@ -118,21 +93,29 @@ For each included spike:
|
||||
<step name="synthesize">
|
||||
## Synthesize Reference Files
|
||||
|
||||
For each feature-area group, write a reference file at `references/[feature-area-name].md`:
|
||||
For each feature-area group, write a reference file at `references/[feature-area-name].md` as an **implementation blueprint** — it should read like a recipe, not a research paper. A future build session should be able to follow this and build the feature correctly without re-spiking anything.
|
||||
|
||||
```markdown
|
||||
# [Feature Area Name]
|
||||
|
||||
## Validated Patterns
|
||||
[For each validated finding: describe the approach that works, include key code snippets extracted from the spike source, explain why it works]
|
||||
## Requirements
|
||||
|
||||
## Landmines
|
||||
[Things that look right but aren't. Gotchas. Anti-patterns discovered during spiking.]
|
||||
[Non-negotiable design decisions from MANIFEST.md Requirements section that apply to this feature area. These MUST be honored in the real build. E.g., "Must use streaming JSON output", "Must support reconnection".]
|
||||
|
||||
## How to Build It
|
||||
|
||||
[Step-by-step: what to install, how to configure, what code pattern to use. Include key code snippets extracted from the spike source. This is the proven approach — not theory, but tested and working code.]
|
||||
|
||||
## What to Avoid
|
||||
|
||||
[Things that look right but aren't. Gotchas. Anti-patterns discovered during spiking. Dead ends that were tried and failed.]
|
||||
|
||||
## Constraints
|
||||
|
||||
[Hard facts: rate limits, library limitations, version requirements, incompatibilities]
|
||||
|
||||
## Origin
|
||||
|
||||
Synthesized from spikes: NNN, NNN, NNN
|
||||
Source files available in: sources/NNN-spike-name/, sources/NNN-spike-name/
|
||||
```
|
||||
@@ -146,7 +129,7 @@ Create (or update) the generated skill's SKILL.md:
|
||||
```markdown
|
||||
---
|
||||
name: spike-findings-[project-dir-name]
|
||||
description: Validated patterns, constraints, and implementation knowledge from spike experiments. Auto-loaded during implementation work on [project-dir-name].
|
||||
description: Implementation blueprint from spike experiments. Requirements, proven patterns, and verified knowledge for building [project-dir-name]. Auto-loaded during implementation work.
|
||||
---
|
||||
|
||||
<context>
|
||||
@@ -157,6 +140,15 @@ description: Validated patterns, constraints, and implementation knowledge from
|
||||
Spike sessions wrapped: [date(s)]
|
||||
</context>
|
||||
|
||||
<requirements>
|
||||
## Requirements
|
||||
|
||||
[Copied directly from MANIFEST.md Requirements section. These are non-negotiable design decisions that emerged from the user's choices during spiking. Every feature area reference must honor these.]
|
||||
|
||||
- [requirement 1]
|
||||
- [requirement 2]
|
||||
</requirements>
|
||||
|
||||
<findings_index>
|
||||
## Feature Areas
|
||||
|
||||
@@ -193,13 +185,9 @@ Write `.planning/spikes/WRAP-UP-SUMMARY.md` for project history:
|
||||
**Feature areas:** [list]
|
||||
**Skill output:** `./.claude/skills/spike-findings-[project]/`
|
||||
|
||||
## Included Spikes
|
||||
| # | Name | Verdict | Feature Area |
|
||||
|---|------|---------|--------------|
|
||||
|
||||
## Excluded Spikes
|
||||
| # | Name | Reason |
|
||||
|---|------|--------|
|
||||
## Processed Spikes
|
||||
| # | Name | Type | Verdict | Feature Area |
|
||||
|---|------|------|---------|--------------|
|
||||
|
||||
## Key Findings
|
||||
[consolidated findings summary]
|
||||
@@ -218,11 +206,47 @@ Add an auto-load routing line to the project's CLAUDE.md (create the file if it
|
||||
If this routing line already exists (append mode), leave it as-is.
|
||||
</step>
|
||||
|
||||
<step name="generate_conventions">
|
||||
## Generate or Update CONVENTIONS.md
|
||||
|
||||
Analyze all processed spikes for recurring patterns and write `.planning/spikes/CONVENTIONS.md`. This file tells future spike sessions *how we spike* — the stack, structure, and patterns that have been established.
|
||||
|
||||
1. Read all spike source code and READMEs looking for:
|
||||
- **Stack choices** — What language/framework/runtime appears across multiple spikes?
|
||||
- **Structure patterns** — Common file layouts, port numbers, naming schemes
|
||||
- **Recurring approaches** — How auth is handled, how styling is done, how data is served
|
||||
- **Tools & libraries** — Packages that showed up repeatedly with versions that worked
|
||||
|
||||
2. Write or update `.planning/spikes/CONVENTIONS.md`:
|
||||
|
||||
```markdown
|
||||
# Spike Conventions
|
||||
|
||||
Patterns and stack choices established across spike sessions. New spikes follow these unless the question requires otherwise.
|
||||
|
||||
## Stack
|
||||
[What we use for frontend, backend, scripts, and why — derived from what repeated across spikes]
|
||||
|
||||
## Structure
|
||||
[Common file layouts, port assignments, naming patterns]
|
||||
|
||||
## Patterns
|
||||
[Recurring approaches: how we handle auth, how we style, how we serve, etc.]
|
||||
|
||||
## Tools & Libraries
|
||||
[Preferred packages with versions that worked, and any to avoid]
|
||||
```
|
||||
|
||||
3. Only include patterns that appeared in 2+ spikes or were explicitly chosen by the user.
|
||||
|
||||
4. If `CONVENTIONS.md` already exists (append mode), update sections with new patterns. Remove entries contradicted by newer spikes.
|
||||
</step>
|
||||
|
||||
<step name="commit">
|
||||
Commit all artifacts (if `COMMIT_DOCS` is true):
|
||||
|
||||
```bash
|
||||
gsd-sdk query commit "docs(spike-wrap-up): package [N] spike findings into project skill" .planning/spikes/WRAP-UP-SUMMARY.md
|
||||
gsd-sdk query commit "docs(spike-wrap-up): package [N] spike findings into project skill" .planning/spikes/WRAP-UP-SUMMARY.md .planning/spikes/CONVENTIONS.md
|
||||
```
|
||||
</step>
|
||||
|
||||
@@ -232,29 +256,37 @@ gsd-sdk query commit "docs(spike-wrap-up): package [N] spike findings into proje
|
||||
GSD ► SPIKE WRAP-UP COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Curated:** {N} spikes ({included} included, {excluded} excluded)
|
||||
**Processed:** {N} spikes
|
||||
**Feature areas:** {list}
|
||||
**Skill:** `./.claude/skills/spike-findings-[project]/`
|
||||
**Conventions:** `.planning/spikes/CONVENTIONS.md`
|
||||
**Summary:** `.planning/spikes/WRAP-UP-SUMMARY.md`
|
||||
**CLAUDE.md:** routing line added
|
||||
|
||||
The spike-findings skill will auto-load in future build conversations.
|
||||
```
|
||||
</step>
|
||||
|
||||
<step name="whats_next">
|
||||
## What's Next
|
||||
|
||||
After the summary, present next-step options:
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
## ▶ Next Up
|
||||
|
||||
**Start building** — plan the real implementation
|
||||
**Explore frontier spikes** — see what else is worth spiking based on what we've learned
|
||||
|
||||
`/gsd-plan-phase`
|
||||
`/gsd-spike` (run with no argument — its frontier mode analyzes the spike landscape and proposes integration and frontier spikes)
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
**Also available:**
|
||||
- `/gsd-add-phase` — add a phase based on spike findings
|
||||
- `/gsd-spike` — spike additional ideas
|
||||
- `/gsd-plan-phase` — start planning the real implementation
|
||||
- `/gsd-spike [idea]` — spike a specific new idea
|
||||
- `/gsd-explore` — continue exploring
|
||||
- Other
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
</step>
|
||||
@@ -262,12 +294,13 @@ The spike-findings skill will auto-load in future build conversations.
|
||||
</process>
|
||||
|
||||
<success_criteria>
|
||||
- [ ] Every unprocessed spike presented for individual curation
|
||||
- [ ] Feature-area grouping proposed and approved
|
||||
- [ ] Spike-findings skill exists at `./.claude/skills/` with SKILL.md, references/, sources/
|
||||
- [ ] Core source files from included spikes copied into sources/
|
||||
- [ ] Reference files contain validated patterns, code snippets, landmines, constraints
|
||||
- [ ] All unprocessed spikes auto-included and processed
|
||||
- [ ] Spikes grouped by feature area
|
||||
- [ ] Spike-findings skill exists at `./.claude/skills/` with SKILL.md (including requirements), references/, sources/
|
||||
- [ ] Reference files are implementation blueprints with Requirements, How to Build It, What to Avoid, Constraints
|
||||
- [ ] `.planning/spikes/CONVENTIONS.md` created or updated with recurring stack/structure/pattern choices
|
||||
- [ ] `.planning/spikes/WRAP-UP-SUMMARY.md` written for project history
|
||||
- [ ] Project CLAUDE.md has auto-load routing line
|
||||
- [ ] Summary presented with next-step routing
|
||||
- [ ] Summary presented
|
||||
- [ ] Next-step options presented (including frontier spike exploration via `/gsd-spike`)
|
||||
</success_criteria>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<purpose>
|
||||
Rapid feasibility validation through focused, throwaway experiments. Each spike answers one
|
||||
specific question with observable evidence. Saves artifacts to `.planning/spikes/`.
|
||||
Companion to `/gsd-spike-wrap-up`.
|
||||
Spike an idea through experiential exploration — build focused experiments to feel the pieces
|
||||
of a future app, validate feasibility, and produce verified knowledge for the real build.
|
||||
Saves artifacts to `.planning/spikes/`. Companion to `/gsd-spike-wrap-up`.
|
||||
|
||||
Supports two modes:
|
||||
- **Idea mode** (default) — user describes an idea to spike
|
||||
- **Frontier mode** — no argument or "frontier" / "what should I spike?" — analyzes existing spike landscape and proposes integration and frontier spikes
|
||||
</purpose>
|
||||
|
||||
<required_reading>
|
||||
@@ -19,7 +23,63 @@ Read all files referenced by the invoking prompt's execution_context before star
|
||||
|
||||
Parse `$ARGUMENTS` for:
|
||||
- `--quick` flag → set `QUICK_MODE=true`
|
||||
- `--text` flag → set `TEXT_MODE=true`
|
||||
- `frontier` or empty → set `FRONTIER_MODE=true`
|
||||
- Remaining text → the idea to spike
|
||||
|
||||
**Text mode:** If TEXT_MODE is enabled, replace AskUserQuestion calls with plain-text numbered lists.
|
||||
</step>
|
||||
|
||||
<step name="route">
|
||||
## Routing
|
||||
|
||||
- **FRONTIER_MODE is true** → Jump to `frontier_mode`
|
||||
- **Otherwise** → Continue to `setup_directory`
|
||||
</step>
|
||||
|
||||
<step name="frontier_mode">
|
||||
## Frontier Mode — Propose What to Spike Next
|
||||
|
||||
### Load the Spike Landscape
|
||||
|
||||
If no `.planning/spikes/` directory exists, tell the user there's nothing to analyze and offer to start fresh with an idea instead.
|
||||
|
||||
Otherwise, load in this order:
|
||||
|
||||
**a. MANIFEST.md** — the overall idea, requirements, and spike table with verdicts.
|
||||
|
||||
**b. Findings skills** — glob `./.claude/skills/spike-findings-*/SKILL.md` and read any that exist, plus their `references/*.md`. These contain curated knowledge from prior wrap-ups.
|
||||
|
||||
**c. CONVENTIONS.md** — read `.planning/spikes/CONVENTIONS.md` if it exists. Established stack and patterns.
|
||||
|
||||
**d. All spike READMEs** — read `.planning/spikes/*/README.md` for verdicts, results, investigation trails, and tags.
|
||||
|
||||
### Analyze for Integration Spikes
|
||||
|
||||
Review every pair and cluster of VALIDATED spikes. Look for:
|
||||
|
||||
- **Shared resources:** Two spikes that both touch the same API, database, state, or data format but were tested independently.
|
||||
- **Data handoffs:** Spike A produces output that Spike B consumes. The formats were assumed compatible but never proven.
|
||||
- **Timing/ordering:** Spikes that work in isolation but have sequencing dependencies in the real flow.
|
||||
- **Resource contention:** Spikes that individually work but may compete for connections, memory, rate limits, or tokens when combined.
|
||||
|
||||
If integration risks exist, present them as concrete proposed spikes with names and Given/When/Then validation questions. If no meaningful integration risks exist, say so and skip this category.
|
||||
|
||||
### Analyze for Frontier Spikes
|
||||
|
||||
Think laterally about the overall idea from MANIFEST.md and what's been proven so far. Consider:
|
||||
|
||||
- **Gaps in the vision:** Capabilities assumed but unproven.
|
||||
- **Discovered dependencies:** Findings that reveal new questions.
|
||||
- **Alternative approaches:** Different angles for PARTIAL or INVALIDATED spikes.
|
||||
- **Adjacent capabilities:** Things that would meaningfully improve the idea if feasible.
|
||||
- **Comparison opportunities:** Approaches that worked but felt heavy.
|
||||
|
||||
Present frontier spikes as concrete proposals numbered from the highest existing spike number with Given/When/Then and risk ordering.
|
||||
|
||||
### Get Alignment and Execute
|
||||
|
||||
Present all integration and frontier candidates, then ask which to run. When the user picks spikes, write definitions into `.planning/spikes/MANIFEST.md` (appending to existing table) and proceed directly to building them starting at `research`.
|
||||
</step>
|
||||
|
||||
<step name="setup_directory">
|
||||
@@ -41,13 +101,16 @@ COMMIT_DOCS=$(gsd-sdk query config-get commit_docs 2>/dev/null || echo "true")
|
||||
</step>
|
||||
|
||||
<step name="detect_stack">
|
||||
Check for the project's tech stack to inform spike technology choices:
|
||||
Check for the project's tech stack to inform spike technology choices.
|
||||
|
||||
**Check conventions first.** If `.planning/spikes/CONVENTIONS.md` exists, follow its stack and patterns — these represent validated choices the user expects to see continued.
|
||||
|
||||
**Then check the project stack:**
|
||||
```bash
|
||||
ls package.json pyproject.toml Cargo.toml go.mod 2>/dev/null
|
||||
```
|
||||
|
||||
Use the project's language/framework by default. For greenfield projects with no existing stack, pick whatever gets to a runnable result fastest (Python, Node, Bash, single HTML file).
|
||||
Use the project's language/framework by default. For greenfield projects with no conventions and no existing stack, pick whatever gets to a runnable result fastest.
|
||||
|
||||
Avoid unless the spike specifically requires it:
|
||||
- Complex package management beyond `npm install` or `pip install`
|
||||
@@ -56,40 +119,53 @@ Avoid unless the spike specifically requires it:
|
||||
- Env files or config systems — hardcode everything
|
||||
</step>
|
||||
|
||||
<step name="load_prior_context">
|
||||
If `.planning/spikes/` has existing content, load context in this priority order:
|
||||
|
||||
**a. Conventions:** Read `.planning/spikes/CONVENTIONS.md` if it exists.
|
||||
|
||||
**b. Findings skills:** Glob for `./.claude/skills/spike-findings-*/SKILL.md` and read any that exist, plus their `references/*.md` files.
|
||||
|
||||
**c. Manifest:** Read `.planning/spikes/MANIFEST.md` for the index of all spikes.
|
||||
|
||||
**d. Related READMEs:** Based on the new idea, identify which prior spikes are related by matching tags, names, technologies, or domain overlap. Read only those `.planning/spikes/*/README.md` files. Skip unrelated ones.
|
||||
|
||||
Cross-reference against this full body of prior work:
|
||||
- **Skip already-validated questions.** Note the prior spike number and move on.
|
||||
- **Build on prior findings.** Don't repeat failed approaches. Use their Research and Results sections.
|
||||
- **Reuse prior research.** Carry findings forward rather than re-researching.
|
||||
- **Follow established conventions.** Mention any deviation.
|
||||
- **Call out relevant prior art** when presenting the decomposition.
|
||||
|
||||
If no `.planning/spikes/` exists, skip this step.
|
||||
</step>
|
||||
|
||||
<step name="decompose">
|
||||
**If `QUICK_MODE` is true:** Skip decomposition and alignment. Take the user's idea as a single spike question. Assign it spike number `001` (or next available). Jump to `build_spikes`.
|
||||
**If `QUICK_MODE` is true:** Skip decomposition and alignment. Take the user's idea as a single spike question. Assign it the next available number. Jump to `research`.
|
||||
|
||||
**Otherwise:**
|
||||
|
||||
Break the idea into 2-5 independent questions that each prove something specific. Frame each as an informal Given/When/Then. Present as a table:
|
||||
Break the idea into 2-5 independent questions. Frame each as Given/When/Then. Present as a table:
|
||||
|
||||
```
|
||||
| # | Spike | Validates (Given/When/Then) | Risk |
|
||||
|---|-------|-----------------------------|------|
|
||||
| 001 | websocket-streaming | Given a WS connection, when LLM streams tokens, then client receives chunks < 100ms | **High** |
|
||||
| 002 | pdf-extraction | Given a multi-page PDF, when parsed with pdfjs, then structured text is extractable | Medium |
|
||||
| # | Spike | Type | Validates (Given/When/Then) | Risk |
|
||||
|---|-------|------|-----------------------------|------|
|
||||
| 001 | websocket-streaming | standard | Given a WS connection, when LLM streams tokens, then client receives chunks < 100ms | **High** |
|
||||
| 002a | pdf-parse-pdfjs | comparison | Given a multi-page PDF, when parsed with pdfjs, then structured text is extractable | Medium |
|
||||
| 002b | pdf-parse-camelot | comparison | Given a multi-page PDF, when parsed with camelot, then structured text is extractable | Medium |
|
||||
```
|
||||
|
||||
Good spikes answer one specific feasibility question:
|
||||
- "Can we parse X format and extract Y?" — script that does it on a sample file
|
||||
- "How fast is X approach?" — benchmark with real-ish data
|
||||
- "Can we get X and Y to talk to each other?" — thinnest integration
|
||||
- "What does X feel like as a UI?" — minimal interactive prototype
|
||||
- "Does X API actually support Y?" — script that calls it and shows the response
|
||||
**Spike types:**
|
||||
- **standard** — one approach answering one question
|
||||
- **comparison** — same question, different approaches. Shared number with letter suffix.
|
||||
|
||||
Bad spikes are too broad or don't produce observable output:
|
||||
- "Set up the project" — not a question, just busywork
|
||||
- "Design the architecture" — planning, not spiking
|
||||
- "Build the backend" — too broad, no specific question
|
||||
Good spikes: specific feasibility questions with observable output.
|
||||
Bad spikes: too broad, no observable output, or just reading/planning.
|
||||
|
||||
Order by risk — the spike most likely to kill the idea runs first.
|
||||
Order by risk — most likely to kill the idea runs first.
|
||||
</step>
|
||||
|
||||
<step name="align">
|
||||
**If `QUICK_MODE` is true:** Skip.
|
||||
|
||||
Present the ordered spike list and ask which to build:
|
||||
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Decision Required ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
@@ -99,8 +175,33 @@ Present the ordered spike list and ask which to build:
|
||||
──────────────────────────────────────────────────────────────
|
||||
→ Build all in this order, or adjust the list?
|
||||
──────────────────────────────────────────────────────────────
|
||||
</step>
|
||||
|
||||
The user may reorder, merge, split, or skip spikes. Wait for alignment.
|
||||
<step name="research">
|
||||
## Research and Briefing Before Each Spike
|
||||
|
||||
This step runs **before each individual spike**, not once at the start.
|
||||
|
||||
**a. Present a spike briefing:**
|
||||
|
||||
> **Spike NNN: Descriptive Name**
|
||||
> [2-3 sentences: what this spike is, why it matters, key risk or unknown.]
|
||||
|
||||
**b. Research the current state of the art.** Use context7 (resolve-library-id → query-docs) for libraries/frameworks. Use web search for APIs/services without a context7 entry. Read actual documentation.
|
||||
|
||||
**c. Surface competing approaches** as a table:
|
||||
|
||||
| Approach | Tool/Library | Pros | Cons | Status |
|
||||
|----------|-------------|------|------|--------|
|
||||
| ... | ... | ... | ... | ... |
|
||||
|
||||
**Chosen approach:** [which one and why]
|
||||
|
||||
If 2+ credible approaches exist, plan to build quick variants within the spike and compare them.
|
||||
|
||||
**d. Capture research findings** in a `## Research` section in the README.
|
||||
|
||||
**Skip when unnecessary** for pure logic with no external dependencies.
|
||||
</step>
|
||||
|
||||
<step name="create_manifest">
|
||||
@@ -112,33 +213,75 @@ Create or update `.planning/spikes/MANIFEST.md`:
|
||||
## Idea
|
||||
[One paragraph describing the overall idea being explored]
|
||||
|
||||
## Requirements
|
||||
[Design decisions that emerged from the user's choices during spiking. Non-negotiable for the real build. Updated as spikes progress.]
|
||||
|
||||
- [e.g., "Must use streaming JSON output, not single-response"]
|
||||
- [e.g., "Must support reconnection on network failure"]
|
||||
|
||||
## Spikes
|
||||
|
||||
| # | Name | Validates | Verdict | Tags |
|
||||
|---|------|-----------|---------|------|
|
||||
| # | Name | Type | Validates | Verdict | Tags |
|
||||
|---|------|------|-----------|---------|------|
|
||||
```
|
||||
|
||||
If MANIFEST.md already exists, append new spikes to the existing table.
|
||||
**Track requirements as they emerge.** When the user expresses a preference during spiking, add it to the Requirements section immediately.
|
||||
</step>
|
||||
|
||||
<step name="reground">
|
||||
## Re-Ground Before Each Spike
|
||||
|
||||
Before starting each spike (not just the first), re-read `.planning/spikes/MANIFEST.md` and `.planning/spikes/CONVENTIONS.md` to prevent drift within long sessions. Check the Requirements section — make sure the spike doesn't contradict any established requirements.
|
||||
</step>
|
||||
|
||||
<step name="build_spikes">
|
||||
Build each spike sequentially, highest-risk first.
|
||||
## Build Each Spike Sequentially
|
||||
|
||||
**Depth over speed.** The goal is genuine understanding, not a quick verdict. Never declare VALIDATED after a single happy-path test. Follow surprising findings. Test edge cases. Document the investigation trail, not just the conclusion.
|
||||
|
||||
**Comparison spikes** use shared number with letter suffix: `NNN-a-name` / `NNN-b-name`. Build back-to-back, then head-to-head comparison.
|
||||
|
||||
### For Each Spike:
|
||||
|
||||
**a.** Find next available number by checking existing `.planning/spikes/NNN-*/` directories.
|
||||
Format: three-digit zero-padded + hyphenated descriptive name.
|
||||
**a.** Create `.planning/spikes/NNN-descriptive-name/`
|
||||
|
||||
**b.** Create the spike directory: `.planning/spikes/NNN-descriptive-name/`
|
||||
**b.** Default to giving the user something they can experience. The bias should be toward building a simple UI or interactive demo, not toward stdout that only Claude reads. The user wants to *feel* the spike working, not just be told it works.
|
||||
|
||||
**c.** Build the minimum code that answers the spike's question. Every line must serve the question — nothing incidental. If auth isn't the question, hardcode a token. If the database isn't the question, use a JSON file. Strip everything that doesn't directly answer "does X work?"
|
||||
**The default is: build something the user can interact with.** This could be:
|
||||
- A simple HTML page that shows the result visually
|
||||
- A web UI with a button that triggers the action and shows the response
|
||||
- A page that displays data flowing through a pipeline
|
||||
- A minimal interface where the user can try different inputs and see outputs
|
||||
|
||||
**d.** Write `README.md` with YAML frontmatter:
|
||||
**Only fall back to stdout/CLI verification when the spike is genuinely about a fact, not a feeling:**
|
||||
- Pure data transformation where the answer is "yes it parses correctly"
|
||||
- Binary yes/no questions (does this API authenticate? does this library exist?)
|
||||
- Benchmark numbers (how fast is X? how much memory does Y use?)
|
||||
|
||||
When in doubt, build the UI. It takes a few extra minutes but produces a spike the user can actually demo and feel confident about.
|
||||
|
||||
**If the spike needs runtime observability,** build a forensic log layer:
|
||||
1. Event log array with ISO timestamps and category tags
|
||||
2. Export mechanism (server: GET endpoint, CLI: JSON file, browser: Export button)
|
||||
3. Log summary (event counts, duration, errors, metadata)
|
||||
4. Analysis helpers if volume warrants it
|
||||
|
||||
**c.** Build the code. Start with simplest version, then deepen.
|
||||
|
||||
**d.** Iterate when findings warrant it:
|
||||
- **Surprising surface?** Write a follow-up test that isolates and explores it.
|
||||
- **Answer feels shallow?** Probe edge cases — large inputs, concurrent requests, malformed data, network failures.
|
||||
- **Assumption wrong?** Adjust. Note the pivot in the README.
|
||||
|
||||
Multiple files per spike are expected for complex questions (e.g., `test-basic.js`, `test-edge-cases.js`, `benchmark.js`).
|
||||
|
||||
**e.** Write `README.md` with YAML frontmatter:
|
||||
|
||||
```markdown
|
||||
---
|
||||
spike: NNN
|
||||
name: descriptive-name
|
||||
type: standard
|
||||
validates: "Given [precondition], when [action], then [expected outcome]"
|
||||
verdict: PENDING
|
||||
related: []
|
||||
@@ -148,30 +291,38 @@ tags: [tag1, tag2]
|
||||
# Spike NNN: Descriptive Name
|
||||
|
||||
## What This Validates
|
||||
[The specific feasibility question, framed as Given/When/Then]
|
||||
[Given/When/Then]
|
||||
|
||||
## Research
|
||||
[Docs checked, approach comparison table, chosen approach, gotchas. Omit if no external deps.]
|
||||
|
||||
## How to Run
|
||||
[Single command or short sequence to run the spike]
|
||||
[Command(s)]
|
||||
|
||||
## What to Expect
|
||||
[Concrete observable outcomes: "When you click X, you should see Y within Z seconds"]
|
||||
[Concrete observable outcomes]
|
||||
|
||||
## Observability
|
||||
[If forensic log layer exists. Omit otherwise.]
|
||||
|
||||
## Investigation Trail
|
||||
[Updated as spike progresses. Document each iteration: what tried, what revealed, what tried next.]
|
||||
|
||||
## Results
|
||||
[Filled in after running — verdict, evidence, surprises]
|
||||
[Verdict, evidence, surprises, log analysis findings.]
|
||||
```
|
||||
|
||||
**e.** Auto-link related spikes: read existing spike READMEs and infer relationships from tags, names, and descriptions. Write the `related` field silently.
|
||||
**f.** Auto-link related spikes silently.
|
||||
|
||||
**f.** Run and verify:
|
||||
- If self-verifiable: run it, check output, update README verdict and Results section
|
||||
- If needs human judgment: run it, present instructions using a checkpoint box:
|
||||
**g.** Run and verify:
|
||||
- Self-verifiable: run, iterate if findings warrant deeper investigation, update verdict
|
||||
- Needs human judgment: present checkpoint box:
|
||||
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Verification Required ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
|
||||
**Spike {NNN}: {name}**
|
||||
|
||||
**How to run:** {command}
|
||||
**What to expect:** {concrete outcomes}
|
||||
|
||||
@@ -179,43 +330,69 @@ tags: [tag1, tag2]
|
||||
→ Does this match what you expected? Describe what you see.
|
||||
──────────────────────────────────────────────────────────────
|
||||
|
||||
**g.** Update verdict to VALIDATED / INVALIDATED / PARTIAL. Update Results section with evidence.
|
||||
|
||||
**h.** Update `.planning/spikes/MANIFEST.md` with the spike's row.
|
||||
|
||||
**i.** Commit (if `COMMIT_DOCS` is true):
|
||||
```bash
|
||||
gsd-sdk query commit "docs(spike-NNN): [VERDICT] — [key finding in one sentence]" .planning/spikes/NNN-descriptive-name/ .planning/spikes/MANIFEST.md
|
||||
gsd-sdk query commit "docs(spike-NNN): [VERDICT] — [key finding]" .planning/spikes/NNN-descriptive-name/ .planning/spikes/MANIFEST.md
|
||||
```
|
||||
|
||||
**j.** Report before moving to next spike:
|
||||
**j.** Report:
|
||||
```
|
||||
◆ Spike NNN: {name}
|
||||
Verdict: {VALIDATED ✓ / INVALIDATED ✗ / PARTIAL ⚠}
|
||||
Finding: {one sentence}
|
||||
Impact: {effect on remaining spikes, if any}
|
||||
Key findings: {not just verdict — investigation trail, surprises, edge cases explored}
|
||||
Impact: {effect on remaining spikes}
|
||||
```
|
||||
|
||||
**k.** If a spike invalidates a core assumption: stop and present:
|
||||
Do not rush to a verdict. A spike that says "VALIDATED — it works" with no nuance is almost always incomplete.
|
||||
|
||||
**k.** If core assumption invalidated:
|
||||
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Decision Required ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
|
||||
Core assumption invalidated by Spike {NNN}.
|
||||
|
||||
{what was invalidated and why}
|
||||
|
||||
──────────────────────────────────────────────────────────────
|
||||
→ Continue with remaining spikes / Pivot approach / Abandon
|
||||
──────────────────────────────────────────────────────────────
|
||||
</step>
|
||||
|
||||
Only proceed if the user says to.
|
||||
<step name="update_conventions">
|
||||
## Update Conventions
|
||||
|
||||
After all spikes in this session are built, update `.planning/spikes/CONVENTIONS.md` with patterns that emerged or solidified.
|
||||
|
||||
```markdown
|
||||
# Spike Conventions
|
||||
|
||||
Patterns and stack choices established across spike sessions. New spikes follow these unless the question requires otherwise.
|
||||
|
||||
## Stack
|
||||
[What we use for frontend, backend, scripts, and why]
|
||||
|
||||
## Structure
|
||||
[Common file layouts, port assignments, naming patterns]
|
||||
|
||||
## Patterns
|
||||
[Recurring approaches: how we handle auth, how we style, how we serve]
|
||||
|
||||
## Tools & Libraries
|
||||
[Preferred packages with versions that worked, and any to avoid]
|
||||
```
|
||||
|
||||
Only include patterns that repeated across 2+ spikes or were explicitly chosen by the user. If `CONVENTIONS.md` already exists, update sections with new patterns from this session.
|
||||
|
||||
Commit (if `COMMIT_DOCS` is true):
|
||||
```bash
|
||||
gsd-sdk query commit "docs(spikes): update conventions" .planning/spikes/CONVENTIONS.md
|
||||
```
|
||||
</step>
|
||||
|
||||
<step name="report">
|
||||
After all spikes complete, present the consolidated report:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SPIKE COMPLETE ✓
|
||||
@@ -223,35 +400,35 @@ After all spikes complete, present the consolidated report:
|
||||
|
||||
## Verdicts
|
||||
|
||||
| # | Name | Verdict |
|
||||
|---|------|---------|
|
||||
| 001 | {name} | ✓ VALIDATED |
|
||||
| 002 | {name} | ✗ INVALIDATED |
|
||||
| # | Name | Type | Verdict |
|
||||
|---|------|------|---------|
|
||||
| 001 | {name} | standard | ✓ VALIDATED |
|
||||
| 002a | {name} | comparison | ✓ WINNER |
|
||||
|
||||
## Key Discoveries
|
||||
{surprises, gotchas, things that weren't expected}
|
||||
{surprises, gotchas, investigation trail highlights}
|
||||
|
||||
## Feasibility Assessment
|
||||
{overall, is the idea viable?}
|
||||
{overall viability}
|
||||
|
||||
## Signal for the Build
|
||||
{what the real implementation should use, avoid, or watch out for}
|
||||
{what to use, avoid, watch out for}
|
||||
```
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
## ▶ Next Up
|
||||
|
||||
**Package findings** — wrap spike knowledge into a reusable skill
|
||||
**Package findings** — wrap spike knowledge into an implementation blueprint
|
||||
|
||||
`/gsd-spike-wrap-up`
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
|
||||
**Also available:**
|
||||
- `/gsd-spike` — spike more ideas (or run with no argument for frontier mode)
|
||||
- `/gsd-plan-phase` — start planning the real implementation
|
||||
- `/gsd-explore` — continue exploring the idea
|
||||
- `/gsd-add-phase` — add a phase to the roadmap based on findings
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
</step>
|
||||
@@ -260,11 +437,16 @@ After all spikes complete, present the consolidated report:
|
||||
|
||||
<success_criteria>
|
||||
- [ ] `.planning/spikes/` created (auto-creates if needed, no project init required)
|
||||
- [ ] Each spike answers one specific question with observable evidence
|
||||
- [ ] Each spike README has complete frontmatter, run instructions, and results
|
||||
- [ ] User verified each spike (self-verified or human checkpoint)
|
||||
- [ ] MANIFEST.md is current
|
||||
- [ ] Prior spikes and findings skills consulted before building
|
||||
- [ ] Conventions followed (or deviation documented)
|
||||
- [ ] Research grounded each spike in current docs before coding
|
||||
- [ ] Depth over speed — edge cases tested, surprising findings followed, investigation trail documented
|
||||
- [ ] Comparison spikes built back-to-back with head-to-head verdict
|
||||
- [ ] Spikes needing human interaction have forensic log layer
|
||||
- [ ] Requirements tracked in MANIFEST.md as they emerge from user choices
|
||||
- [ ] CONVENTIONS.md created or updated with patterns that emerged
|
||||
- [ ] Each spike README has complete frontmatter, Investigation Trail, and Results
|
||||
- [ ] MANIFEST.md is current (with Type column and Requirements section)
|
||||
- [ ] Commits use `docs(spike-NNN): [VERDICT]` format
|
||||
- [ ] Consolidated report presented with next-step routing
|
||||
- [ ] If core assumption invalidated, execution stopped and user consulted
|
||||
</success_criteria>
|
||||
|
||||
182
get-shit-done/workflows/sync-skills.md
Normal file
182
get-shit-done/workflows/sync-skills.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# sync-skills — Cross-Runtime GSD Skill Sync
|
||||
|
||||
**Command:** `/gsd-sync-skills`
|
||||
|
||||
Sync managed `gsd-*` skill directories from one canonical runtime's skills root to one or more destination runtime skills roots. Keeps multi-runtime installs aligned after a `gsd-update` on one runtime.
|
||||
|
||||
---
|
||||
|
||||
## Arguments
|
||||
|
||||
| Flag | Required | Default | Description |
|
||||
|------|----------|---------|-------------|
|
||||
| `--from <runtime>` | Yes | *(none)* | Source runtime — the canonical runtime to copy from |
|
||||
| `--to <runtime\|all>` | Yes | *(none)* | Destination runtime or `all` supported runtimes |
|
||||
| `--dry-run` | No | *on by default* | Preview changes without writing anything |
|
||||
| `--apply` | No | *off* | Execute the diff (overrides dry-run) |
|
||||
|
||||
If neither `--dry-run` nor `--apply` is specified, dry-run is the default.
|
||||
|
||||
**Supported runtime names:** `claude`, `codex`, `copilot`, `cursor`, `windsurf`, `opencode`, `gemini`, `kilo`, `augment`, `trae`, `qwen`, `codebuddy`, `cline`, `antigravity`
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Parse Arguments
|
||||
|
||||
```bash
|
||||
FROM_RUNTIME=""
|
||||
TO_RUNTIMES=()
|
||||
IS_APPLY=false
|
||||
|
||||
# Parse --from
|
||||
if [[ "$@" == *"--from"* ]]; then
|
||||
FROM_RUNTIME=$(echo "$@" | grep -oP '(?<=--from )\S+')
|
||||
fi
|
||||
|
||||
# Parse --to
|
||||
if [[ "$@" == *"--to all"* ]]; then
|
||||
TO_RUNTIMES=(claude codex copilot cursor windsurf opencode gemini kilo augment trae qwen codebuddy cline antigravity)
|
||||
elif [[ "$@" == *"--to"* ]]; then
|
||||
TO_RUNTIMES=( $(echo "$@" | grep -oP '(?<=--to )\S+') )
|
||||
fi
|
||||
|
||||
# Parse --apply
|
||||
if [[ "$@" == *"--apply"* ]]; then
|
||||
IS_APPLY=true
|
||||
fi
|
||||
```
|
||||
|
||||
**Validation:**
|
||||
- If `--from` is missing or unrecognized: print error and exit
|
||||
- If `--to` is missing or unrecognized: print error and exit
|
||||
- If `--from` == `--to` (single destination): print `[no-op: source and destination are the same runtime]` and exit
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Resolve Skills Roots
|
||||
|
||||
Use `install.js --skills-root` to resolve paths — this reuses the single authoritative path table rather than duplicating it:
|
||||
|
||||
```bash
|
||||
INSTALL_JS="$(dirname "$0")/../get-shit-done/bin/install.js"
|
||||
# If running from a global install, resolve relative to the GSD package
|
||||
INSTALL_JS_GLOBAL="$HOME/.claude/get-shit-done/bin/install.js"
|
||||
[[ ! -f "$INSTALL_JS" ]] && INSTALL_JS="$INSTALL_JS_GLOBAL"
|
||||
|
||||
SRC_SKILLS_ROOT=$(node "$INSTALL_JS" --skills-root "$FROM_RUNTIME")
|
||||
|
||||
for DEST_RUNTIME in "${TO_RUNTIMES[@]}"; do
|
||||
DEST_SKILLS_ROOTS["$DEST_RUNTIME"]=$(node "$INSTALL_JS" --skills-root "$DEST_RUNTIME")
|
||||
done
|
||||
```
|
||||
|
||||
**Guard:** If the source skills root does not exist, print:
|
||||
```
|
||||
error: source skills root not found: <path>
|
||||
Is GSD installed globally for the '<runtime>' runtime?
|
||||
Run: node ~/.claude/get-shit-done/bin/install.js --global --<runtime>
|
||||
```
|
||||
Then exit.
|
||||
|
||||
**Guard:** If `--to` contains the same runtime as `--from`, skip that destination silently.
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Compute Diff Per Destination
|
||||
|
||||
For each destination runtime:
|
||||
|
||||
```bash
|
||||
# List gsd-* subdirectories in source
|
||||
SRC_SKILLS=$(ls -1 "$SRC_SKILLS_ROOT" 2>/dev/null | grep '^gsd-')
|
||||
|
||||
# List gsd-* subdirectories in destination (may not exist yet)
|
||||
DST_SKILLS=$(ls -1 "$DEST_ROOT" 2>/dev/null | grep '^gsd-')
|
||||
|
||||
# Diff:
|
||||
# CREATE — in SRC but not in DST
|
||||
# UPDATE — in both; content differs (compare recursively via checksums)
|
||||
# REMOVE — in DST but not in SRC (stale GSD skill no longer in source)
|
||||
# SKIP — in both; content identical (already up to date)
|
||||
```
|
||||
|
||||
**Non-GSD preservation:** Only `gsd-*` entries are ever created, updated, or removed. Entries in the destination that do not start with `gsd-` are never touched.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Print Diff Report
|
||||
|
||||
Always print the report, regardless of `--apply` or `--dry-run`:
|
||||
|
||||
```
|
||||
sync source: <runtime> (<src_skills_root>)
|
||||
sync targets: <dest1>, <dest2>
|
||||
|
||||
== <dest1> (<dest1_skills_root>) ==
|
||||
CREATE: gsd-help
|
||||
UPDATE: gsd-update
|
||||
REMOVE: gsd-old-command
|
||||
SKIP: gsd-plan-phase (up to date)
|
||||
(N changes)
|
||||
|
||||
== <dest2> (<dest2_skills_root>) ==
|
||||
CREATE: gsd-help
|
||||
(N changes)
|
||||
|
||||
dry-run only. use --apply to execute. ← omit this line if --apply
|
||||
```
|
||||
|
||||
If a destination root does not exist and `--apply` is true, print `CREATE DIR: <path>` before its entries.
|
||||
|
||||
If all destinations are already up to date:
|
||||
```
|
||||
All destinations are up to date. No changes needed.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Execute (only when --apply)
|
||||
|
||||
If `--dry-run` (or no flag): skip this step entirely and exit after printing the report.
|
||||
|
||||
For each destination with changes:
|
||||
|
||||
```bash
|
||||
mkdir -p "$DEST_ROOT"
|
||||
|
||||
for SKILL in $CREATE_LIST $UPDATE_LIST; do
|
||||
rm -rf "$DEST_ROOT/$SKILL"
|
||||
cp -r "$SRC_SKILLS_ROOT/$SKILL" "$DEST_ROOT/$SKILL"
|
||||
done
|
||||
|
||||
for SKILL in $REMOVE_LIST; do
|
||||
rm -rf "$DEST_ROOT/$SKILL"
|
||||
done
|
||||
```
|
||||
|
||||
**Idempotency:** Running `--apply` a second time with no intervening changes must report zero changes (all entries are SKIP).
|
||||
|
||||
**Atomicity:** Each skill directory is replaced as a unit (remove then copy). Partial updates of individual files within a skill are not performed — the whole directory is replaced.
|
||||
|
||||
After executing all destinations:
|
||||
|
||||
```
|
||||
Sync complete: <N> skills synced to <M> runtime(s).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Safety Rules
|
||||
|
||||
1. **Only `gsd-*` directories** are created, updated, or removed. Any directory not starting with `gsd-` in a destination root is untouched.
|
||||
2. **Dry-run is the default.** `--apply` must be passed explicitly to write anything.
|
||||
3. **Source root must exist.** Never create the source root; it must have been created by a prior `gsd-update` or installer run.
|
||||
4. **No cross-runtime content transformation.** Sync copies files verbatim. It does not apply runtime-specific content transformations (those happen at install time). If a runtime requires transformed content (e.g. Augment's format differs), the developer should run the installer for that runtime instead of using sync.
|
||||
|
||||
---
|
||||
|
||||
## Limitations
|
||||
|
||||
- Sync copies files verbatim and does not apply runtime-specific content transformations. Use the GSD installer directly for runtimes that require format conversion.
|
||||
- Cross-project skills (`.agents/skills/`) are out of scope — this command only touches global runtime skills roots.
|
||||
- Bidirectional sync is not supported. Choose one canonical source with `--from`.
|
||||
@@ -271,6 +271,28 @@ After (Phase 2 shipped JWT auth, discovered rate limiting needed):
|
||||
|
||||
</step>
|
||||
|
||||
<step name="graduation_scan">
|
||||
|
||||
Scan LEARNINGS.md files from recent phases for recurring patterns and surface promotion candidates to the developer.
|
||||
|
||||
**Invoke the graduation helper:**
|
||||
|
||||
```text
|
||||
@~/.claude/get-shit-done/workflows/graduation.md
|
||||
```
|
||||
|
||||
This step is fully delegated to `graduation.md`. It handles guard checks (feature flag, window size, threshold), clustering, backlog filtering, HITL prompting, promotion writes, and STATE.md updates.
|
||||
|
||||
**This step is always non-blocking:** graduation candidates are surfaced for the developer's decision; no action is required to continue the transition. If the graduation scan produces no qualifying clusters, it prints a single `[graduation: no qualifying clusters]` line and returns.
|
||||
|
||||
**Step complete when:**
|
||||
|
||||
- [ ] graduation.md guard checks passed (or skipped with silent no-op)
|
||||
- [ ] Recurring clusters surfaced (or `[graduation: no qualifying clusters]` printed)
|
||||
- [ ] Each cluster resolved as Promote / Defer / Dismiss (or all skipped)
|
||||
|
||||
</step>
|
||||
|
||||
<step name="update_current_position_after_transition">
|
||||
|
||||
**Note:** Basic position updates (Current Phase, Status, Current Plan, Last Activity) were already handled by `gsd-sdk query phase.complete` in the update_roadmap_and_state step.
|
||||
|
||||
@@ -388,14 +388,15 @@ installer does not know about and will delete during the wipe.
|
||||
**Do not use bash path-stripping (`${filepath#$RUNTIME_DIR/}`) or `node -e require()`
|
||||
inline** — those patterns fail when `$RUNTIME_DIR` is unset and the stripped
|
||||
relative path may not match manifest key format, which causes CUSTOM_COUNT=0
|
||||
even when custom files exist (bug #1997). Use `gsd-tools detect-custom-files`
|
||||
instead, which resolves paths reliably with Node.js `path.relative()`.
|
||||
even when custom files exist (bug #1997). Use `gsd-sdk query detect-custom-files`
|
||||
when `gsd-sdk` is on `PATH`, or the bundled `gsd-tools.cjs detect-custom-files`
|
||||
otherwise — both resolve paths reliably with Node.js `path.relative()`.
|
||||
|
||||
First, resolve the config directory (`RUNTIME_DIR`) from the install scope
|
||||
detected in `get_installed_version`:
|
||||
|
||||
```bash
|
||||
# RUNTIME_DIR is the resolved config directory (e.g. ~/.claude, ~/.config/opencode)
|
||||
# RUNTIME_DIR is the resolved config directory (e.g. ~/.config/opencode, ~/.gemini)
|
||||
# It should already be set from get_installed_version as GLOBAL_DIR or LOCAL_DIR.
|
||||
# Use the appropriate variable based on INSTALL_SCOPE.
|
||||
if [ "$INSTALL_SCOPE" = "LOCAL" ]; then
|
||||
@@ -410,17 +411,20 @@ fi
|
||||
If `RUNTIME_DIR` is empty or does not exist, skip this step (no config dir to
|
||||
inspect).
|
||||
|
||||
Otherwise, resolve the path to `gsd-tools.cjs` and run:
|
||||
Otherwise run `detect-custom-files` (prefer SDK when available):
|
||||
|
||||
```bash
|
||||
GSD_TOOLS="$RUNTIME_DIR/get-shit-done/bin/gsd-tools.cjs"
|
||||
if [ -f "$GSD_TOOLS" ] && [ -n "$RUNTIME_DIR" ]; then
|
||||
CUSTOM_JSON=''
|
||||
if [ -n "$RUNTIME_DIR" ] && command -v gsd-sdk >/dev/null 2>&1; then
|
||||
CUSTOM_JSON=$(gsd-sdk query detect-custom-files --config-dir "$RUNTIME_DIR" 2>/dev/null)
|
||||
elif [ -f "$GSD_TOOLS" ] && [ -n "$RUNTIME_DIR" ]; then
|
||||
CUSTOM_JSON=$(node "$GSD_TOOLS" detect-custom-files --config-dir "$RUNTIME_DIR" 2>/dev/null)
|
||||
CUSTOM_COUNT=$(echo "$CUSTOM_JSON" | node -e "process.stdin.resume();let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{console.log(JSON.parse(d).custom_count);}catch{console.log(0);}})" 2>/dev/null || echo "0")
|
||||
else
|
||||
CUSTOM_COUNT=0
|
||||
fi
|
||||
if [ -z "$CUSTOM_JSON" ]; then
|
||||
CUSTOM_JSON='{"custom_files":[],"custom_count":0}'
|
||||
fi
|
||||
CUSTOM_COUNT=$(echo "$CUSTOM_JSON" | node -e "process.stdin.resume();let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{console.log(JSON.parse(d).custom_count);}catch{console.log(0);}})" 2>/dev/null || echo "0")
|
||||
```
|
||||
|
||||
**If `CUSTOM_COUNT` > 0:**
|
||||
|
||||
@@ -197,22 +197,29 @@ inspecting static artifacts.
|
||||
**Step 1: Run test suite**
|
||||
|
||||
```bash
|
||||
# Resolve test command: project config > Makefile > language sniff
|
||||
TEST_CMD=$(gsd-sdk query config-get workflow.test_command --default "" 2>/dev/null || true)
|
||||
if [ -z "$TEST_CMD" ]; then
|
||||
if [ -f "Makefile" ] && grep -q "^test:" Makefile; then
|
||||
TEST_CMD="make test"
|
||||
elif [ -f "Justfile" ] || [ -f "justfile" ]; then
|
||||
TEST_CMD="just test"
|
||||
elif [ -f "package.json" ]; then
|
||||
TEST_CMD="npm test"
|
||||
elif [ -f "Cargo.toml" ]; then
|
||||
TEST_CMD="cargo test"
|
||||
elif [ -f "go.mod" ]; then
|
||||
TEST_CMD="go test ./..."
|
||||
elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then
|
||||
TEST_CMD="python -m pytest -q --tb=short 2>&1 || uv run python -m pytest -q --tb=short"
|
||||
else
|
||||
TEST_CMD="false"
|
||||
echo "⚠ No test runner detected — skipping test suite"
|
||||
fi
|
||||
fi
|
||||
# Detect test runner and run all tests (timeout: 5 minutes)
|
||||
TEST_EXIT=0
|
||||
timeout 300 bash -c '
|
||||
if [ -f "package.json" ]; then
|
||||
npm test 2>&1
|
||||
elif [ -f "Cargo.toml" ]; then
|
||||
cargo test 2>&1
|
||||
elif [ -f "go.mod" ]; then
|
||||
go test ./... 2>&1
|
||||
elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then
|
||||
python -m pytest -q --tb=short 2>&1 || uv run python -m pytest -q --tb=short 2>&1
|
||||
else
|
||||
echo "⚠ No test runner detected — skipping test suite"
|
||||
exit 1
|
||||
fi
|
||||
'
|
||||
timeout 300 bash -c "$TEST_CMD" 2>&1
|
||||
TEST_EXIT=$?
|
||||
if [ "${TEST_EXIT}" -eq 0 ]; then
|
||||
echo "✓ Test suite passed"
|
||||
@@ -367,9 +374,34 @@ If a requirement specifies a quantity of test cases (e.g., "30 calculations"), c
|
||||
</step>
|
||||
|
||||
<step name="identify_human_verification">
|
||||
**Always needs human:** Visual appearance, user flow completion, real-time behavior (WebSocket/SSE), external service integration, performance feel, error message clarity.
|
||||
**First: determine if this is an infrastructure/foundation phase.**
|
||||
|
||||
**Needs human if uncertain:** Complex wiring grep can't trace, dynamic state-dependent behavior, edge cases.
|
||||
Infrastructure and foundation phases — code foundations, database schema, internal APIs, data models, build tooling, CI/CD, internal service integrations — have no user-facing elements by definition. For these phases:
|
||||
|
||||
- Do NOT invent artificial manual steps (e.g., "manually run git commits", "manually invoke methods", "manually check database state").
|
||||
- Mark human verification as **N/A** with rationale: "Infrastructure/foundation phase — no user-facing elements to test manually."
|
||||
- Set `human_verification: []` and do **not** produce a `human_needed` status solely due to lack of user-facing features.
|
||||
- Only add human verification items if the phase goal or success criteria explicitly describe something a user would interact with (UI, CLI command output visible to end users, external service UX).
|
||||
|
||||
**How to determine if a phase is infrastructure/foundation:**
|
||||
- Phase goal or name contains: "foundation", "infrastructure", "schema", "database", "internal API", "data model", "scaffolding", "pipeline", "tooling", "CI", "migrations", "service layer", "backend", "core library"
|
||||
- Phase success criteria describe only technical artifacts (files exist, tests pass, schema is valid) with no user interaction required
|
||||
- There is no UI, CLI output visible to end users, or real-time behavior to observe
|
||||
|
||||
**If the phase IS infrastructure/foundation:** auto-pass UAT — skip the human verification items list entirely. Log:
|
||||
|
||||
```markdown
|
||||
## Human Verification
|
||||
|
||||
N/A — Infrastructure/foundation phase with no user-facing elements.
|
||||
All acceptance criteria are verifiable programmatically.
|
||||
```
|
||||
|
||||
**If the phase IS user-facing:** Only flag items that genuinely require a human. Do not invent steps.
|
||||
|
||||
**Always needs human (user-facing phases only):** Visual appearance, user flow completion, real-time behavior (WebSocket/SSE), external service integration, performance feel, error message clarity.
|
||||
|
||||
**Needs human if uncertain (user-facing phases only):** Complex wiring grep can't trace, dynamic state-dependent behavior, edge cases.
|
||||
|
||||
Format each as: Test Name → What to do → Expected result → Why can't verify programmatically.
|
||||
</step>
|
||||
|
||||
@@ -464,7 +464,7 @@ Run phase artifact scan to surface any open items before marking phase verified:
|
||||
`audit-open` is CJS-only until registered on `gsd-sdk query`:
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" audit-open --json 2>/dev/null
|
||||
gsd-sdk query audit-open --json 2>/dev/null
|
||||
```
|
||||
|
||||
Parse the JSON output. For the CURRENT PHASE ONLY, surface:
|
||||
|
||||
@@ -147,10 +147,15 @@ function runStatusline() {
|
||||
if (sessionSafe) {
|
||||
try {
|
||||
const bridgePath = path.join(os.tmpdir(), `claude-ctx-${session}.json`);
|
||||
// used_pct written to the bridge must match CC's native /context reporting:
|
||||
// raw used = 100 - remaining_percentage (no buffer normalization applied).
|
||||
// The normalized `used` value is correct for the statusline progress bar but
|
||||
// inflates the context monitor warning messages by ~13 points (#2451).
|
||||
const rawUsedPct = Math.round(100 - remaining);
|
||||
const bridgeData = JSON.stringify({
|
||||
session_id: session,
|
||||
remaining_percentage: remaining,
|
||||
used_pct: used,
|
||||
used_pct: rawUsedPct,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
fs.writeFileSync(bridgePath, bridgeData);
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "get-shit-done-cc",
|
||||
"version": "1.37.1",
|
||||
"version": "1.38.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "get-shit-done-cc",
|
||||
"version": "1.37.1",
|
||||
"version": "1.38.2",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"get-shit-done-cc": "bin/install.js"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "get-shit-done-cc",
|
||||
"version": "1.37.1",
|
||||
"version": "1.38.2",
|
||||
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, Gemini and Codex by TÂCHES.",
|
||||
"bin": {
|
||||
"get-shit-done-cc": "bin/install.js"
|
||||
|
||||
237
sdk/HANDOVER-GOLDEN-PARITY.md
Normal file
237
sdk/HANDOVER-GOLDEN-PARITY.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# Handover: Query layer + golden parity
|
||||
|
||||
Use this document at the start of a new session so work continues in context without re-deriving history.
|
||||
|
||||
**Related:** `HANDOVER-PARITY-DOCS.md` (#2302 scope); **`sdk/src/query/QUERY-HANDLERS.md`** (golden matrix, CJS↔SDK routing).
|
||||
|
||||
---
|
||||
|
||||
## Goal for the next session (primary)
|
||||
|
||||
**Track A (Golden/parity) is complete.** 127/128 canonicals covered — the single exception (`phases.archive`) is permanent (SDK-only, no CJS analogue). Focus shifts to the remaining #2302 acceptance criteria.
|
||||
|
||||
**Ongoing:** pick next gap from **`GOLDEN_PARITY_EXCEPTIONS`** / registry orphans (run `golden-policy.test.ts`) or expand **`READ_ONLY_JSON_PARITY_ROWS`** for read-only handlers still on generic exceptions. The read-only batch in **§ Next batch** below is **done**.
|
||||
|
||||
**Follow-up:** confirm **`GOLDEN_PARITY_EXCEPTIONS`** for any remaining read-only registry gaps (`learnings.query`, `progress.bar`, `profile-questionnaire` — still exception-only until strict rows); extend **`read-only-golden-rows.ts`** when aligned.
|
||||
|
||||
### Remaining work — ordered by priority
|
||||
|
||||
1. **Track C — Runner alignment** (not started)
|
||||
- `PhaseRunner` and `InitRunner` both take `GSDTools` (subprocess bridge) as a `tools` dependency (`phase-runner.ts:55`, `init-runner.ts:70`).
|
||||
- Issue #2302 says: "Align programmatic paths with the same contracts as query handlers (shared helpers or registry dispatch), **without** removing `GSDTools`."
|
||||
- Concretely: where runners currently shell out via `GSDTools.run('state update …')`, they could call the typed handler (`stateUpdate()`) directly or dispatch through `createRegistry()`. This eliminates subprocess overhead on the hot path while keeping `GSDTools` exported for backward compatibility.
|
||||
- Files to touch: `sdk/src/phase-runner.ts`, `sdk/src/init-runner.ts`, `sdk/src/index.ts` (re-exports). Tests: `phase-runner.integration.test.ts`, `init-e2e.integration.test.ts`, `lifecycle-e2e.integration.test.ts`.
|
||||
- **Risk:** Runner integration tests are slow and sensitive to state. Approach: swap one `tools.run()` call at a time, verify the integration test still passes, then proceed to the next.
|
||||
|
||||
2. **Track B — CHANGELOG.md [Unreleased] entries** (not started)
|
||||
- `CHANGELOG.md` has an `[Unreleased]` section but no Phase 3 entries yet.
|
||||
- Add entries covering: golden parity policy gate, mutation subprocess infrastructure, handler alignment, profile-output port, CJS deprecation header.
|
||||
- `docs/CLI-TOOLS.md` already references `QUERY-HANDLERS.md` and SDK query layer — may need minor polish but is substantively done.
|
||||
- `QUERY-HANDLERS.md` is maintained and current.
|
||||
|
||||
3. **Track D — CJS deprecation headers** (done)
|
||||
- `gsd-tools.cjs` already has `@deprecated` JSDoc header (lines 3-6) pointing to `gsd-sdk query` and `@gsd-build/sdk`.
|
||||
- No additional CJS file deletion in scope per #2302.
|
||||
|
||||
4. **CI verification** (should run before any PR)
|
||||
- Run full integration suite: `npx vitest run --project integration` (mutation subprocess + read-only parity + golden composition).
|
||||
- Verify against CI matrix expectations: Ubuntu + macOS, Node 22 + 24.
|
||||
|
||||
### Acceptance criteria from #2302 — status
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
| --------- | ------ | ----- |
|
||||
| Policy gate | **Done** | `verifyGoldenPolicyComplete()` green; 0 orphan canonicals |
|
||||
| Parity | **Done** | 127/128 covered; strict rows, mutation subprocess, composition goldens |
|
||||
| Registry | **Done** | CJS-only matrix in `QUERY-HANDLERS.md`; `docs/CLI-TOOLS.md` updated |
|
||||
| Runners (Track C) | **Not started** | `PhaseRunner`/`InitRunner` still use `GSDTools` subprocess bridge |
|
||||
| Deprecation (Track D) | **Done** | `@deprecated` header on `gsd-tools.cjs` |
|
||||
| Docs | **Partial** | `QUERY-HANDLERS.md` current; `CHANGELOG.md` [Unreleased] needs Phase 3 entries |
|
||||
| CI | **Not verified** | Unit tests green (1261/1261); integration suite not run this session |
|
||||
|
||||
---
|
||||
|
||||
## Repo / branch
|
||||
|
||||
- **Workspace:** `D:\Repos\get-shit-done` (GSD PBR backport initiative).
|
||||
- **Feature branch:** `feat/sdk-phase3-query-layer` (62 commits ahead of `main`; confirm against `origin` before merging).
|
||||
- **Upstream PRs:** `gsd-build/get-shit-done` issue #2302.
|
||||
|
||||
---
|
||||
|
||||
## Golden parity architecture (current)
|
||||
|
||||
| Piece | Role |
|
||||
| ----- | ---- |
|
||||
| `sdk/src/golden/registry-canonical-commands.ts` | One canonical dispatch string per unique handler (`pickCanonicalCommandName`). |
|
||||
| `sdk/src/golden/golden-integration-covered.ts` | Canonicals exercised by **`golden.integration.test.ts`** (subset/full/shape tests). |
|
||||
| `sdk/src/golden/read-only-golden-rows.ts` | **Strict** `JsonParityRow[]` for `read-only-parity.integration.test.ts` (`toEqual` on parsed CJS JSON vs `sdkResult.data`). |
|
||||
| `sdk/src/golden/read-only-parity.integration.test.ts` | Rows from `READ_ONLY_JSON_PARITY_ROWS` + **`config-path`** (plain stdout vs `{ path }`, `path.normalize`) + **`verify.commits`**. |
|
||||
| `sdk/src/golden/capture.ts` | `captureGsdToolsOutput` (JSON stdout); **`captureGsdToolsStdout`** (raw stdout, e.g. `config-path`). |
|
||||
| `sdk/src/golden/golden-policy.ts` | `GOLDEN_PARITY_INTEGRATION_COVERED` = integration ∪ `readOnlyGoldenCanonicals()` ∪ **`GOLDEN_MUTATION_SUBPROCESS_COVERED`**; `GOLDEN_PARITY_EXCEPTIONS` includes `NO_CJS_SUBPROCESS_REASON`, then `MUTATION_DEFERRED_REASON` for remaining mutations, else read-only. |
|
||||
| `sdk/src/golden/golden-mutation-covered.ts` | Canonicals exercised by **`mutation-subprocess.integration.test.ts`** (must match non-skipped tests). |
|
||||
| `sdk/src/golden/mutation-subprocess.integration.test.ts` | Tmp fixture + `captureGsdToolsOutput` vs `registry.dispatch`; dual sandbox per comparison. |
|
||||
| `sdk/src/golden/mutation-sandbox.ts` | `createMutationSandbox({ git?: boolean })` — copy fixture, optional `git init` + commit. |
|
||||
| `sdk/src/golden/golden-policy.test.ts` | Calls `verifyGoldenPolicyComplete()` so every canonical is covered or excepted. |
|
||||
|
||||
**Invariant:** Every canonical from `getCanonicalRegistryCommands()` is either in `GOLDEN_PARITY_INTEGRATION_COVERED` or has an exception string—**never** leave orphans by removing tests.
|
||||
|
||||
---
|
||||
|
||||
## Reference pattern: porting like `scan-sessions` and `workstream.status`
|
||||
|
||||
These were fixed by **aligning the TypeScript handler with the CJS implementation**, then adding a row to `READ_ONLY_JSON_PARITY_ROWS`.
|
||||
|
||||
1. **Find the CJS source of truth**
|
||||
- `scan-sessions`: `get-shit-done/bin/lib/profile-pipeline.cjs` → `cmdScanSessions`
|
||||
- `workstream status`: `get-shit-done/bin/lib/workstream.cjs` → `cmdWorkstreamStatus`
|
||||
- `gsd-tools.cjs` `runCommand` switch shows the top-level command and argv.
|
||||
|
||||
2. **Implement or adjust the SDK module**
|
||||
- Example: `sdk/src/query/profile-scan-sessions.ts` mirrors the project-array build from `cmdScanSessions`; `scanSessions` in `profile.ts` parses `--path` / `--verbose`, throws when no sessions root (same error text as CJS), returns `{ data: projects }` where `projects` matches CJS JSON array.
|
||||
|
||||
3. **Add a parity row** in `read-only-golden-rows.ts` with `canonical`, `sdkArgs`, `cjs`, `cjsArgs` (must match what `execFile(node, [gsdToolsPath, command, ...args])` expects).
|
||||
|
||||
4. **Run**
|
||||
`cd sdk && npm run build && npx vitest run src/golden/read-only-parity.integration.test.ts src/golden/golden-policy.test.ts --project integration --project unit`
|
||||
|
||||
5. **Policy**
|
||||
`readOnlyGoldenCanonicals()` picks up new canonicals automatically; no manual duplicate if the canonical is already in the JSON row list.
|
||||
|
||||
**When not to copy line-for-line:** subprocess-only concerns (e.g. `agents_installed` / `missing_agents` differing from in-process `~` resolution). Then **normalize in the test** (see `golden.integration.test.ts` `docs-init`: sort `existing_docs`, omit install fields)—**document in QUERY-HANDLERS.md**, do not delete the assertion.
|
||||
|
||||
---
|
||||
|
||||
## Completed — Track A (golden parity)
|
||||
|
||||
All 127 portable canonicals have subprocess or in-process parity coverage. Summary of completed work by batch:
|
||||
|
||||
### Profile-output + milestone subprocess batch (latest)
|
||||
|
||||
**`write-profile`**, **`generate-claude-profile`**, **`generate-dev-preferences`**, **`generate-claude-md`** — implemented in **`sdk/src/query/profile-output.ts`** (templates from `get-shit-done/templates/`, same JSON as `profile-output.cjs`); re-exported from **`profile.ts`**. **`milestone.complete`** — full port of **`cmdMilestoneComplete`** in **`phase-lifecycle.ts`**; **`readModifyWriteStateMdFull`** in **`state-mutation.ts`** for STATE writes matching CJS.
|
||||
|
||||
### Mutation subprocess infrastructure
|
||||
|
||||
**`mutation-subprocess.integration.test.ts`** — tmp fixture `sdk/src/golden/fixtures/mutation-project/` + `createMutationSandbox()` (`mutation-sandbox.ts`). **`assertJsonParity`** runs CJS and SDK on **two fresh sandboxes** (factory fn) so neither run sees the other's filesystem mutations. **`GOLDEN_MUTATION_SUBPROCESS_COVERED`** lists canonicals with non-skipped subprocess assertions. Handlers covered: `config-ensure-section`, `commit`, `commitToSubrepo`, `configSetModelProfile`, `state.patch`, `frontmatter.set`/`merge`, `workstream.progress`, `workstream.set`, nine `state.*` subprocess tests, `write-profile`, `generate-claude-profile`, `generate-dev-preferences`, `generate-claude-md`, `milestone.complete`, `init.remove-workspace`.
|
||||
|
||||
### CJS mutation handler alignment
|
||||
|
||||
`commit.ts` — `--files` argv boundary, `commitToSubrepo` config check, `checkCommit` `allowed` field. `state-mutation.ts` — `readModifyWriteStateMdFull`, `statePlannedPhase`=`cmdStatePlannedPhase`, record-session/add-decision/add-blocker/resolve-blocker/record-metric/update-progress JSON shapes. `phase-lifecycle.ts` — `milestone.complete`. `workstream.ts` — `workstream.progress` (`cmdWorkstreamProgress`), `workstream.set`. `roadmap.ts` — extracted `roadmapUpdatePlanProgress` to own module. `frontmatter-mutation.ts` — `--field`/`--value`, `--data` parsing. `config-mutation.ts` — `configSetModelProfile` CJS-shaped `{ updated, profile, previousProfile, agentToModelMap }`. `config-query.ts` — `getAgentToModelMapForProfile()`.
|
||||
|
||||
### Read-only parity rows (earlier batches)
|
||||
|
||||
`progress.table` / `stats.table`, `progress.bar`, `learnings.query`, `profile-questionnaire`, `verify.references`, `init.*` composition goldens (9 handlers), `profile-sample`, `extract-messages`, `uat.render-checkpoint`, `validate.agents` + `state.get`, `skill-manifest`, `audit-open` + `audit-uat`, `intel.extract-exports`, `summary-extract` + `history-digest`, `stats.json`, `todo.match-phase`, `verify.key-links`, `verify.schema-drift`, `state-snapshot`, `state.json`/`state.load`, `scan-sessions`, `workstream.status`.
|
||||
|
||||
---
|
||||
|
||||
## Next batch — summary / audit / skill / validate / UAT / intel / profile / init
|
||||
|
||||
**Same workflow as above:** read `gsd-tools.cjs` `runCommand` for argv → implement/adjust `sdk/src/query/*.ts` → add `READ_ONLY_JSON_PARITY_ROWS` and/or a **named `describe` block** with documented omissions → `npm run build` → `read-only-parity.integration.test.ts` + `golden-policy.test.ts`.
|
||||
|
||||
| Priority | Command (CLI) | `gsd-tools.cjs` case / args | CJS implementation | SDK module | Notes |
|
||||
| -------- | ------------- | -------------------------- | -------------------- | ---------- | ----- |
|
||||
| ~~1~~ | ~~`summary-extract <path>`~~ `[--fields a,b]` | `summary-extract` | `commands.cjs` `cmdSummaryExtract` (~L425) | `summary.ts` `summaryExtract` | **Done:** strict `READ_ONLY_JSON_PARITY_ROWS`; `summary.ts` aligned with `commands.cjs`; `extractFrontmatterLeading` in `frontmatter.ts` for first-`---`-block parity with `frontmatter.cjs`. |
|
||||
| ~~2~~ | ~~`history-digest`~~ | `history-digest` | `commands.cjs` `cmdHistoryDigest` (~L133) | `summary.ts` `historyDigest` | **Done:** same row / handler alignment as above. |
|
||||
| ~~3~~ | ~~`audit-open`~~ | `audit-open` `[--json]` | `audit.cjs` `auditOpenArtifacts` + optional `formatAuditReport` | `audit-open.ts` | **Done:** `--json` parity test + `scanned_at` normalization; `sanitizeForDisplay` = `security.cjs`. |
|
||||
| ~~4~~ | ~~`audit-uat`~~ | `audit-uat` | `uat.cjs` `cmdAuditUat` | `uat.ts` `auditUat` | **Done:** `auditUat` ports `cmdAuditUat` (`parseUatItems`, milestone filter, `summary.by_*`); strict `READ_ONLY_JSON_PARITY_ROWS` row. |
|
||||
| ~~5~~ | ~~`skill-manifest`~~ | `skill-manifest` + args | `init.cjs` `cmdSkillManifest` (~L1829) | `skill-manifest.ts` | **Done:** strict row; `extractFrontmatterLeading` for CJS parity (see `QUERY-HANDLERS.md`). |
|
||||
| ~~6~~ | ~~`validate agents`~~ | `validate` + `agents` | `verify.cjs` `cmdValidateAgents` (~L997) | `validate.ts` `validateAgents` | **Done:** strict row; `getAgentsDir` parity with `core.cjs`; `MODEL_PROFILES` includes `gsd-pattern-mapper` (sync with `model-profiles.cjs`). |
|
||||
| ~~7~~ | ~~`uat render-checkpoint --file <path>`~~ | `uat` subcommand | `uat.cjs` `cmdRenderCheckpoint` | `uat.ts` `uatRenderCheckpoint` | **Done:** strict row; fixture `sdk/src/golden/fixtures/uat-render-checkpoint-sample.md`; see `QUERY-HANDLERS.md`. |
|
||||
| ~~8~~ | ~~`intel extract-exports <file>`~~ | `intel` `extract-exports` | `intel.cjs` `intelExtractExports` (~L502) | `intel.ts` `intelExtractExports` | **Done:** strict row + handler parity with `intel.cjs` (fixed file e.g. `sdk/src/query/utils.ts`). |
|
||||
| ~~9~~ | ~~`extract-messages`~~ | `extract-messages` + project/session flags | `profile-pipeline.cjs` | `profile.ts` `extractMessages` | **Done:** `profile-extract-messages.ts` + golden `output_file` strip + JSONL compare; fixture `extract-messages-sessions/`. |
|
||||
| ~~10~~ | ~~`profile-sample`~~ | `profile-sample` | `profile-pipeline.cjs` | `profile.ts` `profileSample` | **Done:** `profile-sample.ts` + golden `output_file` strip + JSONL compare; fixture `profile-sample-sessions/`. |
|
||||
| ~~11~~ | ~~**`init.*` read-only JSON**~~ | various | `init.cjs` / `init-complex` | `init.ts`, `init-complex.ts` | **Done:** `golden.integration.test.ts` + nine init composition tests; `withProjectRoot` / `subagent_timeout` / `GOLDEN_INTEGRATION_MAIN_FILE_CANONICALS`; see `QUERY-HANDLERS.md`. |
|
||||
|
||||
**Suggested order:** Audit/read-only batch above is complete — follow-ups via **`GOLDEN_PARITY_EXCEPTIONS`** / new strict rows as needed (`learnings.query`, `progress.bar`, `profile-questionnaire`, etc.).
|
||||
|
||||
**Done (this line of work):** `summary-extract` + `history-digest` — strict `READ_ONLY_JSON_PARITY_ROWS`; `summary.ts` aligned with `commands.cjs`; `extractFrontmatterLeading` in `frontmatter.ts` for first-`---`-block parity with `frontmatter.cjs`.
|
||||
|
||||
**Done (profile-output + milestone mutation batch):** `write-profile`, `generate-claude-profile`, `generate-dev-preferences`, `generate-claude-md` (`profile-output.ts`); `milestone.complete` (`phase-lifecycle.ts` + `readModifyWriteStateMdFull`); `GOLDEN_MUTATION_SUBPROCESS_COVERED` updated; **`MUTATION_SUBPROCESS_GAP_REASON` removed** from `golden-policy.ts`.
|
||||
|
||||
**Mutations** (`QUERY_MUTATION_COMMANDS`): subprocess coverage is **`mutation-subprocess.integration.test.ts`** + `GOLDEN_MUTATION_SUBPROCESS_COVERED`. Remaining mutation canonicals without a subprocess row use **`MUTATION_DEFERRED_REASON`** (see `golden-policy.ts`). For known gaps before parity, prefer **`it.skip`** with an explicit rationale in code comments or restore a dedicated gap map — do not rely on silent deferral alone.
|
||||
|
||||
---
|
||||
|
||||
## Backlog: other read-only handlers (lower priority or follow-ups)
|
||||
|
||||
Confirm against `GOLDEN_PARITY_EXCEPTIONS` in `golden-policy.ts` for the live list.
|
||||
|
||||
**Mutations:** Prefer tmp fixture + dual sandbox (see `mutation-sandbox.ts`). Do not green the suite by deleting subprocess tests; skip with **`it.skip`** and document the gap (policy entry or comment) until parity is restored.
|
||||
|
||||
---
|
||||
|
||||
## Not in the SDK registry (product decision)
|
||||
|
||||
- **`graphify`**, **`from-gsd2` / `gsd2-import`** — CLI-only; no registry handler.
|
||||
|
||||
---
|
||||
|
||||
## Files to know (updated)
|
||||
|
||||
| Path | Role |
|
||||
| ---- | ---- |
|
||||
| `sdk/src/query/index.ts` | `createRegistry()`, `QUERY_MUTATION_COMMANDS`. |
|
||||
| `sdk/src/golden/golden-policy.ts` | Coverage set + exceptions; `verifyGoldenPolicyComplete()`. |
|
||||
| `sdk/src/golden/read-only-golden-rows.ts` | Strict read-only JSON matrix. |
|
||||
| `sdk/src/golden/read-only-parity.integration.test.ts` | Subprocess + dispatch parity tests. |
|
||||
| `sdk/src/golden/capture.ts` | `captureGsdToolsOutput`, `captureGsdToolsStdout`. |
|
||||
| `sdk/src/golden/fixtures/mutation-project/` | Ephemeral copy for mutation subprocess tests. |
|
||||
| `sdk/src/golden/mutation-subprocess.integration.test.ts` | Mutation handler subprocess parity. |
|
||||
| `sdk/src/golden/mutation-sandbox.ts` | `createMutationSandbox({ git?: boolean })`. |
|
||||
| `sdk/src/query/profile-output.ts` | CJS-parity profile output handlers. |
|
||||
| `sdk/src/phase-runner.ts` | **Track C target** — currently uses `GSDTools`. |
|
||||
| `sdk/src/init-runner.ts` | **Track C target** — currently uses `GSDTools`. |
|
||||
| `sdk/src/gsd-tools.ts` | Subprocess bridge; **not deleted** in Phase 3 scope. |
|
||||
| `get-shit-done/bin/gsd-tools.cjs` | `runCommand` — argv routing. Has `@deprecated` header. |
|
||||
| `get-shit-done/bin/lib/*.cjs` | Per-command implementations (CJS source of truth). |
|
||||
|
||||
---
|
||||
|
||||
## Commands (verification)
|
||||
|
||||
```bash
|
||||
cd sdk
|
||||
npm run build
|
||||
npm run test:unit
|
||||
npm run test:integration
|
||||
```
|
||||
|
||||
Focused:
|
||||
|
||||
```bash
|
||||
npx vitest run src/golden/read-only-parity.integration.test.ts src/golden/golden.integration.test.ts --project integration
|
||||
npx vitest run src/golden/mutation-subprocess.integration.test.ts --project integration
|
||||
npx vitest run src/golden/golden-policy.test.ts --project unit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success criteria (extend, not replace)
|
||||
|
||||
- **No regression:** `golden-policy.test.ts` / `verifyGoldenPolicyComplete()` stays green.
|
||||
- **Track A complete:** 127/128 covered; read-only rows, mutation subprocess, composition goldens all in place.
|
||||
- **Track C:** Runner alignment — `PhaseRunner` and `InitRunner` use typed handlers where possible; `GSDTools` remains exported.
|
||||
- **CHANGELOG.md** [Unreleased] updated with Phase 3 entries.
|
||||
- **`QUERY-HANDLERS.md`** updated when assertion style changes (full `toEqual` vs normalized subset).
|
||||
|
||||
**Do not "green the suite" by deleting or shrinking golden tests.** If a handler cannot match CJS byte-for-byte without product decisions, use **documented normalization** in the test or **fix the TypeScript handler** — do not silently remove assertions.
|
||||
|
||||
---
|
||||
|
||||
## Commit history (this branch)
|
||||
|
||||
62 commits ahead of `main` on `feat/sdk-phase3-query-layer`. Recent batch (5 commits):
|
||||
|
||||
```
|
||||
95db59c docs(sdk): update handover for profile-output and mutation subprocess batch
|
||||
05e8238 sdk(golden): mutation subprocess test infrastructure and golden policy
|
||||
593d9be sdk(query): port profile output handlers from profile-output.cjs
|
||||
a2d0eb6 sdk(query): CJS parity for state, phase-lifecycle, workstream, roadmap, frontmatter, config, and intel
|
||||
8bd9f1d sdk(query): align commit handler with CJS --files argv and allowed field
|
||||
```
|
||||
|
||||
**Cherry-pick notes:** Commits 1 (`8bd9f1d`) and 3 (`593d9be`) are independently cherry-pickable. Commit 2 (`a2d0eb6`) is a bulk handler alignment (13 files). Commit 4 (`05e8238`) depends on handlers from 2+3 at test-runtime but compiles independently. Commit 5 is docs-only.
|
||||
|
||||
---
|
||||
|
||||
*Update this file when registry or golden milestones change.*
|
||||
97
sdk/HANDOVER-PARITY-DOCS.md
Normal file
97
sdk/HANDOVER-PARITY-DOCS.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Handover: Parity exceptions doc + CJS-only matrix (next session)
|
||||
|
||||
**Status:** The deliverables described below are implemented in `sdk/src/query/QUERY-HANDLERS.md` (sections **Golden parity: coverage and exceptions** and **CJS command surface vs SDK registry**). Use that file as the canonical registry + parity reference; this handover remains useful for issue **#2302** scope and parent **#2007** links.
|
||||
|
||||
Paste this document (or `@sdk/HANDOVER-PARITY-DOCS.md`) at the start of a new chat so work continues without re-auditing issue scope.
|
||||
|
||||
## Goal for this session
|
||||
|
||||
1. **Parity “exceptions” documentation** — A clear, maintainable description of where **full JSON equality** between `gsd-tools.cjs` and `createRegistry()` is **not** expected or not attempted, and why (stubs, structural-only tests, environment-dependent fields, ordering, etc.). Map this to **#2007 / #2302** expectations: no *undocumented* gap.
|
||||
2. **CJS-only matrix** — A **single authoritative table**: each relevant `gsd-tools.cjs` surface (top-level command or documented cluster) → **registered in SDK** vs **permanent CLI-only** vs **alias / naming difference**, with a **one-line justification** where not registered.
|
||||
|
||||
## Parent tracking
|
||||
|
||||
- **Issue:** [gsd-build/get-shit-done#2302](https://github.com/gsd-build/get-shit-done/issues/2302) — Phase 3 SDK query parity, registry, docs (parent umbrella #2007).
|
||||
- **Acceptance criteria touched here:** parity coverage/exceptions documented; registry audit reflected in a **matrix** (issue wording: “every required CJS surface either has a handler or appears in the CJS-only matrix with justification”).
|
||||
|
||||
## Repo / branch
|
||||
|
||||
- **Workspace:** `D:\Repos\get-shit-done` (PBR backport); adjust path if different machine.
|
||||
- **Feature branch (typical):** `feat/sdk-phase3-query-layer` — confirm with `git branch` before editing.
|
||||
- **Upstream:** `gsd-build/get-shit-done`.
|
||||
|
||||
## What already exists (do not duplicate blindly)
|
||||
|
||||
- `sdk/src/query/QUERY-HANDLERS.md` — Registry conventions, partial “not registered” list (**graphify**, **from-gsd2**), CLI name differences (**summary-extract** vs **summary.extract**, **scaffold** vs **phase.scaffold**), **intel.update** (CJS JSON parity; refresh via agent), **skill-manifest --write** / mutation events, **docs-init** golden note (agent install fields), **stateExtractField** rule.
|
||||
- `sdk/src/golden/golden.integration.test.ts` — Source of truth for **which commands** are golden-tested and **how** (full equality vs subset vs normalized `existing_docs` vs omitted fields; `init.quick` strips clock-derived keys via `init-golden-normalize.ts`).
|
||||
- `sdk/src/golden/capture.ts` — `captureGsdToolsOutput()` spawns `get-shit-done/bin/gsd-tools.cjs`.
|
||||
- `docs/CLI-TOOLS.md` — User-facing CLI reference; should **link** to the parity exceptions + matrix (or host a short summary with pointer to `sdk/`).
|
||||
|
||||
## Deliverables (suggested shape)
|
||||
|
||||
### A) Parity exceptions section
|
||||
|
||||
Add or extend a dedicated section (prefer `QUERY-HANDLERS.md` under a heading like **"Golden parity: coverage and exceptions"**, or a new `sdk/PARITY.md` if the team wants less churn in QUERY-HANDLERS — **pick one canonical location** and link from the other).
|
||||
|
||||
Cover at least:
|
||||
|
||||
|
||||
| Category | Examples to document |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Full JSON parity** | Commands where tests use `toEqual` on `sdkResult.data` vs CJS stdout JSON. |
|
||||
| **Structural / field subset** | Tests that compare only selected keys (e.g. `frontmatter.get`, `find-phase` — SDK subset vs CJS). Full parity for `roadmap.analyze`, `init.*` (except `init.quick` volatile keys), etc. — see `QUERY-HANDLERS.md` matrix. |
|
||||
| **Normalized comparison** | e.g. `docs-init`: `existing_docs` sorted by path; `agents_installed` / `missing_agents` omitted between subprocess vs in-process. |
|
||||
| **CLI parity without in-process refresh** | `intel.update` — JSON matches CJS `intel.cjs` (spawn hint or disabled); refresh is agent-driven. |
|
||||
| **Conditional behavior** | `skill-manifest`: writes only with `--write`; not in `QUERY_MUTATION_COMMANDS`. |
|
||||
| **Environment / time** | `current-timestamp`: structure and format, not same instant. |
|
||||
| **Not in golden suite** | Commands registered but not (yet) covered — list as **coverage gap** or **out of scope for golden** with rationale. |
|
||||
|
||||
|
||||
### B) CJS-only matrix
|
||||
|
||||
Build the table by **diffing** `get-shit-done/bin/gsd-tools.cjs` `switch (command)` top-level cases against `createRegistry()` registrations in `sdk/src/query/index.ts`.
|
||||
|
||||
**Already documented as product-out-of-scope for registry:** **graphify**, **from-gsd2** / **gsd2-import**.
|
||||
|
||||
**Already documented as naming/alias differences (registered, different string):** **summary-extract** ↔ **summary.extract**; top-level **scaffold** ↔ **phase.scaffold**.
|
||||
|
||||
Matrix columns (suggested):
|
||||
|
||||
- **CJS command** (or subcommand pattern)
|
||||
- **SDK dispatch name(s)** if any
|
||||
- **Disposition:** Registered / CLI-only / Alias-only / Stub / N/A
|
||||
- **Justification** (one line) if not a straight registered parity
|
||||
|
||||
Optional: footnote that `detect-custom-files` skips multi-repo root resolution in CJS (`SKIP_ROOT_RESOLUTION`) — behavior is documented in CLI; matrix can mention if relevant.
|
||||
|
||||
## Files likely to edit
|
||||
|
||||
|
||||
| Path | Role |
|
||||
| --------------------------------- | ----------------------------------------------------------------- |
|
||||
| `sdk/src/query/QUERY-HANDLERS.md` | Primary home for exceptions + matrix, or link hub. |
|
||||
| `sdk/PARITY.md` | Optional dedicated file if QUERY-HANDLERS becomes too long. |
|
||||
| `docs/CLI-TOOLS.md` | Short “Parity & registry” subsection with links into `sdk/` docs. |
|
||||
| `sdk/HANDOVER-GOLDEN-PARITY.md` | Optional one-line pointer to new parity doc section when done. |
|
||||
|
||||
|
||||
## Out of scope for *this* handover session
|
||||
|
||||
- Implementing runner alignment (`GSDTools` → registry) — separate #2302 work.
|
||||
- Adding `@deprecated` headers to `gsd-tools.cjs` — separate task.
|
||||
- **CHANGELOG** — only if you batch doc work with release notes in same PR (optional).
|
||||
|
||||
## Verification
|
||||
|
||||
- No code behavior change required for pure docs; run `npm run build` in `sdk/` only if TypeScript-adjacent files were touched.
|
||||
- Proofread: every **CLI-only** row has a **justification**; every **exception** in golden tests appears in the exceptions doc.
|
||||
|
||||
## Success criteria
|
||||
|
||||
- A reader can answer: **“Which commands are fully golden-parity vs partial vs stub vs untested?”** without reading the whole test file.
|
||||
- A reader can answer: **“Which `gsd-tools` top-level commands are not registered and why?”** from one table.
|
||||
- **#2302** acceptance bullets on parity documentation and registry matrix are satisfied for the **documentation** slice (remaining issue items may still be open for code).
|
||||
|
||||
---
|
||||
|
||||
*Created for handoff to “parity exceptions + CJS-only matrix” session. Update when the canonical doc location or golden coverage changes.*
|
||||
170
sdk/HANDOVER-QUERY-LAYER.md
Normal file
170
sdk/HANDOVER-QUERY-LAYER.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# Handover: SDK query layer (registry, CLI, parity docs)
|
||||
|
||||
Paste this document (or `@sdk/HANDOVER-QUERY-LAYER.md`) at the start of a new session so work continues without re-deriving scope.
|
||||
|
||||
## Parent tracking
|
||||
|
||||
- **Issue:** [gsd-build/get-shit-done#2302](https://github.com/gsd-build/get-shit-done/issues/2302) — Phase 3 SDK query parity, registry, docs (umbrella #2007).
|
||||
- **Workspace:** `D:\Repos\get-shit-done` (PBR backport). **Upstream:** `gsd-build/get-shit-done`. Confirm branch with `git branch` (typical: `feat/sdk-phase3-query-layer`).
|
||||
|
||||
### Scope anchors (do not confuse issues)
|
||||
|
||||
|
||||
| Role | GitHub | Notes |
|
||||
| --------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Product / requirements anchor** | [#2007](https://github.com/gsd-build/get-shit-done/issues/2007) | Problem statement, user stories, and target architecture for the SDK-first migration. **Do not** treat its original acceptance-checklist boxes as proof of what is merged upstream; work was split into phased PRs after maintainer review. |
|
||||
| **Phase 3 execution scope** | [#2302](https://github.com/gsd-build/get-shit-done/issues/2302) **+ this handover** | What this branch is actually doing now: registry/CLI parity, docs, harness gaps, runner alignment follow-ups as listed below. |
|
||||
| **Patch mine (if local tree is short)** | [PR #2008](https://github.com/gsd-build/get-shit-done/pull/2008) and matching branches | Large pre-phasing PR; cherry-pick or compare when something looks missing vs that line of work. |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## What was delivered (this line of work)
|
||||
|
||||
### 1. Parity documentation (`QUERY-HANDLERS.md`)
|
||||
|
||||
- **”Golden parity: coverage and exceptions”** — How `golden.integration.test.ts` compares SDK vs `gsd-tools.cjs` (full `toEqual`, subset, normalized `docs-init`, `intel.update` CJS parity, time-dependent fields, etc.).
|
||||
- **”CJS command surface vs SDK registry”** — Naming aliases, CLI-only rows, SDK-only rows, and a **top-level `gsd-tools` command → SDK** matrix.
|
||||
- `docs/CLI-TOOLS.md` — Short “Parity & registry” pointer into those sections.
|
||||
- `HANDOVER-GOLDEN-PARITY.md` — One paragraph linking to the same sections.
|
||||
|
||||
### 2. `gsd-sdk query` tokenization (`normalizeQueryCommand`)
|
||||
|
||||
- **Problem:** `gsd-sdk query` used only argv[0] as the registry key, so `query state json` dispatched `state` (unregistered) instead of `state.json`.
|
||||
- **Fix:** `sdk/src/query/normalize-query-command.ts` merges the same **command + subcommand** patterns as `gsd-tools` `runCommand()` (e.g. `state json` → `state.json`, `init execute-phase 9` → `init.execute-phase`, `scaffold …` → `phase.scaffold`, `progress bar` → `progress.bar`). Wired in `sdk/src/cli.ts` before `registry.dispatch()`.
|
||||
- **Tests:** `sdk/src/query/normalize-query-command.test.ts`.
|
||||
|
||||
### 3. `phase add-batch` in the registry
|
||||
|
||||
- **Implementation:** `phaseAddBatch` in `sdk/src/query/phase-lifecycle.ts` — port of `cmdPhaseAddBatch` from `get-shit-done/bin/lib/phase.cjs` (batch append under one roadmap lock; sequential or `phase_naming: custom`).
|
||||
- **Registration:** `phase.add-batch` and `phase add-batch` in `sdk/src/query/index.ts`; listed in `QUERY_MUTATION_COMMANDS` (dotted + space forms).
|
||||
- **Tests:** `describe('phaseAddBatch')` in `sdk/src/query/phase-lifecycle.test.ts`.
|
||||
- **Docs:** `QUERY-HANDLERS.md` updated — `phase add-batch` is **registered**; CLI-only table no longer lists it.
|
||||
|
||||
### 4. `state load` fully in the registry (split from `state json`)
|
||||
|
||||
Previously `state.json` and `state.load` were easy to confuse: CJS has two different commands — `cmdStateJson` (`state json`, rebuilt frontmatter) vs `cmdStateLoad` (`state load`, `loadConfig` + `state_raw` + existence flags).
|
||||
|
||||
- `stateJson` — `sdk/src/query/state.ts`; registry key `state.json`.
|
||||
- `stateProjectLoad` — `sdk/src/query/state-project-load.ts`; registry key `state.load`. Uses `createRequire` to call `core.cjs` `loadConfig(projectDir)` from the same resolution paths as a normal install (bundled monorepo path, `projectDir/.claude/get-shit-done/...`, `~/.claude/get-shit-done/...`). `GSDTools.stateLoad()` and `formatRegistryRawStdout` for `--raw` no longer force a subprocess solely for this command.
|
||||
- **Risk:** If `core.cjs` is absent (e.g. some `@gsd-build/sdk`-only layouts), `state.load` throws `GSDError` — document; future option is a TS `loadConfig` port or bundling.
|
||||
- **Goldens:** `read-only-parity.integration.test.ts` — one block compares `state.json` to `state json` (strip `last_updated`); another compares `state.load` to `state load` (full `toEqual`). `read-only-golden-rows.ts` `readOnlyGoldenCanonicals()` includes both `state.json` and `state.load`.
|
||||
|
||||
---
|
||||
|
||||
## Query surface completeness (snapshot)
|
||||
|
||||
|
||||
| Status | Surface |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------------------ |
|
||||
| **Registered** | Essentially all `gsd-tools.cjs` `runCommand` surfaces, including `phase.add-batch`. |
|
||||
| **CLI-only (by design)** | `graphify`, `from-gsd2` — not in `createRegistry()`; documented in `QUERY-HANDLERS.md`. |
|
||||
| **SDK-only extra** | `phases.archive` — no `gsd-tools phases archive` subcommand (CJS has `list` / `clear` only). |
|
||||
|
||||
|
||||
**Programmatic API:** `createRegistry()` / `registry.dispatch('dotted.name', args, projectDir)`.
|
||||
|
||||
**CLI:** `gsd-sdk query …` — apply `normalizeQueryCommand` semantics (or pass dotted names explicitly).
|
||||
|
||||
**Still not unified:** `GSDTools` (`sdk/src/gsd-tools.ts`) shells out to `gsd-tools.cjs` for plan/session flows; migrating callers to the registry is separate #2302 / runner work. `state load` is **not** among the subprocess-only exceptions anymore (it uses the registry like other native query handlers when native query is active).
|
||||
|
||||
---
|
||||
|
||||
## Canonical files
|
||||
|
||||
|
||||
| Path | Role |
|
||||
| ------------------------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| `sdk/src/query/index.ts` | `createRegistry()`, `QUERY_MUTATION_COMMANDS`, handler wiring. |
|
||||
| `sdk/src/query/state-project-load.ts` | `state.load` — CJS `cmdStateLoad` parity (`loadConfig` + `state_raw` + flags). |
|
||||
| `sdk/src/query/normalize-query-command.ts` | CLI argv → registry command string. |
|
||||
| `sdk/src/cli.ts` | `gsd-sdk query` path (uses `normalizeQueryCommand`). |
|
||||
| `sdk/src/query/QUERY-HANDLERS.md` | Registry contracts, parity tiers, CJS matrix, mutation notes. |
|
||||
| `sdk/src/golden/golden.integration.test.ts` | Golden parity vs `captureGsdToolsOutput()`. |
|
||||
| `docs/CLI-TOOLS.md` | User-facing CLI; links to parity sections. |
|
||||
|
||||
|
||||
Related handovers: `HANDOVER-GOLDEN-PARITY.md`, `HANDOVER-PARITY-DOCS.md` (older parity-doc brief; content largely folded into `QUERY-HANDLERS.md`).
|
||||
|
||||
---
|
||||
|
||||
## Roadmap: parity vs decision offloading
|
||||
|
||||
Work that moves **deterministic** orchestration out of AI/bash and into **SDK queries** (historically `gsd-tools.cjs`) has **two layers**. Do not confuse them:
|
||||
|
||||
|
||||
| Layer | Goal | What “done” looks like |
|
||||
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| **Parity / migration** | Existing CLI behavior is **stable and testable** in the registry so callers can use `gsd-sdk query` instead of `node …/gsd-tools.cjs` without silent drift. | Goldens + `QUERY-HANDLERS.md`; same JSON/`--raw` contracts as CJS. |
|
||||
| **Offloading decisions** | **New or consolidated** queries replace repeated `grep`, `ls` piped to `wc -l`, many `config-get`s, and inline `node -e` in workflows — so the model does less parsing and branching. | Fewer inline shell blocks; measurable token/step reduction on representative workflows. |
|
||||
|
||||
|
||||
Phase 3–style registry work mainly advances **parity**. The `decision-routing-audit.md` proposals are mostly **offloading** — they assume parity exists for commands workflows already call.
|
||||
|
||||
### Decision-routing audit (proposed `gsd-tools` / SDK queries)
|
||||
|
||||
Source: `.planning/research/decision-routing-audit.md` §3. **Tier** = priority from §5 (implementation order). **Do not implement** = explicitly rejected in the audit.
|
||||
|
||||
| # | Proposed command | Tier | Notes |
|
||||
|---|------------------|------|--------|
|
||||
| 3.1 | `route next-action` | **1** | Next slash-command from `/gsd-next`-style routing. |
|
||||
| 3.2 | `check gates <workflow>` | 3 | Safety gates (continue-here, error state, verification debt). |
|
||||
| 3.3 | `check config-gates <workflow>` | **1** | Batch `workflow.*` config for orchestration (replaces many `config-get`s). |
|
||||
| 3.4 | `check phase-ready <phase>` | **1** | Phase directory readiness + `next_step` hint. |
|
||||
| 3.5 | `check auto-mode` | 2 | `auto_advance` + `_auto_chain_active` → single boolean. |
|
||||
| 3.6 | `detect phase-type <phase>` | 2 | Structured UI/schema detection (replaces fragile grep). |
|
||||
| 3.7 | `check completion <scope>` | 2 | Phase or milestone completion rollup. |
|
||||
| 3.8 | `check verification-status <phase>` | 3 | VERIFICATION.md parsing for routing. |
|
||||
| 3.9 | `check ship-ready <phase>` | 3 | Ship preflight (`ship.md`). |
|
||||
| 3.10 | `route workflow-steps <workflow>` | ❌ **Do not implement** | Pre-computed step lists are unsound when mid-workflow writes change state. See `review-and-risks.md` §3.6. |
|
||||
|
||||
**Not in audit:** `phase-artifact-counts` was only an example in an older handover line; there is no §3.11 for it — add via a new research doc if needed.
|
||||
|
||||
**SDK registry (Tier 1):** **Done** — `check.config-gates`, `check.phase-ready`, `route.next-action` in `createRegistry()` (`sdk/src/query/index.ts`). Documented in `sdk/src/query/QUERY-HANDLERS.md` § Decision routing (**SDK-only** until/unless mirrored in `gsd-tools.cjs`).
|
||||
|
||||
**Simple roadmap (execute in order):**
|
||||
|
||||
1. **Harden parity** for surfaces workflows already depend on (registry dispatch, goldens, docs) so swaps from CJS to `gsd-sdk query` stay safe.
|
||||
2. **Ship 1–2 high-leverage consolidation handlers** from the audit (pick based on impact and risk; examples: `check auto-mode`, `phase-artifact-counts`, `route next-action` — with **display/routing fields** required by `review-and-risks.md` if applicable). Each needs handlers, tests, and `QUERY-HANDLERS.md` notes. **Progress:** `check.auto-mode` shipped (`sdk/src/query/check-auto-mode.ts`); Tier 1 `route.next-action` already registered.
|
||||
3. **Rewrite one heavy workflow** (e.g. `next.md` or a focused slice of `autonomous.md`) to consume those queries and **measure** before/after (steps, tokens, or both). **Progress:** `execute-phase.md`, `discuss-phase.md`, `discuss-phase-assumptions.md`, and `plan-phase.md` (UI gate) now use `check auto-mode` instead of paired `config-get`s where applicable.
|
||||
4. **Maintain a living boundary** between SDK (**data, deterministic checks**) and workflows (**judgment, sequencing, user-facing messages**). Extend `decision-routing-audit.md` §6 (decisions that stay with the AI) and `review-and-risks.md` “Do not implement” (e.g. no pre-computed `route workflow-steps`) as you add primitives. **Progress:** audit §3.5 / Tier 2 #4 updated to reference SDK implementation.
|
||||
|
||||
**Gaps to keep in mind when designing new queries:** call-time vs stale data after file writes (re-query volatile fields); workflows own gates/UX; behavioral contracts (e.g. UI keyword lists) must match existing greps; `stderr`/`stdout` and JSON shapes stable for bash/`jq`; hybrid `require(core.cjs)` paths called out for minimal installs.
|
||||
|
||||
**Research references (repo root):** `.planning/research/decision-routing-audit.md`, `.planning/research/review-and-risks.md`, `.planning/research/inline-computation-audit.md`, `.planning/research/questions.md` (Q1 boundary). For parity mechanics, prefer `sdk/src/query/QUERY-HANDLERS.md` and `HANDOVER-GOLDEN-PARITY.md`.
|
||||
|
||||
---
|
||||
|
||||
## Suggested next session
|
||||
|
||||
(Strategic ordering of **parity vs decision offloading** is in **Roadmap** above.)
|
||||
|
||||
1. ~~**Golden test for `phase.add-batch`**~~ — Done: `sdk/src/golden/mutation-subprocess.integration.test.ts` (`phase.add-batch` JSON parity vs CJS).
|
||||
2. ~~**Re-export `normalizeQueryCommand`**~~ — Done: exported from `sdk/src/query/index.ts` and `sdk/src/index.ts` (`@gsd-build/sdk`).
|
||||
3. **Issue #2302 follow-ups** — Runner alignment (`GSDTools` → registry where appropriate). **`configGet`** now uses `dispatchNativeJson` with canonical `config-get` (fixes subprocess argv vs real `gsd-tools.cjs`, which has no `config` + `get` top-level). Keep `graphify` / `from-gsd2` out of scope unless product reopens.
|
||||
4. **Drift check** — When adding CJS commands, update `QUERY-HANDLERS.md` matrix and golden docs in the same PR.
|
||||
|
||||
---
|
||||
|
||||
## Verification commands
|
||||
|
||||
```bash
|
||||
cd sdk
|
||||
npm run build
|
||||
npx vitest run src/query/normalize-query-command.test.ts src/query/phase-lifecycle.test.ts src/query/registry.test.ts --project unit
|
||||
npx vitest run src/golden/golden.integration.test.ts --project integration
|
||||
```
|
||||
|
||||
(Adjust `--project` to match `sdk/vitest.config.ts`.)
|
||||
|
||||
---
|
||||
|
||||
## Success criteria (query-layer slice)
|
||||
|
||||
- Parity expectations and CJS↔SDK matrix documented in one place (`QUERY-HANDLERS.md`).
|
||||
- `gsd-sdk query` understands two-token command patterns like `gsd-tools`.
|
||||
- `phase add-batch` implemented and registered; **only** intentional CLI-only gaps remain (**graphify**, **from-gsd2**).
|
||||
|
||||
---
|
||||
|
||||
*Created/updated for query-layer handoff. Revise when registry surface, golden coverage, or the parity/offloading roadmap changes materially.*
|
||||
53
sdk/README.md
Normal file
53
sdk/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# @gsd-build/sdk
|
||||
|
||||
TypeScript SDK for **Get Shit Done**: deterministic query/mutation handlers, plan execution, and event-stream telemetry so agents focus on judgment, not shell plumbing.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install @gsd-build/sdk
|
||||
```
|
||||
|
||||
## Quickstart — programmatic
|
||||
|
||||
```typescript
|
||||
import { GSD, createRegistry } from '@gsd-build/sdk';
|
||||
|
||||
const gsd = new GSD({ projectDir: process.cwd(), sessionId: 'my-run' });
|
||||
const tools = gsd.createTools();
|
||||
|
||||
const registry = createRegistry(gsd.eventStream, 'my-run');
|
||||
const { data } = await registry.dispatch('state.json', [], process.cwd());
|
||||
```
|
||||
|
||||
## Quickstart — CLI
|
||||
|
||||
From a project that depends on this package, **invoke the CLI with Node** (recommended in CI and local dev):
|
||||
|
||||
```bash
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query state.json
|
||||
node ./node_modules/@gsd-build/sdk/dist/cli.js query roadmap.analyze
|
||||
```
|
||||
|
||||
If no native handler is registered for a command, the CLI can transparently shell out to `get-shit-done/bin/gsd-tools.cjs` (see stderr warning), unless `GSD_QUERY_FALLBACK=off`.
|
||||
|
||||
## What ships
|
||||
|
||||
| Area | Entry |
|
||||
|------|--------|
|
||||
| Query registry | `createRegistry()` in `src/query/index.ts` — same handlers as `gsd-sdk query` |
|
||||
| Tools bridge | `GSDTools` — native dispatch with optional CJS subprocess fallback |
|
||||
| Orchestrators | `PhaseRunner`, `InitRunner`, `GSD` |
|
||||
| CLI | `gsd-sdk` — `query`, `run`, `init`, `auto` |
|
||||
|
||||
## Guides
|
||||
|
||||
- **Handler registry & contracts:** [`src/query/QUERY-HANDLERS.md`](src/query/QUERY-HANDLERS.md)
|
||||
- **Repository docs** (when present): `docs/ARCHITECTURE.md`, `docs/CLI-TOOLS.md` at repo root
|
||||
|
||||
## Environment
|
||||
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `GSD_QUERY_FALLBACK` | `off` / `never` disables CLI fallback to `gsd-tools.cjs` for unknown commands |
|
||||
| `GSD_AGENTS_DIR` | Override directory scanned for installed GSD agents (`~/.claude/agents` by default) |
|
||||
@@ -5,7 +5,7 @@ tools: Read, Bash, Glob, Grep
|
||||
---
|
||||
|
||||
<role>
|
||||
You are a GSD plan checker. Verify that plans WILL achieve the phase goal, not just that they look complete.
|
||||
A set of phase plans has been submitted for pre-execution review. Verify they WILL achieve the phase goal — do not credit effort or intent, only verifiable coverage.
|
||||
|
||||
Goal-backward verification of PLANS before execution. Start from what the phase SHOULD deliver, verify plans address it.
|
||||
|
||||
@@ -19,6 +19,21 @@ If the prompt contains a `<files_to_read>` block, you MUST read every file liste
|
||||
- Scope exceeds context budget
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume every plan set is flawed until evidence proves otherwise. Your starting hypothesis: these plans will not deliver the phase goal. Surface what disqualifies them.
|
||||
|
||||
**Common failure modes — how plan checkers go soft:**
|
||||
- Accepting a plausible-sounding task list without tracing each task back to a phase requirement
|
||||
- Crediting a decision reference without verifying the task delivers the full decision scope
|
||||
- Treating scope reduction ("v1", "static for now") as acceptable when full delivery was required
|
||||
- Letting dimensions that pass anchor judgment — a plan can pass 6 of 7 dimensions and still miss the goal
|
||||
|
||||
**Required finding classification:**
|
||||
- **BLOCKER** — the phase goal will not be achieved if this is not fixed before execution
|
||||
- **WARNING** — quality or maintainability is degraded; fix recommended but execution can proceed
|
||||
Issues without a severity classification are not valid output.
|
||||
</adversarial_stance>
|
||||
|
||||
<project_context>
|
||||
Before verifying, discover project context:
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ Always include current year. Use multiple query variations. Mark WebSearch-only
|
||||
If Brave Search is available, use it for higher quality results:
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" websearch "your query" --limit 10
|
||||
gsd-sdk query websearch "your query" --limit 10
|
||||
```
|
||||
|
||||
**Options:**
|
||||
|
||||
@@ -5,9 +5,9 @@ tools: Read, Write, Bash, Grep, Glob
|
||||
---
|
||||
|
||||
<role>
|
||||
You are a GSD phase verifier. You verify that a phase achieved its GOAL, not just completed its TASKS.
|
||||
A completed phase has been submitted for goal-backward verification. Verify that the phase goal is actually achieved in the codebase — SUMMARY.md claims are not evidence.
|
||||
|
||||
Your job: Goal-backward verification. Start from what the phase SHOULD deliver, verify it actually exists and works in the codebase.
|
||||
Goal-backward verification. Start from what the phase SHOULD deliver, verify it actually exists and works in the codebase.
|
||||
|
||||
**CRITICAL: Mandatory Initial Read**
|
||||
If the prompt contains a `<files_to_read>` block, you MUST read every file listed there before performing any other actions. This is your primary context.
|
||||
@@ -15,6 +15,21 @@ If the prompt contains a `<files_to_read>` block, you MUST read every file liste
|
||||
**Critical mindset:** Do NOT trust SUMMARY.md claims. SUMMARYs document what was SAID it did. You verify what ACTUALLY exists in the code.
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
**FORCE stance:** Assume the phase goal was not achieved until codebase evidence proves it. Your starting hypothesis: tasks completed, goal missed. Falsify the SUMMARY.md narrative.
|
||||
|
||||
**Common failure modes — how verifiers go soft:**
|
||||
- Trusting SUMMARY.md bullet points without reading the actual code files they describe
|
||||
- Accepting "file exists" as "truth verified" — a stub satisfies existence but not behavior
|
||||
- Choosing UNCERTAIN instead of FAILED when absence is observable
|
||||
- Letting high task-completion percentage bias judgment toward PASS before truths are checked
|
||||
|
||||
**Required finding classification:**
|
||||
- **BLOCKER** — a must-have truth is FAILED; phase goal not achieved; must not proceed
|
||||
- **WARNING** — a must-have is UNCERTAIN or wiring is incomplete
|
||||
Every truth must resolve to VERIFIED, FAILED (BLOCKER), or UNCERTAIN (WARNING).
|
||||
</adversarial_stance>
|
||||
|
||||
<project_context>
|
||||
Before verifying, discover project context:
|
||||
|
||||
|
||||
59
sdk/scripts/gen-profile-questionnaire-data.mjs
Normal file
59
sdk/scripts/gen-profile-questionnaire-data.mjs
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* One-off generator: extracts PROFILING_QUESTIONS + CLAUDE_INSTRUCTIONS from profile-output.cjs
|
||||
* Run: node scripts/gen-profile-questionnaire-data.mjs
|
||||
*/
|
||||
import fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = join(__dirname, '..', '..');
|
||||
const cjs = fs.readFileSync(join(root, 'get-shit-done/bin/lib/profile-output.cjs'), 'utf-8');
|
||||
|
||||
const m1 = cjs.match(/const PROFILING_QUESTIONS = (\[[\s\S]*?\]);/);
|
||||
const m2 = cjs.match(/const CLAUDE_INSTRUCTIONS = (\{[\s\S]*?\n\});/);
|
||||
if (!m1 || !m2) {
|
||||
console.error('regex extract failed');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const header = `/**
|
||||
* Synced from get-shit-done/bin/lib/profile-output.cjs (PROFILING_QUESTIONS, CLAUDE_INSTRUCTIONS).
|
||||
* Used by profileQuestionnaire for parity with cmdProfileQuestionnaire.
|
||||
*/
|
||||
|
||||
export type ProfilingOption = { label: string; value: string; rating: string };
|
||||
|
||||
export type ProfilingQuestion = {
|
||||
dimension: string;
|
||||
header: string;
|
||||
context: string;
|
||||
question: string;
|
||||
options: ProfilingOption[];
|
||||
};
|
||||
|
||||
export const PROFILING_QUESTIONS: ProfilingQuestion[] = ${m1[1]};
|
||||
|
||||
export const CLAUDE_INSTRUCTIONS: Record<string, Record<string, string>> = ${m2[1]};
|
||||
|
||||
export function isAmbiguousAnswer(dimension: string, value: string): boolean {
|
||||
if (dimension === 'communication_style' && value === 'd') return true;
|
||||
const question = PROFILING_QUESTIONS.find((q) => q.dimension === dimension);
|
||||
if (!question) return false;
|
||||
const option = question.options.find((o) => o.value === value);
|
||||
if (!option) return false;
|
||||
return option.rating === 'mixed';
|
||||
}
|
||||
|
||||
export function generateClaudeInstruction(dimension: string, rating: string): string {
|
||||
const dimInstructions = CLAUDE_INSTRUCTIONS[dimension];
|
||||
if (dimInstructions && dimInstructions[rating]) {
|
||||
return dimInstructions[rating]!;
|
||||
}
|
||||
return \`Adapt to this developer's \${dimension.replace(/_/g, ' ')} preference: \${rating}.\`;
|
||||
}
|
||||
`;
|
||||
|
||||
const outPath = join(root, 'sdk/src/query/profile-questionnaire-data.ts');
|
||||
fs.writeFileSync(outPath, header);
|
||||
console.log('wrote', outPath);
|
||||
122
sdk/src/cli.ts
122
sdk/src/cli.ts
@@ -7,8 +7,9 @@
|
||||
*/
|
||||
|
||||
import { parseArgs } from 'node:util';
|
||||
import { execFile } from 'node:child_process';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { resolve, join } from 'node:path';
|
||||
import { resolve, join, isAbsolute } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { GSD } from './index.js';
|
||||
@@ -257,6 +258,57 @@ async function readStdin(): Promise<string> {
|
||||
});
|
||||
}
|
||||
|
||||
/** When false, unknown `gsd-sdk query` commands error instead of shelling out to gsd-tools.cjs. */
|
||||
function queryFallbackToCjsEnabled(): boolean {
|
||||
const v = process.env.GSD_QUERY_FALLBACK?.toLowerCase();
|
||||
if (v === 'off' || v === 'never' || v === 'false' || v === '0') return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
async function parseCliQueryJsonOutput(raw: string, projectDir: string): Promise<unknown> {
|
||||
const trimmed = raw.trim();
|
||||
if (trimmed === '') return null;
|
||||
let jsonStr = trimmed;
|
||||
if (jsonStr.startsWith('@file:')) {
|
||||
const rel = jsonStr.slice(6).trim();
|
||||
const { resolvePathUnderProject } = await import('./query/helpers.js');
|
||||
const filePath = await resolvePathUnderProject(projectDir, rel);
|
||||
jsonStr = await readFile(filePath, 'utf-8');
|
||||
}
|
||||
return JSON.parse(jsonStr);
|
||||
}
|
||||
|
||||
/** Map registry-style dotted command tokens to gsd-tools.cjs argv (space-separated subcommands). */
|
||||
function dottedCommandToCjsArgv(normCmd: string, normArgs: string[]): string[] {
|
||||
if (normCmd.includes('.')) {
|
||||
return [...normCmd.split('.'), ...normArgs];
|
||||
}
|
||||
return [normCmd, ...normArgs];
|
||||
}
|
||||
|
||||
function execGsdToolsCjsQuery(
|
||||
projectDir: string,
|
||||
gsdToolsPath: string,
|
||||
normCmd: string,
|
||||
normArgs: string[],
|
||||
ws: string | undefined,
|
||||
): Promise<{ stdout: string; stderr: string }> {
|
||||
const cjsArgv = dottedCommandToCjsArgv(normCmd, normArgs);
|
||||
const wsSuffix = ws ? ['--ws', ws] : [];
|
||||
const fullArgv = [gsdToolsPath, ...cjsArgv, ...wsSuffix];
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile(
|
||||
process.execPath,
|
||||
fullArgv,
|
||||
{ cwd: projectDir, maxBuffer: 10 * 1024 * 1024, env: { ...process.env } },
|
||||
(err, stdout, stderr) => {
|
||||
if (err) reject(err);
|
||||
else resolve({ stdout: stdout?.toString() ?? '', stderr: stderr?.toString() ?? '' });
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Main ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function main(argv: string[] = process.argv.slice(2)): Promise<void> {
|
||||
@@ -298,12 +350,6 @@ export async function main(argv: string[] = process.argv.slice(2)): Promise<void
|
||||
|
||||
const queryArgs = args.queryArgv ?? [];
|
||||
|
||||
if (queryArgs.length === 0 || !queryArgs[0]) {
|
||||
console.error('Error: "gsd-sdk query" requires a command');
|
||||
process.exitCode = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract --pick before dispatch
|
||||
const pickIdx = queryArgs.indexOf('--pick');
|
||||
let pickField: string | undefined;
|
||||
@@ -317,26 +363,60 @@ export async function main(argv: string[] = process.argv.slice(2)): Promise<void
|
||||
queryArgs.splice(pickIdx, 2);
|
||||
}
|
||||
|
||||
if (queryArgs.length === 0 || !queryArgs[0]) {
|
||||
console.error('Error: "gsd-sdk query" requires a command');
|
||||
process.exitCode = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const queryCommand = queryArgs[0];
|
||||
const { normalizeQueryCommand } = await import('./query/normalize-query-command.js');
|
||||
const [normCmd, normArgs] = normalizeQueryCommand(queryCommand, queryArgs.slice(1));
|
||||
if (!normCmd || !String(normCmd).trim()) {
|
||||
console.error('Error: "gsd-sdk query" requires a command');
|
||||
process.exitCode = 10;
|
||||
return;
|
||||
}
|
||||
const registry = createRegistry();
|
||||
const tokens = [...queryArgs];
|
||||
const tokens = [normCmd, ...normArgs];
|
||||
const matched = resolveQueryArgv(tokens, registry);
|
||||
if (!matched) {
|
||||
throw new GSDError(
|
||||
`Unknown command: "${tokens.join(' ')}". Use a registered \`gsd-sdk query\` subcommand (see sdk/src/query/QUERY-HANDLERS.md) or invoke \`node …/gsd-tools.cjs\` for CJS-only operations.`,
|
||||
ErrorClassification.Validation,
|
||||
if (!queryFallbackToCjsEnabled()) {
|
||||
throw new GSDError(
|
||||
`Unknown command: "${tokens.join(' ')}". Use a registered \`gsd-sdk query\` subcommand (see sdk/src/query/QUERY-HANDLERS.md) or invoke \`node …/gsd-tools.cjs\` for CJS-only operations. Set GSD_QUERY_FALLBACK=registered (default) to allow automatic fallback.`,
|
||||
ErrorClassification.Validation,
|
||||
);
|
||||
}
|
||||
const { resolveGsdToolsPath } = await import('./gsd-tools.js');
|
||||
const gsdPath = resolveGsdToolsPath(args.projectDir);
|
||||
console.error(
|
||||
`[gsd-sdk] '${tokens.join(' ')}' not in native registry; falling back to gsd-tools.cjs.`,
|
||||
);
|
||||
console.error('[gsd-sdk] Transparent bridge — prefer adding a native handler when parity matters.');
|
||||
const { stdout, stderr } = await execGsdToolsCjsQuery(
|
||||
args.projectDir,
|
||||
gsdPath,
|
||||
normCmd,
|
||||
normArgs,
|
||||
args.ws,
|
||||
);
|
||||
if (stderr.trim()) console.error(stderr.trimEnd());
|
||||
let output: unknown = await parseCliQueryJsonOutput(stdout, args.projectDir);
|
||||
if (pickField) {
|
||||
output = extractField(output, pickField);
|
||||
}
|
||||
console.log(JSON.stringify(output, null, 2));
|
||||
} else {
|
||||
const result = await registry.dispatch(matched.cmd, matched.args, args.projectDir, args.ws);
|
||||
let output: unknown = result.data;
|
||||
|
||||
if (pickField) {
|
||||
output = extractField(output, pickField);
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(output, null, 2));
|
||||
}
|
||||
|
||||
const result = await registry.dispatch(matched.cmd, matched.args, args.projectDir);
|
||||
|
||||
let output: unknown = result.data;
|
||||
|
||||
if (pickField) {
|
||||
output = extractField(output, pickField);
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(output, null, 2));
|
||||
} catch (err) {
|
||||
if (err instanceof GSDError) {
|
||||
console.error(`Error: ${err.message}`);
|
||||
|
||||
@@ -23,6 +23,8 @@ export interface WorkflowConfig {
|
||||
plan_check: boolean;
|
||||
verifier: boolean;
|
||||
nyquist_validation: boolean;
|
||||
/** Mirrors gsd-tools flat `config.tdd_mode` (from `workflow.tdd_mode`). */
|
||||
tdd_mode: boolean;
|
||||
auto_advance: boolean;
|
||||
node_repair: boolean;
|
||||
node_repair_budget: number;
|
||||
@@ -34,6 +36,8 @@ export interface WorkflowConfig {
|
||||
skip_discuss: boolean;
|
||||
/** Maximum self-discuss passes in auto/headless mode before forcing proceed. Default: 3. */
|
||||
max_discuss_passes: number;
|
||||
/** Subagent timeout in ms (matches `get-shit-done/bin/lib/core.cjs` default 300000). */
|
||||
subagent_timeout: number;
|
||||
}
|
||||
|
||||
export interface HooksConfig {
|
||||
@@ -52,6 +56,12 @@ export interface GSDConfig {
|
||||
workflow: WorkflowConfig;
|
||||
hooks: HooksConfig;
|
||||
agent_skills: Record<string, unknown>;
|
||||
/** Project slug for branch templates; mirrors gsd-tools `config.project_code`. */
|
||||
project_code?: string | null;
|
||||
/** Interactive vs headless; mirrors gsd-tools flat `config.mode`. */
|
||||
mode?: string;
|
||||
/** Internal auto-chain flag; mirrors gsd-tools `config._auto_chain_active`. */
|
||||
_auto_chain_active?: boolean;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@@ -76,6 +86,7 @@ export const CONFIG_DEFAULTS: GSDConfig = {
|
||||
plan_check: true,
|
||||
verifier: true,
|
||||
nyquist_validation: true,
|
||||
tdd_mode: false,
|
||||
auto_advance: false,
|
||||
node_repair: true,
|
||||
node_repair_budget: 2,
|
||||
@@ -86,11 +97,15 @@ export const CONFIG_DEFAULTS: GSDConfig = {
|
||||
discuss_mode: 'discuss',
|
||||
skip_discuss: false,
|
||||
max_discuss_passes: 3,
|
||||
subagent_timeout: 300000,
|
||||
},
|
||||
hooks: {
|
||||
context_warnings: true,
|
||||
},
|
||||
agent_skills: {},
|
||||
project_code: null,
|
||||
mode: 'interactive',
|
||||
_auto_chain_active: false,
|
||||
};
|
||||
|
||||
// ─── Loader ──────────────────────────────────────────────────────────────────
|
||||
|
||||
95
sdk/src/golden/capture.ts
Normal file
95
sdk/src/golden/capture.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Golden test helpers — run `gsd-tools.cjs` as a subprocess and capture JSON or raw stdout.
|
||||
*
|
||||
* Used by `golden.integration.test.ts` and `read-only-parity.integration.test.ts` to assert
|
||||
* SDK `createRegistry()` output matches the legacy CJS CLI.
|
||||
*/
|
||||
|
||||
import { execFile } from 'node:child_process';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { isAbsolute, join } from 'node:path';
|
||||
|
||||
import { resolveGsdToolsPath } from '../gsd-tools.js';
|
||||
|
||||
const CAPTURE_TIMEOUT_MS = 120_000;
|
||||
const MAX_BUFFER = 10 * 1024 * 1024;
|
||||
|
||||
function execGsdTools(
|
||||
projectDir: string,
|
||||
command: string,
|
||||
args: string[],
|
||||
): Promise<{ stdout: string; stderr: string }> {
|
||||
const script = resolveGsdToolsPath(projectDir);
|
||||
const fullArgs = [script, command, ...args];
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile(
|
||||
process.execPath,
|
||||
fullArgs,
|
||||
{
|
||||
cwd: projectDir,
|
||||
maxBuffer: MAX_BUFFER,
|
||||
timeout: CAPTURE_TIMEOUT_MS,
|
||||
env: { ...process.env },
|
||||
},
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
const code = typeof err === 'object' && err && 'code' in err ? String((err as NodeJS.ErrnoException).code) : '';
|
||||
const stderrStr = stderr?.toString() ?? '';
|
||||
reject(
|
||||
new Error(
|
||||
`gsd-tools failed (exit ${code}): ${stderrStr || (err instanceof Error ? err.message : String(err))}`,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
resolve({ stdout: stdout?.toString() ?? '', stderr: stderr?.toString() ?? '' });
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/** Same `@file:` indirection handling as {@link GSDTools} private parseOutput (cwd = projectDir). */
|
||||
async function parseGsdToolsJson(raw: string, projectDir: string): Promise<unknown> {
|
||||
const trimmed = raw.trim();
|
||||
if (trimmed === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
let jsonStr = trimmed;
|
||||
if (jsonStr.startsWith('@file:')) {
|
||||
const rel = jsonStr.slice(6).trim();
|
||||
const filePath = isAbsolute(rel) ? rel : join(projectDir, rel);
|
||||
try {
|
||||
jsonStr = await readFile(filePath, 'utf-8');
|
||||
} catch (err) {
|
||||
const reason = err instanceof Error ? err.message : String(err);
|
||||
throw new Error(`Failed to read gsd-tools @file: indirection at "${filePath}": ${reason}`);
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.parse(jsonStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run `node gsd-tools.cjs <command> [...args]` in `projectDir` and parse stdout as JSON.
|
||||
*/
|
||||
export async function captureGsdToolsOutput(
|
||||
command: string,
|
||||
args: string[],
|
||||
projectDir: string,
|
||||
): Promise<unknown> {
|
||||
const { stdout } = await execGsdTools(projectDir, command, args);
|
||||
return parseGsdToolsJson(stdout, projectDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run `node gsd-tools.cjs <command> [...args]` and return raw stdout (no JSON parse).
|
||||
*/
|
||||
export async function captureGsdToolsStdout(
|
||||
command: string,
|
||||
args: string[],
|
||||
projectDir: string,
|
||||
): Promise<string> {
|
||||
const { stdout } = await execGsdTools(projectDir, command, args);
|
||||
return stdout;
|
||||
}
|
||||
1
sdk/src/golden/fixtures/generate-slug.golden.json
Normal file
1
sdk/src/golden/fixtures/generate-slug.golden.json
Normal file
@@ -0,0 +1 @@
|
||||
{"slug":"my-phase"}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"type":"user","userType":"external","message":{"content":"profile sample message one"},"timestamp":1700000000000,"cwd":"/fixture/proj"}
|
||||
{"type":"assistant","message":{"content":"ok"},"timestamp":1700000000001}
|
||||
{"type":"user","userType":"external","message":{"content":"profile sample message two"},"timestamp":1700000000002,"cwd":"/fixture/proj"}
|
||||
26
sdk/src/golden/fixtures/summary-extract-sample.md
Normal file
26
sdk/src/golden/fixtures/summary-extract-sample.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
phase: "01"
|
||||
name: Golden Fixture
|
||||
one-liner: From frontmatter YAML
|
||||
key-files:
|
||||
- sdk/src/foo.ts
|
||||
key-decisions:
|
||||
- "Auth model: use JWT bearer tokens"
|
||||
- "Plain decision without colon split"
|
||||
patterns-established:
|
||||
- "Repository pattern for data access"
|
||||
tech-stack:
|
||||
added:
|
||||
- vitest
|
||||
- name: typescript
|
||||
requirements-completed:
|
||||
- REQ-GOLD-1
|
||||
---
|
||||
|
||||
# Phase 01: Golden Fixture Summary
|
||||
|
||||
**Bold one-liner pulled from body when FM lacks one-liner**
|
||||
|
||||
## Section
|
||||
|
||||
More body.
|
||||
15
sdk/src/golden/fixtures/uat-render-checkpoint-sample.md
Normal file
15
sdk/src/golden/fixtures/uat-render-checkpoint-sample.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
status: draft
|
||||
---
|
||||
# UAT
|
||||
|
||||
## Current Test
|
||||
|
||||
number: 1
|
||||
name: Login flow
|
||||
expected: |
|
||||
User can sign in
|
||||
|
||||
## Other
|
||||
|
||||
Placeholder section after Current Test.
|
||||
30
sdk/src/golden/golden-integration-covered.ts
Normal file
30
sdk/src/golden/golden-integration-covered.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Canonical commands exercised by `golden.integration.test.ts` (SDK dispatch vs
|
||||
* `gsd-tools.cjs` where applicable). Update when adding `describe` blocks there.
|
||||
*/
|
||||
|
||||
export const GOLDEN_INTEGRATION_MAIN_FILE_CANONICALS: readonly string[] = [
|
||||
'config-get',
|
||||
'config-set',
|
||||
'current-timestamp',
|
||||
'detect-custom-files',
|
||||
'docs-init',
|
||||
'find-phase',
|
||||
'frontmatter.get',
|
||||
'frontmatter.validate',
|
||||
'generate-slug',
|
||||
'init.execute-phase',
|
||||
'init.plan-phase',
|
||||
'init.quick',
|
||||
'init.resume',
|
||||
'init.verify-work',
|
||||
'intel.update',
|
||||
'progress.json',
|
||||
'roadmap.analyze',
|
||||
'state.sync',
|
||||
'state.validate',
|
||||
'template.select',
|
||||
'validate.consistency',
|
||||
'verify.phase-completeness',
|
||||
'verify.plan-structure',
|
||||
].sort((a, b) => a.localeCompare(b));
|
||||
7
sdk/src/golden/golden-mutation-covered.ts
Normal file
7
sdk/src/golden/golden-mutation-covered.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Mutation canonicals with explicit subprocess JSON parity vs `gsd-tools.cjs`
|
||||
* (see `mutation-subprocess.integration.test.ts` when present). Empty until those
|
||||
* tests land; other mutations rely on `MUTATION_DEFERRED_REASON` in golden-policy.
|
||||
*/
|
||||
|
||||
export const GOLDEN_MUTATION_SUBPROCESS_COVERED: readonly string[] = [];
|
||||
8
sdk/src/golden/golden-policy.test.ts
Normal file
8
sdk/src/golden/golden-policy.test.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { verifyGoldenPolicyComplete } from './golden-policy.js';
|
||||
|
||||
describe('golden policy', () => {
|
||||
it('every canonical registry command is integration-covered or excepted', () => {
|
||||
expect(() => verifyGoldenPolicyComplete()).not.toThrow();
|
||||
});
|
||||
});
|
||||
112
sdk/src/golden/golden-policy.ts
Normal file
112
sdk/src/golden/golden-policy.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Golden parity policy — every canonical registry command must be either:
|
||||
* - Listed in `GOLDEN_PARITY_INTEGRATION_COVERED` (subprocess CJS check under `sdk/src/golden/*integration*.test.ts`), or
|
||||
* - Documented in `GOLDEN_PARITY_EXCEPTIONS` with a stable rationale (mirrored in QUERY-HANDLERS.md § Golden registry coverage matrix).
|
||||
*/
|
||||
import { QUERY_MUTATION_COMMANDS } from '../query/index.js';
|
||||
import { getCanonicalRegistryCommands } from './registry-canonical-commands.js';
|
||||
import { GOLDEN_INTEGRATION_MAIN_FILE_CANONICALS } from './golden-integration-covered.js';
|
||||
import { GOLDEN_MUTATION_SUBPROCESS_COVERED } from './golden-mutation-covered.js';
|
||||
import { readOnlyGoldenCanonicals } from './read-only-golden-rows.js';
|
||||
|
||||
/** True if this canonical command participates in mutation event wiring (see QUERY_MUTATION_COMMANDS). */
|
||||
export function isMutationCanonicalCmd(canonical: string): boolean {
|
||||
const spaced = canonical.replace(/\./g, ' ');
|
||||
for (const m of QUERY_MUTATION_COMMANDS) {
|
||||
if (m === canonical || m === spaced) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const MUTATION_DEFERRED_REASON =
|
||||
'Listed in QUERY_MUTATION_COMMANDS — mutates `.planning/`, git, or profile files. Subprocess golden vs gsd-tools.cjs is covered where a tmp fixture or `--dry-run` exists in golden.integration.test.ts; otherwise handler parity lives in sdk/src/query/*-mutation.test.ts, commit.test.ts, phase-lifecycle.test.ts, workstream.test.ts, intel.test.ts, profile.test.ts, template.test.ts, docs-init.ts, or uat.test.ts as applicable.';
|
||||
|
||||
/** Registry commands with no `gsd-tools.cjs` analogue — cannot have subprocess JSON parity. */
|
||||
const NO_CJS_SUBPROCESS_REASON: Record<string, string> = {
|
||||
'phases.archive':
|
||||
'No `gsd-tools.cjs` command for `phases archive` (SDK-only). Covered in sdk/src/query/phase-lifecycle.test.ts.',
|
||||
'check.config-gates':
|
||||
'SDK-only decision-routing query (`.planning/research/decision-routing-audit.md` §3.3). Covered in sdk/src/query/config-gates.test.ts.',
|
||||
'check.phase-ready':
|
||||
'SDK-only decision-routing query (audit §3.4). Covered in sdk/src/query/phase-ready.test.ts.',
|
||||
'route.next-action':
|
||||
'SDK-only decision-routing query (audit §3.1). Covered in sdk/src/query/route-next-action.test.ts.',
|
||||
'check.auto-mode':
|
||||
'SDK-only decision-routing query (audit §3.5). Covered in sdk/src/query/check-auto-mode.test.ts.',
|
||||
'detect.phase-type':
|
||||
'SDK-only decision-routing query (audit §3.6). Covered in sdk/src/query/detect-phase-type.test.ts.',
|
||||
'check.completion':
|
||||
'SDK-only decision-routing query (audit §3.7). Covered in sdk/src/query/check-completion.test.ts.',
|
||||
'check.gates':
|
||||
'SDK-only decision-routing query (audit §3.2). Covered in sdk/src/query/check-gates.test.ts.',
|
||||
'check.verification-status':
|
||||
'SDK-only decision-routing query (audit §3.8). Covered in sdk/src/query/check-verification-status.test.ts.',
|
||||
'check.ship-ready':
|
||||
'SDK-only decision-routing query (audit §3.9). Covered in sdk/src/query/check-ship-ready.test.ts.',
|
||||
'phase.list-plans':
|
||||
'SDK-only listing helper for agents (no `gsd-tools.cjs` mirror). Covered in sdk/src/query/phase-list-queries.test.ts.',
|
||||
'phase.list-artifacts':
|
||||
'SDK-only artifact enumeration (no CJS mirror). Covered in sdk/src/query/phase-list-queries.test.ts.',
|
||||
'plan.task-structure':
|
||||
'SDK-only structured plan parse (no CJS mirror). Covered in sdk/src/query/plan-task-structure.test.ts.',
|
||||
'requirements.extract-from-plans':
|
||||
'SDK-only requirements aggregation (no CJS mirror). Covered in sdk/src/query/requirements-extract-from-plans.test.ts.',
|
||||
};
|
||||
|
||||
const READ_HANDLER_ONLY_REASON = (cmd: string) =>
|
||||
`No ` +
|
||||
'`toEqual` subprocess row yet for this read-only command — handler parity is covered in sdk/src/query/*.test.ts / decomposed-handlers.test.ts; add `captureGsdToolsOutput` + `registry.dispatch` in sdk/src/golden/ when JSON shapes are aligned (see QUERY-HANDLERS.md § Golden registry coverage matrix). Command: `' +
|
||||
cmd +
|
||||
'`.';
|
||||
|
||||
function buildIntegrationCoveredSet(): Set<string> {
|
||||
return new Set<string>([
|
||||
...GOLDEN_INTEGRATION_MAIN_FILE_CANONICALS,
|
||||
...readOnlyGoldenCanonicals(),
|
||||
...GOLDEN_MUTATION_SUBPROCESS_COVERED,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical commands with an explicit subprocess JSON check vs gsd-tools.cjs
|
||||
* (golden.integration.test.ts + read-only-parity.integration.test.ts).
|
||||
*/
|
||||
export const GOLDEN_PARITY_INTEGRATION_COVERED = buildIntegrationCoveredSet();
|
||||
|
||||
export const GOLDEN_PARITY_EXCEPTIONS: Record<string, string> = buildGoldenParityExceptions();
|
||||
|
||||
function buildGoldenParityExceptions(): Record<string, string> {
|
||||
const out: Record<string, string> = {};
|
||||
for (const c of getCanonicalRegistryCommands()) {
|
||||
if (GOLDEN_PARITY_INTEGRATION_COVERED.has(c)) continue;
|
||||
if (Object.prototype.hasOwnProperty.call(NO_CJS_SUBPROCESS_REASON, c)) {
|
||||
out[c] = NO_CJS_SUBPROCESS_REASON[c]!;
|
||||
continue;
|
||||
}
|
||||
if (isMutationCanonicalCmd(c)) {
|
||||
out[c] = MUTATION_DEFERRED_REASON;
|
||||
} else {
|
||||
out[c] = READ_HANDLER_ONLY_REASON(c);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function verifyGoldenPolicyComplete(): void {
|
||||
const canon = getCanonicalRegistryCommands();
|
||||
const missingException: string[] = [];
|
||||
for (const c of canon) {
|
||||
if (GOLDEN_PARITY_INTEGRATION_COVERED.has(c)) continue;
|
||||
if (!Object.prototype.hasOwnProperty.call(GOLDEN_PARITY_EXCEPTIONS, c)) missingException.push(c);
|
||||
}
|
||||
if (missingException.length) {
|
||||
throw new Error(`Missing GOLDEN_PARITY_EXCEPTIONS entry for:\n${missingException.join('\n')}`);
|
||||
}
|
||||
const stale: string[] = [];
|
||||
for (const c of GOLDEN_PARITY_INTEGRATION_COVERED) {
|
||||
if (!canon.includes(c)) stale.push(c);
|
||||
}
|
||||
if (stale.length) {
|
||||
throw new Error(`Stale GOLDEN_PARITY_INTEGRATION_COVERED entries:\n${stale.join('\n')}`);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user