get-shit-done-cc 1.37.1 → 1.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +6 -8
  2. package/agents/gsd-doc-classifier.md +168 -0
  3. package/agents/gsd-doc-synthesizer.md +204 -0
  4. package/bin/install.js +193 -1
  5. package/commands/gsd/import.md +1 -0
  6. package/commands/gsd/ingest-docs.md +42 -0
  7. package/commands/gsd/set-profile.md +1 -1
  8. package/commands/gsd/ultraplan-phase.md +33 -0
  9. package/get-shit-done/references/doc-conflict-engine.md +91 -0
  10. package/get-shit-done/workflows/import.md +6 -36
  11. package/get-shit-done/workflows/ingest-docs.md +326 -0
  12. package/get-shit-done/workflows/sketch.md +3 -1
  13. package/get-shit-done/workflows/ultraplan-phase.md +189 -0
  14. package/hooks/dist/gsd-read-injection-scanner.js +153 -0
  15. package/package.json +7 -2
  16. package/scripts/build-hooks.js +1 -0
  17. package/sdk/package-lock.json +1998 -0
  18. package/sdk/package.json +52 -0
  19. package/sdk/prompts/agents/gsd-executor.md +110 -0
  20. package/sdk/prompts/agents/gsd-phase-researcher.md +158 -0
  21. package/sdk/prompts/agents/gsd-plan-checker.md +145 -0
  22. package/sdk/prompts/agents/gsd-planner.md +214 -0
  23. package/sdk/prompts/agents/gsd-project-researcher.md +323 -0
  24. package/sdk/prompts/agents/gsd-research-synthesizer.md +237 -0
  25. package/sdk/prompts/agents/gsd-roadmapper.md +670 -0
  26. package/sdk/prompts/agents/gsd-verifier.md +144 -0
  27. package/sdk/prompts/templates/project.md +186 -0
  28. package/sdk/prompts/templates/requirements.md +231 -0
  29. package/sdk/prompts/templates/research-project/ARCHITECTURE.md +204 -0
  30. package/sdk/prompts/templates/research-project/FEATURES.md +147 -0
  31. package/sdk/prompts/templates/research-project/PITFALLS.md +200 -0
  32. package/sdk/prompts/templates/research-project/STACK.md +120 -0
  33. package/sdk/prompts/templates/research-project/SUMMARY.md +170 -0
  34. package/sdk/prompts/templates/roadmap.md +202 -0
  35. package/sdk/prompts/templates/state.md +175 -0
  36. package/sdk/prompts/workflows/discuss-phase.md +123 -0
  37. package/sdk/prompts/workflows/execute-plan.md +106 -0
  38. package/sdk/prompts/workflows/plan-phase.md +84 -0
  39. package/sdk/prompts/workflows/research-phase.md +44 -0
  40. package/sdk/prompts/workflows/verify-phase.md +142 -0
  41. package/sdk/src/assembled-prompts.test.ts +349 -0
  42. package/sdk/src/cli-transport.test.ts +388 -0
  43. package/sdk/src/cli-transport.ts +130 -0
  44. package/sdk/src/cli.test.ts +383 -0
  45. package/sdk/src/cli.ts +590 -0
  46. package/sdk/src/config.test.ts +168 -0
  47. package/sdk/src/config.ts +162 -0
  48. package/sdk/src/context-engine.test.ts +295 -0
  49. package/sdk/src/context-engine.ts +170 -0
  50. package/sdk/src/context-truncation.test.ts +163 -0
  51. package/sdk/src/context-truncation.ts +233 -0
  52. package/sdk/src/e2e.integration.test.ts +178 -0
  53. package/sdk/src/errors.ts +72 -0
  54. package/sdk/src/event-stream.test.ts +661 -0
  55. package/sdk/src/event-stream.ts +441 -0
  56. package/sdk/src/gsd-tools.test.ts +407 -0
  57. package/sdk/src/gsd-tools.ts +314 -0
  58. package/sdk/src/headless-prompts.test.ts +159 -0
  59. package/sdk/src/index.ts +326 -0
  60. package/sdk/src/init-e2e.integration.test.ts +136 -0
  61. package/sdk/src/init-runner.test.ts +783 -0
  62. package/sdk/src/init-runner.ts +734 -0
  63. package/sdk/src/lifecycle-e2e.integration.test.ts +258 -0
  64. package/sdk/src/logger.test.ts +149 -0
  65. package/sdk/src/logger.ts +113 -0
  66. package/sdk/src/milestone-runner.test.ts +421 -0
  67. package/sdk/src/phase-prompt.test.ts +538 -0
  68. package/sdk/src/phase-prompt.ts +264 -0
  69. package/sdk/src/phase-runner-types.test.ts +421 -0
  70. package/sdk/src/phase-runner.integration.test.ts +377 -0
  71. package/sdk/src/phase-runner.test.ts +2286 -0
  72. package/sdk/src/phase-runner.ts +1164 -0
  73. package/sdk/src/plan-parser.test.ts +528 -0
  74. package/sdk/src/plan-parser.ts +427 -0
  75. package/sdk/src/prompt-builder.test.ts +306 -0
  76. package/sdk/src/prompt-builder.ts +193 -0
  77. package/sdk/src/prompt-sanitizer.test.ts +260 -0
  78. package/sdk/src/prompt-sanitizer.ts +71 -0
  79. package/sdk/src/query/QUERY-HANDLERS.md +33 -0
  80. package/sdk/src/query/commit.test.ts +202 -0
  81. package/sdk/src/query/commit.ts +267 -0
  82. package/sdk/src/query/config-mutation.test.ts +356 -0
  83. package/sdk/src/query/config-mutation.ts +462 -0
  84. package/sdk/src/query/config-query.test.ts +161 -0
  85. package/sdk/src/query/config-query.ts +159 -0
  86. package/sdk/src/query/frontmatter-mutation.test.ts +259 -0
  87. package/sdk/src/query/frontmatter-mutation.ts +325 -0
  88. package/sdk/src/query/frontmatter.test.ts +266 -0
  89. package/sdk/src/query/frontmatter.ts +360 -0
  90. package/sdk/src/query/helpers.test.ts +254 -0
  91. package/sdk/src/query/helpers.ts +353 -0
  92. package/sdk/src/query/index.ts +461 -0
  93. package/sdk/src/query/init-complex.test.ts +232 -0
  94. package/sdk/src/query/init-complex.ts +578 -0
  95. package/sdk/src/query/init.test.ts +330 -0
  96. package/sdk/src/query/init.ts +979 -0
  97. package/sdk/src/query/intel.test.ts +90 -0
  98. package/sdk/src/query/intel.ts +316 -0
  99. package/sdk/src/query/phase-lifecycle.test.ts +1079 -0
  100. package/sdk/src/query/phase-lifecycle.ts +1453 -0
  101. package/sdk/src/query/phase.test.ts +307 -0
  102. package/sdk/src/query/phase.ts +340 -0
  103. package/sdk/src/query/pipeline.test.ts +169 -0
  104. package/sdk/src/query/pipeline.ts +243 -0
  105. package/sdk/src/query/profile.test.ts +54 -0
  106. package/sdk/src/query/profile.ts +367 -0
  107. package/sdk/src/query/progress.test.ts +156 -0
  108. package/sdk/src/query/progress.ts +267 -0
  109. package/sdk/src/query/registry.test.ts +181 -0
  110. package/sdk/src/query/registry.ts +174 -0
  111. package/sdk/src/query/roadmap.test.ts +275 -0
  112. package/sdk/src/query/roadmap.ts +470 -0
  113. package/sdk/src/query/skills.test.ts +73 -0
  114. package/sdk/src/query/skills.ts +56 -0
  115. package/sdk/src/query/state-mutation.test.ts +390 -0
  116. package/sdk/src/query/state-mutation.ts +761 -0
  117. package/sdk/src/query/state.test.ts +347 -0
  118. package/sdk/src/query/state.ts +395 -0
  119. package/sdk/src/query/stubs.test.ts +351 -0
  120. package/sdk/src/query/summary.test.ts +55 -0
  121. package/sdk/src/query/summary.ts +178 -0
  122. package/sdk/src/query/template.test.ts +179 -0
  123. package/sdk/src/query/template.ts +242 -0
  124. package/sdk/src/query/uat.test.ts +73 -0
  125. package/sdk/src/query/uat.ts +175 -0
  126. package/sdk/src/query/utils.test.ts +82 -0
  127. package/sdk/src/query/utils.ts +92 -0
  128. package/sdk/src/query/validate.test.ts +656 -0
  129. package/sdk/src/query/validate.ts +738 -0
  130. package/sdk/src/query/verify.test.ts +414 -0
  131. package/sdk/src/query/verify.ts +588 -0
  132. package/sdk/src/query/websearch.test.ts +31 -0
  133. package/sdk/src/query/websearch.ts +82 -0
  134. package/sdk/src/query/workspace.test.ts +119 -0
  135. package/sdk/src/query/workspace.ts +131 -0
  136. package/sdk/src/query/workstream.test.ts +51 -0
  137. package/sdk/src/query/workstream.ts +252 -0
  138. package/sdk/src/research-gate.test.ts +190 -0
  139. package/sdk/src/research-gate.ts +94 -0
  140. package/sdk/src/session-runner.test.ts +98 -0
  141. package/sdk/src/session-runner.ts +299 -0
  142. package/sdk/src/tool-scoping.test.ts +160 -0
  143. package/sdk/src/tool-scoping.ts +61 -0
  144. package/sdk/src/types.ts +913 -0
  145. package/sdk/src/workstream-utils.ts +33 -0
  146. package/sdk/src/ws-flag.test.ts +285 -0
  147. package/sdk/src/ws-transport.test.ts +161 -0
  148. package/sdk/src/ws-transport.ts +93 -0
  149. package/sdk/tsconfig.json +20 -0
package/README.md CHANGED
@@ -89,14 +89,11 @@ People who want to describe what they want and have it built correctly — witho
89
89
 
90
90
  Built-in quality gates catch real problems: schema drift detection flags ORM changes missing migrations, security enforcement anchors verification to threat models, and scope reduction detection prevents the planner from silently dropping your requirements.
91
91
 
92
- ### v1.36.0 Highlights
92
+ ### v1.37.0 Highlights
93
93
 
94
- - **Knowledge graph integration** — `/gsd-graphify` brings knowledge graphs to planning agents for richer context connections
95
- - **SDK typed query foundation** — Registry-based `gsd-sdk query` command with classified errors and handlers for state, roadmap, phase lifecycle, and config
96
- - **TDD pipeline mode** — Opt-in test-driven development workflow with `--tdd` flag
97
- - **Context-window-aware prompt thinning** — Automatic prompt size reduction for sub-200K models
98
- - **Project skills awareness** — 9 GSD agents now discover and use project-scoped skills
99
- - **30+ bug fixes** — Worktree safety, state management, installer paths, and health check optimizations
94
+ - **Spiking & sketching** — `/gsd-spike` runs 2–5 focused experiments with Given/When/Then verdicts; `/gsd-sketch` produces 2–3 interactive HTML mockup variants per design question — both store artifacts in `.planning/` and pair with wrap-up commands to package findings into project-local skills
95
+ - **Agent size-budget enforcement** — Tiered line-count limits (XL: 1 600, Large: 1 000, Default: 500) keep agent prompts lean; violations surface in CI
96
+ - **Shared boilerplate extraction** — Mandatory-initial-read and project-skills-discovery logic extracted to reference files, reducing duplication across a dozen agents
100
97
 
101
98
  ---
102
99
 
@@ -196,7 +193,7 @@ npx get-shit-done-cc --all --global # Install to all directories
196
193
 
197
194
  Use `--global` (`-g`) or `--local` (`-l`) to skip the location prompt.
198
195
  Use `--claude`, `--opencode`, `--gemini`, `--kilo`, `--codex`, `--copilot`, `--cursor`, `--windsurf`, `--antigravity`, `--augment`, `--trae`, `--qwen`, `--codebuddy`, `--cline`, or `--all` to skip the runtime prompt.
199
- Use `--sdk` to also install the GSD SDK CLI (`gsd-sdk`) for headless autonomous execution.
196
+ The GSD SDK CLI (`gsd-sdk`) is installed automatically (required by `/gsd-*` commands). Pass `--no-sdk` to skip the SDK install, or `--sdk` to force a reinstall.
200
197
 
201
198
  </details>
202
199
 
@@ -627,6 +624,7 @@ You're never locked in. The system adapts.
627
624
  | Command | What it does |
628
625
  |---------|--------------|
629
626
  | `/gsd-map-codebase [area]` | Analyze existing codebase before new-project |
627
+ | `/gsd-ingest-docs [dir]` | Scan a repo of mixed ADRs, PRDs, SPECs, and DOCs and bootstrap or merge the full `.planning/` setup in one pass — parallel classification, synthesis with precedence rules, and a three-bucket conflicts report |
630
628
 
631
629
  ### Phase Management
632
630
 
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: gsd-doc-classifier
3
+ description: Classifies a single planning document as ADR, PRD, SPEC, DOC, or UNKNOWN. Extracts title, scope summary, and cross-references. Spawned in parallel by /gsd-ingest-docs. Writes a JSON classification file and returns a one-line confirmation.
4
+ tools: Read, Write, Grep, Glob
5
+ color: yellow
6
+ # hooks:
7
+ # PostToolUse:
8
+ # - matcher: "Write|Edit"
9
+ # hooks:
10
+ # - type: command
11
+ # command: "true"
12
+ ---
13
+
14
+ <role>
15
+ You are a GSD doc classifier. You read ONE document and write a structured classification to `.planning/intel/classifications/`. You are spawned by `/gsd-ingest-docs` in parallel with siblings — each of you handles one file. Your output is consumed by `gsd-doc-synthesizer`.
16
+
17
+ **CRITICAL: Mandatory Initial Read**
18
+ If the prompt contains a `<required_reading>` block, use the `Read` tool to load every file listed there before doing anything else. That is your primary context.
19
+ </role>
20
+
21
+ <why_this_matters>
22
+ Your classification drives extraction. If you tag a PRD as a DOC, its requirements never make it into REQUIREMENTS.md. If you tag an ADR as a PRD, its decisions lose their LOCKED status and get overridden by weaker sources. Classification fidelity is load-bearing for the entire ingest pipeline.
23
+ </why_this_matters>
24
+
25
+ <taxonomy>
26
+
27
+ **ADR** (Architecture Decision Record)
28
+ - One architectural or technical decision, locked once made
29
+ - Hallmarks: `Status: Accepted|Proposed|Superseded`, numbered filename (`0001-`, `ADR-001-`), sections like `Context / Decision / Consequences`
30
+ - Content: trade-off analysis ending in one chosen path
31
+ - Produces: **locked decisions** (highest precedence by default)
32
+
33
+ **PRD** (Product Requirements Document)
34
+ - What the product/feature should do, from a user/business perspective
35
+ - Hallmarks: user stories, acceptance criteria, success metrics, goals/non-goals, "as a user..." language
36
+ - Content: requirements + scope, not implementation
37
+ - Produces: **requirements** (mid precedence)
38
+
39
+ **SPEC** (Technical Specification)
40
+ - How something is built — APIs, schemas, contracts, non-functional requirements
41
+ - Hallmarks: endpoint tables, request/response schemas, SLOs, protocol definitions, data models
42
+ - Content: implementation contracts the system must honor
43
+ - Produces: **technical constraints** (above PRD, below ADR)
44
+
45
+ **DOC** (General Documentation)
46
+ - Supporting context: guides, tutorials, design rationales, onboarding, runbooks
47
+ - Hallmarks: prose-heavy, tutorial structure, explanations without a decision or requirement
48
+ - Produces: **context only** (lowest precedence)
49
+
50
+ **UNKNOWN**
51
+ - Cannot be confidently placed in any of the above
52
+ - Record observed signals and let the synthesizer or user decide
53
+
54
+ </taxonomy>
55
+
56
+ <process>
57
+
58
+ <step name="parse_input">
59
+ The prompt gives you:
60
+ - `FILEPATH` — the document to classify (absolute path)
61
+ - `OUTPUT_DIR` — where to write your JSON output (e.g., `.planning/intel/classifications/`)
62
+ - `MANIFEST_TYPE` (optional) — if present, the manifest declared this file's type; treat as authoritative, skip heuristic+LLM classification
63
+ - `MANIFEST_PRECEDENCE` (optional) — override precedence if declared
64
+ </step>
65
+
66
+ <step name="heuristic_classification">
67
+ Before reading the file, apply fast filename/path heuristics:
68
+
69
+ - Path matches `**/adr/**` or filename `ADR-*.md` or `0001-*.md`…`9999-*.md` → strong ADR signal
70
+ - Path matches `**/prd/**` or filename `PRD-*.md` → strong PRD signal
71
+ - Path matches `**/spec/**`, `**/specs/**`, `**/rfc/**` or filename `SPEC-*.md`/`RFC-*.md` → strong SPEC signal
72
+ - Everything else → unclear, proceed to content analysis
73
+
74
+ If `MANIFEST_TYPE` is provided, skip to `extract_metadata` with that type.
75
+ </step>
76
+
77
+ <step name="read_and_analyze">
78
+ Read the file. Parse its frontmatter (if YAML) and scan the first 50 lines + any table-of-contents.
79
+
80
+ **Frontmatter signals (authoritative if present):**
81
+ - `type: adr|prd|spec|doc` → use directly
82
+ - `status: Accepted|Proposed|Superseded|Draft` → ADR signal
83
+ - `decision:` field → ADR
84
+ - `requirements:` or `user_stories:` → PRD
85
+
86
+ **Content signals:**
87
+ - Contains `## Decision` + `## Consequences` sections → ADR
88
+ - Contains `## User Stories` or `As a [user], I want` paragraphs → PRD
89
+ - Contains endpoint/schema tables, OpenAPI snippets, protocol fields → SPEC
90
+ - None of the above, prose only → DOC
91
+
92
+ **Ambiguity rule:** If two types compete at roughly equal strength, pick the one with the highest-precedence signal (ADR > SPEC > PRD > DOC). Record the ambiguity in `notes`.
93
+
94
+ **Confidence:**
95
+ - `high` — frontmatter or filename convention + matching content signals
96
+ - `medium` — content signals only, one dominant
97
+ - `low` — signals conflict or are thin → classify as best guess but flag the low confidence
98
+
99
+ If signals are too thin to choose, output `UNKNOWN` with `low` confidence and list observed signals in `notes`.
100
+ </step>
101
+
102
+ <step name="extract_metadata">
103
+ Regardless of type, extract:
104
+
105
+ - **title** — the document's H1, or the filename if no H1
106
+ - **summary** — one sentence (≤ 30 words) describing the doc's subject
107
+ - **scope** — list of concrete nouns the doc is about (systems, components, features)
108
+ - **cross_refs** — list of other doc paths referenced by this doc (markdown links, filename mentions). Include both relative and absolute paths as-written.
109
+ - **locked_markers** — for ADRs only: does status read `Accepted` (locked) vs `Proposed`/`Draft` (not locked)? Set `locked: true|false`.
110
+ </step>
111
+
112
+ <step name="write_output">
113
+ Write to `{OUTPUT_DIR}/{slug}.json` where `slug` is the filename without extension (replace non-alphanumerics with `-`).
114
+
115
+ JSON schema:
116
+
117
+ ```json
118
+ {
119
+ "source_path": "{FILEPATH}",
120
+ "type": "ADR|PRD|SPEC|DOC|UNKNOWN",
121
+ "confidence": "high|medium|low",
122
+ "manifest_override": false,
123
+ "title": "...",
124
+ "summary": "...",
125
+ "scope": ["...", "..."],
126
+ "cross_refs": ["path/to/other.md", "..."],
127
+ "locked": true,
128
+ "precedence": null,
129
+ "notes": "Only populated when confidence is low or ambiguity was resolved"
130
+ }
131
+ ```
132
+
133
+ Field rules:
134
+ - `manifest_override: true` only when `MANIFEST_TYPE` was provided
135
+ - `locked`: always `false` unless type is `ADR` with `Accepted` status
136
+ - `precedence`: `null` unless `MANIFEST_PRECEDENCE` was provided (then store the integer)
137
+ - `notes`: omit or empty string when confidence is `high`
138
+
139
+ **ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation.
140
+ </step>
141
+
142
+ <step name="return_confirmation">
143
+ Return one line to the orchestrator. No JSON, no document contents.
144
+
145
+ ```
146
+ Classified: {filename} → {TYPE} ({confidence}){, LOCKED if true}
147
+ ```
148
+ </step>
149
+
150
+ </process>
151
+
152
+ <anti_patterns>
153
+ Do NOT:
154
+ - Read the doc's transitive references — only classify what you were assigned
155
+ - Invent classification types beyond the five defined
156
+ - Output anything other than the one-line confirmation to the orchestrator
157
+ - Downgrade confidence silently — when unsure, output `UNKNOWN` with signals in `notes`
158
+ - Classify a `Proposed` or `Draft` ADR as `locked: true` — only `Accepted` counts as locked
159
+ - Use markdown tables or prose in your JSON output — stick to the schema
160
+ </anti_patterns>
161
+
162
+ <success_criteria>
163
+ - [ ] Exactly one JSON file written to OUTPUT_DIR
164
+ - [ ] Schema matches the template above, all required fields present
165
+ - [ ] Confidence level reflects the actual signal strength
166
+ - [ ] `locked` is true only for Accepted ADRs
167
+ - [ ] Confirmation line returned to orchestrator (≤ 1 line)
168
+ </success_criteria>
@@ -0,0 +1,204 @@
1
+ ---
2
+ name: gsd-doc-synthesizer
3
+ description: Synthesizes classified planning docs into a single consolidated context. Applies precedence rules, detects cross-ref cycles, enforces LOCKED-vs-LOCKED hard-blocks, and writes INGEST-CONFLICTS.md with three buckets (auto-resolved, competing-variants, unresolved-blockers). Spawned by /gsd-ingest-docs.
4
+ tools: Read, Write, Grep, Glob, Bash
5
+ color: orange
6
+ # hooks:
7
+ # PostToolUse:
8
+ # - matcher: "Write|Edit"
9
+ # hooks:
10
+ # - type: command
11
+ # command: "true"
12
+ ---
13
+
14
+ <role>
15
+ You are a GSD doc synthesizer. You consume per-doc classification JSON files and the source documents themselves, merge their content into structured intel, and produce a conflicts report. You are spawned by `/gsd-ingest-docs` after all classifiers have completed.
16
+
17
+ You do NOT prompt the user. You do NOT write PROJECT.md, REQUIREMENTS.md, or ROADMAP.md — those are produced downstream by `gsd-roadmapper` using your output. Your job is synthesis + conflict surfacing.
18
+
19
+ **CRITICAL: Mandatory Initial Read**
20
+ If the prompt contains a `<required_reading>` block, load every file listed there first — especially `references/doc-conflict-engine.md` which defines your conflict report format.
21
+ </role>
22
+
23
+ <why_this_matters>
24
+ You are the precedence-enforcing layer. Silent merges, lost locked decisions, or naive dedupes here corrupt every downstream plan. When in doubt, surface the conflict rather than pick.
25
+ </why_this_matters>
26
+
27
+ <inputs>
28
+ The prompt provides:
29
+ - `CLASSIFICATIONS_DIR` — directory containing per-doc `*.json` files produced by `gsd-doc-classifier`
30
+ - `INTEL_DIR` — where to write synthesized intel (typically `.planning/intel/`)
31
+ - `CONFLICTS_PATH` — where to write `INGEST-CONFLICTS.md` (typically `.planning/INGEST-CONFLICTS.md`)
32
+ - `MODE` — `new` or `merge`
33
+ - `EXISTING_CONTEXT` (merge mode only) — list of paths to existing `.planning/` files to check against (ROADMAP.md, PROJECT.md, REQUIREMENTS.md, CONTEXT.md files)
34
+ - `PRECEDENCE` — ordered list, default `["ADR", "SPEC", "PRD", "DOC"]`; may be overridden per-doc via the classification's `precedence` field
35
+ </inputs>
36
+
37
+ <precedence_rules>
38
+
39
+ **Default ordering:** `ADR > SPEC > PRD > DOC`. Higher-precedence sources win when content contradicts.
40
+
41
+ **Per-doc override:** If a classification has a non-null `precedence` integer, it overrides the default for that doc only. Lower integer = higher precedence.
42
+
43
+ **LOCKED decisions:**
44
+ - An ADR with `locked: true` produces decisions that cannot be auto-overridden by any source, including another LOCKED ADR.
45
+ - **LOCKED vs LOCKED:** two locked ADRs in the ingest set that contradict → hard BLOCKER, both in `new` and `merge` modes. Never auto-resolve.
46
+ - **LOCKED vs non-LOCKED:** LOCKED wins, logged in auto-resolved bucket with rationale.
47
+ - **Merge mode, LOCKED in ingest vs existing locked decision in CONTEXT.md:** hard BLOCKER.
48
+
49
+ **Same requirement, divergent acceptance criteria across PRDs:**
50
+ Do NOT pick one. Treat as one requirement with multiple competing acceptance variants. Write all variants to the `competing-variants` bucket for user resolution.
51
+
52
+ </precedence_rules>
53
+
54
+ <process>
55
+
56
+ <step name="load_classifications">
57
+ Read every `*.json` in `CLASSIFICATIONS_DIR`. Build an in-memory index keyed by `source_path`. Count by type.
58
+
59
+ If any classification is `UNKNOWN` with `low` confidence, note it — these will surface as unresolved-blockers (user must type-tag via manifest and re-run).
60
+ </step>
61
+
62
+ <step name="cycle_detection">
63
+ Build a directed graph from `cross_refs`. Run cycle detection (DFS with three-color marking).
64
+
65
+ If cycles exist:
66
+ - Record each cycle as an unresolved-blocker entry
67
+ - Do NOT proceed with synthesis on the cyclic set — synthesis loops produce garbage
68
+ - Docs outside the cycle may still be synthesized
69
+
70
+ **Cap:** Max traversal depth 50. If the ref graph exceeds this, abort with a BLOCKER entry directing user to shrink input via `--manifest`.
71
+ </step>
72
+
73
+ <step name="extract_per_type">
74
+ For each classified doc, read the source and extract per-type content. Write per-type intel files to `INTEL_DIR`:
75
+
76
+ - **ADRs** → `INTEL_DIR/decisions.md`
77
+ - One entry per ADR: title, source path, status (locked/proposed), decision statement, scope
78
+ - Preserve every decision separately; synthesis happens in the next step
79
+
80
+ - **PRDs** → `INTEL_DIR/requirements.md`
81
+ - One entry per requirement: ID (derive `REQ-{slug}`), source PRD path, description, acceptance criteria, scope
82
+ - One PRD usually yields multiple requirements
83
+
84
+ - **SPECs** → `INTEL_DIR/constraints.md`
85
+ - One entry per constraint: title, source path, type (api-contract | schema | nfr | protocol), content block
86
+
87
+ - **DOCs** → `INTEL_DIR/context.md`
88
+ - Running notes keyed by topic; appended verbatim with source attribution
89
+
90
+ Every entry must have `source: {path}` so downstream consumers can trace provenance.
91
+ </step>
92
+
93
+ <step name="detect_conflicts">
94
+ Walk the extracted intel to find conflicts. Apply precedence rules to classify each into a bucket.
95
+
96
+ **Conflict detection passes:**
97
+
98
+ 1. **LOCKED-vs-LOCKED ADR contradiction** — two ADRs with `locked: true` whose decision statements contradict on the same scope → `unresolved-blockers`
99
+ 2. **ADR-vs-existing locked CONTEXT.md (merge mode only)** — any ingest decision contradicts a decision in an existing `<decisions>` block marked locked → `unresolved-blockers`
100
+ 3. **PRD requirement overlap with different acceptance** — two PRDs define requirements on the same scope with non-identical acceptance criteria → `competing-variants`; preserve all variants
101
+ 4. **SPEC contradicts higher-precedence ADR** — SPEC asserts a technical decision contradicting a higher-precedence ADR decision → `auto-resolved` with ADR as winner, rationale logged
102
+ 5. **Lower-precedence contradicts higher** (non-locked) — `auto-resolved` with higher-precedence source winning
103
+ 6. **UNKNOWN-confidence-low docs** — `unresolved-blockers` (user must re-tag)
104
+ 7. **Cycle-detection blockers** (from previous step) — `unresolved-blockers`
105
+
106
+ Apply the `doc-conflict-engine` severity semantics:
107
+ - `unresolved-blockers` maps to [BLOCKER] — gate the workflow
108
+ - `competing-variants` maps to [WARNING] — user must pick before routing
109
+ - `auto-resolved` maps to [INFO] — recorded for transparency
110
+ </step>
111
+
112
+ <step name="write_conflicts_report">
113
+ Write `CONFLICTS_PATH` using the format from `references/doc-conflict-engine.md`. Three buckets, plain text, no tables.
114
+
115
+ Structure:
116
+
117
+ ```
118
+ ## Conflict Detection Report
119
+
120
+ ### BLOCKERS ({N})
121
+
122
+ [BLOCKER] LOCKED ADR contradiction
123
+ Found: docs/adr/0004-db.md declares "Postgres" (Accepted)
124
+ Expected: docs/adr/0011-db.md declares "DynamoDB" (Accepted) — same scope "primary datastore"
125
+ → Resolve by marking one ADR Superseded, or set precedence in --manifest
126
+
127
+ ### WARNINGS ({N})
128
+
129
+ [WARNING] Competing acceptance variants for REQ-user-auth
130
+ Found: docs/prd/auth-v1.md requires "email+password", docs/prd/auth-v2.md requires "SSO only"
131
+ Impact: Synthesis cannot pick without losing intent
132
+ → Choose one variant or split into two requirements before routing
133
+
134
+ ### INFO ({N})
135
+
136
+ [INFO] Auto-resolved: ADR > SPEC on cache layer
137
+ Note: docs/adr/0007-cache.md (Accepted) chose Redis; docs/specs/cache-api.md assumed Memcached — ADR wins, SPEC updated to Redis in synthesized intel
138
+ ```
139
+
140
+ Every entry requires `source:` references for every claim.
141
+ </step>
142
+
143
+ <step name="write_synthesis_summary">
144
+ Write `INTEL_DIR/SYNTHESIS.md` — a human-readable summary of what was synthesized:
145
+
146
+ - Doc counts by type
147
+ - Decisions locked (count + source paths)
148
+ - Requirements extracted (count, with IDs)
149
+ - Constraints (count + type breakdown)
150
+ - Context topics (count)
151
+ - Conflicts: N blockers, N competing-variants, N auto-resolved
152
+ - Pointer to `CONFLICTS_PATH` for detail
153
+ - Pointer to per-type intel files
154
+
155
+ This is the single entry point `gsd-roadmapper` reads.
156
+
157
+ **ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation.
158
+ </step>
159
+
160
+ <step name="return_confirmation">
161
+ Return ≤ 10 lines to the orchestrator:
162
+
163
+ ```
164
+ ## Synthesis Complete
165
+
166
+ Docs synthesized: {N} ({breakdown})
167
+ Decisions locked: {N}
168
+ Requirements: {N}
169
+ Conflicts: {N} blockers, {N} variants, {N} auto-resolved
170
+
171
+ Intel: {INTEL_DIR}/
172
+ Report: {CONFLICTS_PATH}
173
+
174
+ {If blockers > 0: "STATUS: BLOCKED — review report before routing"}
175
+ {If variants > 0: "STATUS: AWAITING USER — competing variants need resolution"}
176
+ {Else: "STATUS: READY — safe to route"}
177
+ ```
178
+
179
+ Do NOT dump intel contents. The orchestrator reads the files directly.
180
+ </step>
181
+
182
+ </process>
183
+
184
+ <anti_patterns>
185
+ Do NOT:
186
+ - Pick a winner between two LOCKED ADRs — always BLOCK
187
+ - Merge competing PRD acceptance criteria into a single "combined" criterion — preserve all variants
188
+ - Write PROJECT.md, REQUIREMENTS.md, ROADMAP.md, or STATE.md — those are the roadmapper's job
189
+ - Skip cycle detection — synthesis loops produce garbage output
190
+ - Use markdown tables in the conflicts report — violates the doc-conflict-engine contract
191
+ - Auto-resolve by filename order, timestamp, or arbitrary tiebreaker — precedence rules only
192
+ - Silently drop `UNKNOWN`-confidence-low docs — they must surface as blockers
193
+ </anti_patterns>
194
+
195
+ <success_criteria>
196
+ - [ ] All classifications in CLASSIFICATIONS_DIR consumed
197
+ - [ ] Cycle detection run on cross-ref graph
198
+ - [ ] Per-type intel files written to INTEL_DIR
199
+ - [ ] INGEST-CONFLICTS.md written with three buckets, format per `doc-conflict-engine.md`
200
+ - [ ] SYNTHESIS.md written as entry point for downstream consumers
201
+ - [ ] LOCKED-vs-LOCKED contradictions surface as BLOCKERs, never auto-resolved
202
+ - [ ] Competing acceptance variants preserved, never merged
203
+ - [ ] Confirmation returned (≤ 10 lines)
204
+ </success_criteria>
package/bin/install.js CHANGED
@@ -10,6 +10,8 @@ const crypto = require('crypto');
10
10
  const cyan = '\x1b[36m';
11
11
  const green = '\x1b[32m';
12
12
  const yellow = '\x1b[33m';
13
+ const red = '\x1b[31m';
14
+ const bold = '\x1b[1m';
13
15
  const dim = '\x1b[2m';
14
16
  const reset = '\x1b[0m';
15
17
 
@@ -77,6 +79,13 @@ const hasBoth = args.includes('--both'); // Legacy flag, keeps working
77
79
  const hasAll = args.includes('--all');
78
80
  const hasUninstall = args.includes('--uninstall') || args.includes('-u');
79
81
  const hasPortableHooks = args.includes('--portable-hooks') || process.env.GSD_PORTABLE_HOOKS === '1';
82
+ const hasSdk = args.includes('--sdk');
83
+ const hasNoSdk = args.includes('--no-sdk');
84
+
85
+ if (hasSdk && hasNoSdk) {
86
+ console.error(` ${yellow}Cannot specify both --sdk and --no-sdk${reset}`);
87
+ process.exit(1);
88
+ }
80
89
 
81
90
  // Runtime selection - can be set by flags or interactive prompt
82
91
  let selectedRuntimes = [];
@@ -5818,6 +5827,7 @@ function install(isGlobal, runtime = 'claude') {
5818
5827
  let content = fs.readFileSync(srcFile, 'utf8');
5819
5828
  content = content.replace(/'\.claude'/g, configDirReplacement);
5820
5829
  content = content.replace(/\/\.claude\//g, `/${getDirName(runtime)}/`);
5830
+ content = content.replace(/\.claude\//g, `${getDirName(runtime)}/`);
5821
5831
  if (isQwen) {
5822
5832
  content = content.replace(/CLAUDE\.md/g, 'QWEN.md');
5823
5833
  content = content.replace(/\bClaude Code\b/g, 'Qwen Code');
@@ -5943,6 +5953,7 @@ function install(isGlobal, runtime = 'claude') {
5943
5953
  let content = fs.readFileSync(srcFile, 'utf8');
5944
5954
  content = content.replace(/'\.claude'/g, configDirReplacement);
5945
5955
  content = content.replace(/\/\.claude\//g, `/${getDirName(runtime)}/`);
5956
+ content = content.replace(/\.claude\//g, `${getDirName(runtime)}/`);
5946
5957
  content = content.replace(/\{\{GSD_VERSION\}\}/g, pkg.version);
5947
5958
  fs.writeFileSync(destFile, content);
5948
5959
  try { fs.chmodSync(destFile, 0o755); } catch (e) { /* Windows */ }
@@ -6626,6 +6637,179 @@ function promptLocation(runtimes) {
6626
6637
  });
6627
6638
  }
6628
6639
 
6640
+ /**
6641
+ * Build `@gsd-build/sdk` from the in-repo `sdk/` source tree and install the
6642
+ * resulting `gsd-sdk` binary globally so workflow commands that shell out to
6643
+ * `gsd-sdk query …` succeed.
6644
+ *
6645
+ * We build from source rather than `npm install -g @gsd-build/sdk` because the
6646
+ * npm-published package lags the source tree and shipping a stale SDK breaks
6647
+ * every /gsd-* command that depends on newer query handlers.
6648
+ *
6649
+ * Skip if --no-sdk. Skip if already on PATH (unless --sdk was explicit).
6650
+ * Failures are FATAL — we exit non-zero so install does not complete with a
6651
+ * silently broken SDK (issue #2439). Set GSD_ALLOW_OFF_PATH=1 to downgrade the
6652
+ * post-install PATH verification to a warning (exit code 2) for users with an
6653
+ * intentionally restricted PATH who will wire things up manually.
6654
+ */
6655
+
6656
+ /**
6657
+ * Resolve `gsd-sdk` on PATH. Uses `command -v` via `sh -c` on POSIX (portable
6658
+ * across sh/bash/zsh) and `where` on Windows. Returns trimmed path or null.
6659
+ */
6660
+ function resolveGsdSdk() {
6661
+ const { spawnSync } = require('child_process');
6662
+ if (process.platform === 'win32') {
6663
+ const r = spawnSync('where', ['gsd-sdk'], { encoding: 'utf-8' });
6664
+ if (r.status === 0 && r.stdout && r.stdout.trim()) {
6665
+ return r.stdout.trim().split('\n')[0].trim();
6666
+ }
6667
+ return null;
6668
+ }
6669
+ const r = spawnSync('sh', ['-c', 'command -v gsd-sdk'], { encoding: 'utf-8' });
6670
+ if (r.status === 0 && r.stdout && r.stdout.trim()) {
6671
+ return r.stdout.trim();
6672
+ }
6673
+ return null;
6674
+ }
6675
+
6676
+ /**
6677
+ * Best-effort detection of the user's shell rc file for PATH remediation hints.
6678
+ */
6679
+ function detectShellRc() {
6680
+ const path = require('path');
6681
+ const shell = process.env.SHELL || '';
6682
+ const home = process.env.HOME || '~';
6683
+ if (/\/zsh$/.test(shell)) return { shell: 'zsh', rc: path.join(home, '.zshrc') };
6684
+ if (/\/bash$/.test(shell)) return { shell: 'bash', rc: path.join(home, '.bashrc') };
6685
+ if (/\/fish$/.test(shell)) return { shell: 'fish', rc: path.join(home, '.config', 'fish', 'config.fish') };
6686
+ return { shell: 'sh', rc: path.join(home, '.profile') };
6687
+ }
6688
+
6689
+ /**
6690
+ * Emit a red fatal banner and exit. Prints actionable PATH remediation when
6691
+ * the global install succeeded but the bin dir is not on PATH.
6692
+ *
6693
+ * If exitCode is 2, this is the "off-PATH" case and GSD_ALLOW_OFF_PATH respect
6694
+ * is applied by the caller; we only print.
6695
+ */
6696
+ function emitSdkFatal(reason, { globalBin, exitCode }) {
6697
+ const { shell, rc } = detectShellRc();
6698
+ const bar = '━'.repeat(72);
6699
+ const redBold = `${red}${bold}`;
6700
+
6701
+ console.error('');
6702
+ console.error(`${redBold}${bar}${reset}`);
6703
+ console.error(`${redBold} ✗ GSD SDK install failed — /gsd-* commands will not work${reset}`);
6704
+ console.error(`${redBold}${bar}${reset}`);
6705
+ console.error(` ${red}Reason:${reset} ${reason}`);
6706
+
6707
+ if (globalBin) {
6708
+ console.error('');
6709
+ console.error(` ${yellow}gsd-sdk was installed to:${reset}`);
6710
+ console.error(` ${cyan}${globalBin}${reset}`);
6711
+ console.error('');
6712
+ console.error(` ${yellow}Your shell's PATH does not include this directory.${reset}`);
6713
+ console.error(` Add it by running:`);
6714
+ if (shell === 'fish') {
6715
+ console.error(` ${cyan}fish_add_path "${globalBin}"${reset}`);
6716
+ console.error(` (or append to ${rc})`);
6717
+ } else {
6718
+ console.error(` ${cyan}echo 'export PATH="${globalBin}:$PATH"' >> ${rc}${reset}`);
6719
+ console.error(` ${cyan}source ${rc}${reset}`);
6720
+ }
6721
+ console.error('');
6722
+ console.error(` Then verify: ${cyan}command -v gsd-sdk${reset}`);
6723
+ if (exitCode === 2) {
6724
+ console.error('');
6725
+ console.error(` ${dim}(GSD_ALLOW_OFF_PATH=1 set → exit ${exitCode} instead of hard failure)${reset}`);
6726
+ }
6727
+ } else {
6728
+ console.error('');
6729
+ console.error(` Build manually to retry:`);
6730
+ console.error(` ${cyan}cd <install-dir>/sdk && npm install && npm run build && npm install -g .${reset}`);
6731
+ }
6732
+
6733
+ console.error(`${redBold}${bar}${reset}`);
6734
+ console.error('');
6735
+ process.exit(exitCode);
6736
+ }
6737
+
6738
+ function installSdkIfNeeded() {
6739
+ if (hasNoSdk) {
6740
+ console.log(`\n ${dim}Skipping GSD SDK install (--no-sdk)${reset}`);
6741
+ return;
6742
+ }
6743
+
6744
+ const { spawnSync } = require('child_process');
6745
+ const path = require('path');
6746
+ const fs = require('fs');
6747
+
6748
+ if (!hasSdk) {
6749
+ const resolved = resolveGsdSdk();
6750
+ if (resolved) {
6751
+ console.log(` ${green}✓${reset} GSD SDK already installed (gsd-sdk on PATH at ${resolved})`);
6752
+ return;
6753
+ }
6754
+ }
6755
+
6756
+ // Locate the in-repo sdk/ directory relative to this installer file.
6757
+ // For global npm installs this resolves inside the published package dir;
6758
+ // for git-based installs (npx github:..., local clone) it resolves to the
6759
+ // repo's sdk/ tree. Both contain the source tree because root package.json
6760
+ // includes "sdk" in its `files` array.
6761
+ const sdkDir = path.resolve(__dirname, '..', 'sdk');
6762
+ const sdkPackageJson = path.join(sdkDir, 'package.json');
6763
+
6764
+ if (!fs.existsSync(sdkPackageJson)) {
6765
+ emitSdkFatal(`SDK source tree not found at ${sdkDir}.`, { globalBin: null, exitCode: 1 });
6766
+ }
6767
+
6768
+ console.log(`\n ${cyan}Building GSD SDK from source (${sdkDir})…${reset}`);
6769
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
6770
+
6771
+ // 1. Install sdk build-time dependencies (tsc, etc.)
6772
+ const installResult = spawnSync(npmCmd, ['install'], { cwd: sdkDir, stdio: 'inherit' });
6773
+ if (installResult.status !== 0) {
6774
+ emitSdkFatal('Failed to `npm install` in sdk/.', { globalBin: null, exitCode: 1 });
6775
+ }
6776
+
6777
+ // 2. Compile TypeScript → sdk/dist/
6778
+ const buildResult = spawnSync(npmCmd, ['run', 'build'], { cwd: sdkDir, stdio: 'inherit' });
6779
+ if (buildResult.status !== 0) {
6780
+ emitSdkFatal('Failed to `npm run build` in sdk/.', { globalBin: null, exitCode: 1 });
6781
+ }
6782
+
6783
+ // 3. Install the built package globally so `gsd-sdk` lands on PATH.
6784
+ const globalResult = spawnSync(npmCmd, ['install', '-g', '.'], { cwd: sdkDir, stdio: 'inherit' });
6785
+ if (globalResult.status !== 0) {
6786
+ emitSdkFatal('Failed to `npm install -g .` from sdk/.', { globalBin: null, exitCode: 1 });
6787
+ }
6788
+
6789
+ // 4. Verify gsd-sdk is actually resolvable on PATH. npm's global bin dir is
6790
+ // not always on the current shell's PATH (Homebrew prefixes, nvm setups,
6791
+ // unconfigured npm prefix), so a zero exit status from `npm install -g`
6792
+ // alone is not proof of a working binary (issue #2439 root cause).
6793
+ const resolved = resolveGsdSdk();
6794
+ if (resolved) {
6795
+ console.log(` ${green}✓${reset} Built and installed GSD SDK from source (gsd-sdk resolved at ${resolved})`);
6796
+ return;
6797
+ }
6798
+
6799
+ // Off-PATH: resolve npm global bin dir for actionable remediation.
6800
+ const prefixResult = spawnSync(npmCmd, ['config', 'get', 'prefix'], { encoding: 'utf-8' });
6801
+ const prefix = prefixResult.status === 0 ? (prefixResult.stdout || '').trim() : null;
6802
+ const globalBin = prefix
6803
+ ? (process.platform === 'win32' ? prefix : path.join(prefix, 'bin'))
6804
+ : null;
6805
+
6806
+ const allowOffPath = process.env.GSD_ALLOW_OFF_PATH === '1';
6807
+ emitSdkFatal(
6808
+ 'Built and installed GSD SDK, but `gsd-sdk` is not on your PATH.',
6809
+ { globalBin, exitCode: allowOffPath ? 2 : 1 },
6810
+ );
6811
+ }
6812
+
6629
6813
  /**
6630
6814
  * Install GSD for all selected runtimes
6631
6815
  */
@@ -6641,7 +6825,15 @@ function installAllRuntimes(runtimes, isGlobal, isInteractive) {
6641
6825
  const primaryStatuslineResult = results.find(r => statuslineRuntimes.includes(r.runtime));
6642
6826
 
6643
6827
  const finalize = (shouldInstallStatusline) => {
6644
- // Handle SDK installation before printing final summaries
6828
+ // Build @gsd-build/sdk from the in-repo sdk/ source and install it globally
6829
+ // so `gsd-sdk` lands on PATH. Every /gsd-* command shells out to
6830
+ // `gsd-sdk query …`; without this, commands fail with "command not found:
6831
+ // gsd-sdk". The npm-published @gsd-build/sdk is kept intentionally frozen
6832
+ // at an older version; we always build from source so users get the SDK
6833
+ // that matches the installed GSD version.
6834
+ // Runs by default; skip with --no-sdk. Idempotent when already present.
6835
+ installSdkIfNeeded();
6836
+
6645
6837
  const printSummaries = () => {
6646
6838
  for (const result of results) {
6647
6839
  const useStatusline = statuslineRuntimes.includes(result.runtime) && shouldInstallStatusline;
@@ -25,6 +25,7 @@ Future: `--prd` mode for PRD extraction is planned for a follow-up PR.
25
25
  @~/.claude/get-shit-done/workflows/import.md
26
26
  @~/.claude/get-shit-done/references/ui-brand.md
27
27
  @~/.claude/get-shit-done/references/gate-prompts.md
28
+ @~/.claude/get-shit-done/references/doc-conflict-engine.md
28
29
  </execution_context>
29
30
 
30
31
  <context>