@opengsd/gsd-core 1.3.0 → 1.4.0-rc.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 (98) hide show
  1. package/agents/gsd-advisor-researcher.md +1 -20
  2. package/agents/gsd-ai-researcher.md +1 -20
  3. package/agents/gsd-domain-researcher.md +1 -20
  4. package/agents/gsd-executor.md +1 -1
  5. package/agents/gsd-phase-researcher.md +92 -166
  6. package/agents/gsd-planner.md +9 -36
  7. package/agents/gsd-project-researcher.md +62 -141
  8. package/agents/gsd-ui-researcher.md +2 -21
  9. package/agents/gsd-verifier.md +8 -2
  10. package/bin/install.js +85 -4
  11. package/commands/gsd/graphify.md +11 -6
  12. package/commands/gsd/import.md +6 -2
  13. package/commands/gsd/plan-phase.md +2 -2
  14. package/gsd-core/bin/check-latest-version.cjs +3 -2
  15. package/gsd-core/bin/gsd-tools.cjs +238 -32
  16. package/gsd-core/bin/lib/check-command-router.cjs +1 -0
  17. package/gsd-core/bin/lib/cli-exit.cjs +42 -0
  18. package/gsd-core/bin/lib/command-routing-hub.cjs +1 -1
  19. package/gsd-core/bin/lib/commands.cjs +5 -4
  20. package/gsd-core/bin/lib/config.cjs +28 -4
  21. package/gsd-core/bin/lib/core.cjs +72 -28
  22. package/gsd-core/bin/lib/graphify.cjs +2 -2
  23. package/gsd-core/bin/lib/init-command-router.cjs +2 -2
  24. package/gsd-core/bin/lib/init.cjs +19 -3
  25. package/gsd-core/bin/lib/installer-migrations.cjs +61 -22
  26. package/gsd-core/bin/lib/intel.cjs +3 -20
  27. package/gsd-core/bin/lib/package-legitimacy.cjs +368 -0
  28. package/gsd-core/bin/lib/phase.cjs +3 -3
  29. package/gsd-core/bin/lib/research-provider.cjs +137 -0
  30. package/gsd-core/bin/lib/research-store.cjs +167 -0
  31. package/gsd-core/bin/lib/roadmap-upgrade.cjs +4 -19
  32. package/gsd-core/bin/lib/security.cjs +73 -0
  33. package/gsd-core/bin/lib/shell-command-projection.cjs +3 -0
  34. package/gsd-core/bin/lib/validate.cjs +2 -2
  35. package/gsd-core/bin/lib/verification-command-router.cjs +31 -0
  36. package/gsd-core/bin/lib/verification.cjs +193 -0
  37. package/gsd-core/bin/lib/verify.cjs +2 -2
  38. package/gsd-core/bin/lib/workstream-inventory.cjs +1 -1
  39. package/gsd-core/bin/lib/worktree-base-ref.cjs +325 -0
  40. package/gsd-core/bin/lib/worktree-safety.cjs +31 -0
  41. package/gsd-core/bin/shared/config-schema.manifest.json +2 -1
  42. package/gsd-core/bin/verify-reapply-patches.cjs +8 -11
  43. package/gsd-core/references/planner-load-graph-context.md +36 -0
  44. package/gsd-core/references/planning-config.md +3 -1
  45. package/gsd-core/references/research-documentation-lookup.md +29 -0
  46. package/gsd-core/references/research-philosophy.md +29 -0
  47. package/gsd-core/references/research-verification-protocol.md +27 -0
  48. package/gsd-core/workflows/execute-phase.md +19 -8
  49. package/gsd-core/workflows/help/modes/full.md +2 -2
  50. package/gsd-core/workflows/ingest-docs.md +3 -2
  51. package/gsd-core/workflows/plan-phase.md +14 -10
  52. package/gsd-core/workflows/plan-review-convergence.md +3 -3
  53. package/gsd-core/workflows/review.md +22 -5
  54. package/gsd-core/workflows/ship.md +5 -8
  55. package/gsd-core/workflows/spec-phase.md +2 -1
  56. package/gsd-core/workflows/update.md +2 -1
  57. package/hooks/dist/gsd-context-monitor.js +1 -1
  58. package/hooks/dist/gsd-workflow-guard.js +1 -0
  59. package/hooks/dist/gsd-worktree-path-guard.js +1 -1
  60. package/hooks/gsd-context-monitor.js +1 -1
  61. package/hooks/gsd-workflow-guard.js +1 -0
  62. package/hooks/gsd-worktree-path-guard.js +1 -1
  63. package/package.json +4 -1
  64. package/scripts/affected-tests-lib.cjs +3 -2
  65. package/scripts/changeset/cli.cjs +183 -28
  66. package/scripts/changeset/lint.cjs +5 -4
  67. package/scripts/changeset/new.cjs +4 -4
  68. package/scripts/check-alias-drift.cjs +77 -71
  69. package/scripts/check-env.cjs +185 -179
  70. package/scripts/check-npm-integrity.cjs +115 -109
  71. package/scripts/ci-guard-runner.cjs +11 -5
  72. package/scripts/ci-prepare-test-scope.cjs +27 -22
  73. package/scripts/ci-rebase-check.cjs +46 -45
  74. package/scripts/ci-test-scope.cjs +6 -4
  75. package/scripts/diff-touches-shipped-paths.cjs +52 -44
  76. package/scripts/gen-inventory-manifest.cjs +38 -32
  77. package/scripts/gen-research-agents.cjs +276 -0
  78. package/scripts/lib/cli-exit.cjs +56 -0
  79. package/scripts/lint-command-contract.cjs +28 -22
  80. package/scripts/lint-descriptions.cjs +32 -28
  81. package/scripts/lint-docs-required.cjs +4 -4
  82. package/scripts/lint-legacy-dir-name.cjs +56 -52
  83. package/scripts/lint-pr-check-project-dir.cjs +3 -1
  84. package/scripts/lint-shell-command-projection-drift.cjs +27 -22
  85. package/scripts/lint-skill-deps.cjs +31 -26
  86. package/scripts/lint-test-file-count.allowlist.json +1 -0
  87. package/scripts/lint-test-file-count.cjs +5 -4
  88. package/scripts/mutation-matrix.cjs +6 -3
  89. package/scripts/prompt-injection-scan.sh +1 -1
  90. package/scripts/release-notes/format-github-release-notes.cjs +8 -3
  91. package/scripts/release-tarball-smoke.cjs +6 -4
  92. package/scripts/research-profiles.cjs +149 -0
  93. package/scripts/run-affected-tests.cjs +2 -1
  94. package/scripts/run-cross-platform-tests.cjs +11 -7
  95. package/scripts/run-tests.cjs +8 -7
  96. package/scripts/strip-prose-atrefs.cjs +1 -1
  97. package/scripts/sync-runtime-launcher.cjs +0 -3
  98. package/scripts/verify-npm-publish.cjs +14 -26
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: gsd-project-researcher
3
3
  description: Researches domain ecosystem before roadmap creation. Produces files in .planning/research/ consumed during roadmap creation. Spawned by /gsd:new-project or /gsd:new-milestone orchestrators.
4
- tools: Read, Write, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*
4
+ tools: Read, Write, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*
5
5
  color: cyan
6
6
  # hooks:
7
7
  # PostToolUse:
@@ -33,53 +33,11 @@ Your files feed the roadmap:
33
33
  </role>
34
34
 
35
35
  <documentation_lookup>
36
- When you need library or framework documentation, check in this order:
37
-
38
- 1. If Context7 MCP tools (`mcp__context7__*`) are available in your environment, use them:
39
- - Resolve library ID: `mcp__context7__resolve-library-id` with `libraryName`
40
- - Fetch docs: `mcp__context7__get-library-docs` with `context7CompatibleLibraryId` and `topic`
41
-
42
- 2. If Context7 MCP is not available (upstream bug anthropics/claude-code#13898 strips MCP
43
- tools from agents with a `tools:` frontmatter restriction), use the CLI fallback via Bash:
44
-
45
- Step 1 — Resolve library ID:
46
- ```bash
47
- npx --yes ctx7@latest library <name> "<query>"
48
- ```
49
- Step 2 — Fetch documentation:
50
- ```bash
51
- npx --yes ctx7@latest docs <libraryId> "<query>"
52
- ```
53
-
54
- Do not skip documentation lookups because MCP tools are unavailable — the CLI fallback
55
- works via Bash and produces equivalent output.
36
+ @~/.claude/gsd-core/references/research-documentation-lookup.md
56
37
  </documentation_lookup>
57
38
 
58
39
  <philosophy>
59
-
60
- ## Training Data = Hypothesis
61
-
62
- Claude's training is 6-18 months stale. Knowledge may be outdated, incomplete, or wrong.
63
-
64
- **Discipline:**
65
- 1. **Verify before asserting** — check Context7 or official docs before stating capabilities
66
- 2. **Prefer current sources** — Context7 and official docs trump training data
67
- 3. **Flag uncertainty** — LOW confidence when only training data supports a claim
68
-
69
- ## Honest Reporting
70
-
71
- - "I couldn't find X" is valuable (investigate differently)
72
- - "LOW confidence" is valuable (flags for validation)
73
- - "Sources contradict" is valuable (surfaces ambiguity)
74
- - Never pad findings, state unverified claims as fact, or hide uncertainty
75
-
76
- ## Investigation, Not Confirmation
77
-
78
- **Bad research:** Start with hypothesis, find supporting evidence
79
- **Good research:** Gather evidence, form conclusions from evidence
80
-
81
- Don't find articles supporting your initial guess — find what the ecosystem actually uses and let evidence drive recommendations.
82
-
40
+ @~/.claude/gsd-core/references/research-philosophy.md
83
41
  </philosophy>
84
42
 
85
43
  <research_modes>
@@ -94,132 +52,95 @@ Don't find articles supporting your initial guess — find what the ecosystem ac
94
52
 
95
53
  <tool_strategy>
96
54
 
97
- ## Tool Priority Order
55
+ ## Research Plan via Code Seam
98
56
 
99
- ### 1. Context7 (highest priority) — Library Questions
100
- Authoritative, current, version-aware documentation.
57
+ The agent decides **what** to research (the questions). The seam decides **which provider** to use and manages caching.
101
58
 
102
- ```
103
- 1. mcp__context7__resolve-library-id with libraryName: "[library]"
104
- 2. mcp__context7__query-docs with libraryId: [resolved ID], query: "[question]"
105
- ```
106
-
107
- Resolve first (don't guess IDs). Use specific queries. Trust over training data.
108
-
109
- ### 2. Official Docs via WebFetch — Authoritative Sources
110
- For libraries not in Context7, changelogs, release notes, official announcements.
111
-
112
- Use exact URLs (not search result pages). Check publication dates. Prefer /docs/ over marketing.
59
+ ### Step A — Build a research-plan input file
113
60
 
114
- ### 3. WebSearch — Ecosystem Discovery
115
- For finding what exists, community patterns, real-world usage.
61
+ Construct a JSON file at a temp path (e.g. `/tmp/research-plan-input.json`):
116
62
 
117
- **Query templates:**
63
+ ```json
64
+ {
65
+ "ecosystem": "<npm|pypi|crates|...>",
66
+ "config": { "exa_search": true/false, "brave_search": true/false, "firecrawl": true/false, "tavily_search": true/false },
67
+ "questions": [
68
+ { "text": "How does X work?", "kind": "docs", "library": "x", "version": "1.2.3" },
69
+ { "text": "Best practices for Y?", "kind": "web" }
70
+ ]
71
+ }
118
72
  ```
119
- Ecosystem: "[tech] best practices", "[tech] recommended libraries"
120
- Patterns: "how to build [type] with [tech]", "[tech] architecture patterns"
121
- Problems: "[tech] common mistakes", "[tech] gotchas"
122
- ```
123
-
124
- Use multiple query variations. Mark WebSearch-only findings as LOW confidence. Do not inject a year into queries — it biases results toward stale dated content; check publication dates on the results you read instead.
125
73
 
126
- ### Enhanced Web Search (Brave API)
74
+ `config` comes from the init context (availability flags). `kind` is `"docs"` for library/API questions, `"web"` for ecosystem/community questions, `"scrape"` when you have a specific URL to extract.
127
75
 
128
- Check `brave_search` from orchestrator context. If `true`, use Brave Search for higher quality results:
76
+ ### Step B — Obtain the fetch plan
129
77
 
130
78
  ```bash
131
- gsd-tools query websearch "your query" --limit 10
79
+ gsd-tools query research-plan --input /tmp/research-plan-input.json
132
80
  ```
133
81
 
134
- **Options:**
135
- - `--limit N` — Number of results (default: 10)
136
- - `--freshness day|week|month` — Restrict to recent content
82
+ Returns `{ "items": [ { "question": "...", "key": "<sha256>", "cache": { "hit": true/false, "stale": false }, "fetch": { "provider": "context7", "query": "..." } } ] }`.
137
83
 
138
- If `brave_search: false` (or not set), use built-in WebSearch tool instead.
84
+ - `cache.hit && !cache.stale` → reuse the cached digest; no fetch needed.
85
+ - `cache.hit && cache.stale` → fetch anyway to refresh; the old entry is returned as a fallback.
86
+ - no `cache` field → cache miss; must fetch.
139
87
 
140
- Brave Search provides an independent index (not Google/Bing dependent) with less SEO spam and faster responses.
88
+ ### Step C — Execute the indicated fetch
141
89
 
142
- ### Exa Semantic Search (MCP)
90
+ For each item where `fetch` is present, invoke the MCP tool matching `fetch.provider`:
143
91
 
144
- Check `exa_search` from orchestrator context. If `true`, use Exa for research-heavy, semantic queries:
92
+ | provider id | MCP tool / built-in |
93
+ |-------------|---------------------|
94
+ | `context7` | `mcp__context7__resolve-library-id` then `mcp__context7__query-docs` |
95
+ | `ref` | `mcp__ref__*` (use the appropriate ref MCP tool for the query) |
96
+ | `jina` | `mcp__jina__*` (use the appropriate jina MCP tool for the query) |
97
+ | `exa` | `mcp__exa__web_search_exa` with `fetch.query` |
98
+ | `tavily` | `mcp__tavily__search` with `fetch.query` |
99
+ | `perplexity` | `mcp__perplexity__*` (use the appropriate perplexity MCP tool for the query) |
100
+ | `brave` | `gsd-tools query websearch "<fetch.query>"` (Brave-backed) or built-in `WebSearch` |
101
+ | `firecrawl` | `mcp__firecrawl__scrape` with url (scrape kind) or `mcp__firecrawl__search` |
102
+ | `websearch` | built-in `WebSearch` tool |
103
+ | `webfetch` | built-in `WebFetch` tool |
145
104
 
146
- ```
147
- mcp__exa__web_search_exa with query: "your semantic query"
148
- ```
105
+ For any other provider id `X` not listed above: use `mcp__X__*` if available, else fall back to `WebSearch`.
149
106
 
150
- **Best for:** Research questions where keyword search fails — "best approaches to X", finding technical/academic content, discovering niche libraries, ecosystem exploration. Returns semantically relevant results rather than keyword matches.
107
+ **WebSearch tip:** Do not inject a year into queries — it biases results toward stale dated content; check publication dates on the results you read instead.
151
108
 
152
- If `exa_search: false` (or not set), fall back to WebSearch or Brave Search.
109
+ ### Step D — Cache each digest
153
110
 
154
- ### Firecrawl Deep Scraping (MCP)
111
+ After digesting a source, persist it so future runs can reuse it:
155
112
 
156
- Check `firecrawl` from orchestrator context. If `true`, use Firecrawl to extract structured content from discovered URLs:
157
-
158
- ```
159
- mcp__firecrawl__scrape with url: "https://docs.example.com/guide"
160
- mcp__firecrawl__search with query: "your query" (web search + auto-scrape results)
113
+ ```bash
114
+ gsd-tools query research-store put <key> \
115
+ --content "<one-paragraph digest>" \
116
+ --source <curated|web> \
117
+ --provider <provider-id> \
118
+ --confidence <HIGH|MEDIUM|LOW> \
119
+ --kind <docs|web>
161
120
  ```
162
121
 
163
- **Best for:** Extracting full page content from documentation, blog posts, GitHub READMEs, comparison articles. Use after finding a relevant URL from Exa, WebSearch, or known docs. Returns clean markdown instead of raw HTML.
122
+ `key` comes from the `research-plan` item. `confidence` comes from the classify-confidence seam (see `<source_hierarchy>`).
164
123
 
165
- If `firecrawl: false` (or not set), fall back to WebFetch.
124
+ </tool_strategy>
166
125
 
167
- ## Verification Protocol
126
+ <source_hierarchy>
168
127
 
169
- **WebSearch findings must be verified:**
128
+ Obtain the confidence tier from code — do not hard-code tiers in your reasoning:
170
129
 
130
+ ```bash
131
+ gsd-tools query classify-confidence --provider <provider-id>
132
+ # for cross-checked findings, add --verified:
133
+ gsd-tools query classify-confidence --provider <provider-id> --verified
171
134
  ```
172
- For each finding:
173
- 1. Verify with Context7? YES → HIGH confidence
174
- 2. Verify with official docs? YES → MEDIUM confidence
175
- 3. Multiple sources agree? YES → Increase one level
176
- Otherwise → LOW confidence, flag for validation
177
- ```
178
-
179
- Never present LOW confidence findings as authoritative.
180
135
 
181
- ## Confidence Levels
136
+ Returns `HIGH`, `MEDIUM`, or `LOW`. Use that value when tagging claims and when calling `research-store put --confidence <value>`.
182
137
 
183
- | Level | Sources | Use |
184
- |-------|---------|-----|
185
- | HIGH | Context7, official documentation, official releases | State as fact |
186
- | MEDIUM | WebSearch verified with official source, multiple credible sources agree | State with attribution |
187
- | LOW | WebSearch only, single source, unverified | Flag as needing validation |
138
+ **Never present LOW confidence findings as authoritative.**
188
139
 
189
- **Source priority:** Context7 → Exa (verified) → Firecrawl (official docs) → Official GitHub → Brave/WebSearch (verified) → WebSearch (unverified)
190
-
191
- </tool_strategy>
140
+ </source_hierarchy>
192
141
 
193
142
  <verification_protocol>
194
-
195
- ## Research Pitfalls
196
-
197
- ### Configuration Scope Blindness
198
- **Trap:** Assuming global config means no project-scoping exists
199
- **Prevention:** Verify ALL scopes (global, project, local, workspace)
200
-
201
- ### Deprecated Features
202
- **Trap:** Old docs → concluding feature doesn't exist
203
- **Prevention:** Check current docs, changelog, version numbers
204
-
205
- ### Negative Claims Without Evidence
206
- **Trap:** Definitive "X is not possible" without official verification
207
- **Prevention:** Is this in official docs? Checked recent updates? "Didn't find" ≠ "doesn't exist"
208
-
209
- ### Single Source Reliance
210
- **Trap:** One source for critical claims
211
- **Prevention:** Require official docs + release notes + additional source
212
-
213
- ## Pre-Submission Checklist
214
-
215
- - [ ] All domains investigated (stack, features, architecture, pitfalls)
216
- - [ ] Negative claims verified with official docs
217
- - [ ] Multiple sources for critical claims
218
- - [ ] URLs provided for authoritative sources
219
- - [ ] Publication dates checked (prefer recent/current)
220
- - [ ] Confidence levels assigned honestly
221
- - [ ] "What might I have missed?" review completed
222
-
143
+ @~/.claude/gsd-core/references/research-verification-protocol.md
223
144
  </verification_protocol>
224
145
 
225
146
  <output_formats>
@@ -564,7 +485,7 @@ Orchestrator provides: project name/description, research mode, project context,
564
485
 
565
486
  ## Step 3: Execute Research
566
487
 
567
- For each domain: Context7 → Official Docs → WebSearch → Verify. Document with confidence levels.
488
+ For each domain, use the `<tool_strategy>` seam (Steps A–D): build questions JSON, call `gsd-tools query research-plan`, run the indicated provider per item, then cache each digest. Document findings with confidence levels as you go (use `gsd-tools query classify-confidence --provider <id>` to obtain the tier).
568
489
 
569
490
  ## Step 4: Quality Check
570
491
 
@@ -678,7 +599,7 @@ Research is complete when:
678
599
  - [ ] Feature landscape mapped (table stakes, differentiators, anti-features)
679
600
  - [ ] Architecture patterns documented
680
601
  - [ ] Domain pitfalls catalogued
681
- - [ ] Source hierarchy followed (Context7 → Official → WebSearch)
602
+ - [ ] Source hierarchy followed (research-plan seam determines provider order; classify-confidence seam determines tiers)
682
603
  - [ ] All findings have confidence levels
683
604
  - [ ] Output files created in `.planning/research/`
684
605
  - [ ] SUMMARY.md includes roadmap implications
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: gsd-ui-researcher
3
3
  description: Produces UI-SPEC.md design contract for frontend phases. Reads upstream artifacts, detects design system state, asks only unanswered questions. Spawned by /gsd:ui-phase orchestrator.
4
- tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*
4
+ tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*
5
5
  color: "#E879F9"
6
6
  # hooks:
7
7
  # PostToolUse:
@@ -28,26 +28,7 @@ If the prompt contains a `<required_reading>` block, you MUST use the `Read` too
28
28
  </role>
29
29
 
30
30
  <documentation_lookup>
31
- When you need library or framework documentation, check in this order:
32
-
33
- 1. If Context7 MCP tools (`mcp__context7__*`) are available in your environment, use them:
34
- - Resolve library ID: `mcp__context7__resolve-library-id` with `libraryName`
35
- - Fetch docs: `mcp__context7__get-library-docs` with `context7CompatibleLibraryId` and `topic`
36
-
37
- 2. If Context7 MCP is not available (upstream bug anthropics/claude-code#13898 strips MCP
38
- tools from agents with a `tools:` frontmatter restriction), use the CLI fallback via Bash:
39
-
40
- Step 1 — Resolve library ID:
41
- ```bash
42
- npx --yes ctx7@latest library <name> "<query>"
43
- ```
44
- Step 2 — Fetch documentation:
45
- ```bash
46
- npx --yes ctx7@latest docs <libraryId> "<query>"
47
- ```
48
-
49
- Do not skip documentation lookups because MCP tools are unavailable — the CLI fallback
50
- works via Bash and produces equivalent output.
31
+ @~/.claude/gsd-core/references/research-documentation-lookup.md
51
32
  </documentation_lookup>
52
33
 
53
34
  <project_context>
@@ -466,8 +466,11 @@ ls $BUILD_OUTPUT_DIR/*.{js,css} 2>/dev/null | wc -l
466
466
  # Module exports expected functions
467
467
  node -e "const m = require('$MODULE_PATH'); console.log(typeof m.$FUNCTION_NAME)" 2>/dev/null | grep -q "function"
468
468
 
469
- # Test suite passes (if tests exist for this phase's code)
470
- npm test -- --grep "$PHASE_TEST_PATTERN" 2>&1 | grep -q "passing"
469
+ # A test EXISTS (existence proof — enumerate, do NOT run the suite)
470
+ cargo test -- --list 2>/dev/null | grep -q "$PHASE_TEST_PATTERN" # pytest --collect-only -q · npx vitest list · go test -list '.*'
471
+
472
+ # A specific test PASSES (run ONE named test, never the whole suite)
473
+ cargo test "$TEST_NAME" -- --exact # pytest -k "$TEST_NAME" · npx vitest run -t "$TEST_NAME"
471
474
  ```
472
475
 
473
476
  2. **Run each check** and record pass/fail:
@@ -487,6 +490,7 @@ npm test -- --grep "$PHASE_TEST_PATTERN" 2>&1 | grep -q "passing"
487
490
  - Each check must complete in under 10 seconds
488
491
  - Do not start servers or services — only test what's already runnable
489
492
  - Do not modify state (no writes, no mutations, no side effects)
493
+ - **Run the full workspace test command at most once per verification.** Never filter a full run per must-have (`<full-suite> 2>&1 | grep X` repeated per truth) — it re-runs everything and yields no new evidence. Prove a test exists by enumeration (`--list` / `--collect-only`); prove one passes via a single named test. If a full run is genuinely required, run it once and `grep` the saved output.
490
494
  - If the project has no runnable entry points yet, skip with: "Step 7b: SKIPPED (no runnable entry points)"
491
495
 
492
496
  ## Step 7c: Probe Execution
@@ -572,6 +576,8 @@ Classify status using this decision tree IN ORDER (most restrictive first):
572
576
 
573
577
  **passed is ONLY valid when the human verification section is empty.** If you identified items requiring human testing in Step 8, status MUST be human_needed.
574
578
 
579
+ > **Shared status seam**: the status vocabulary (`passed`, `gaps_found`, `human_needed`) and the per-status routing (next action and next command for each value) are owned by `src/verification.cts` via `gsd_run query verification.status`. This agent is the single emitter of the frontmatter status field; consumers (ship.md, execute-phase.md) read routing from that query instead of re-deriving it.
580
+
575
581
  **Score:** `verified_truths / total_truths`
576
582
 
577
583
  ## Step 9b: Filter Deferred Items
package/bin/install.js CHANGED
@@ -31,6 +31,10 @@ const {
31
31
  const {
32
32
  resolveAntigravityGlobalDir,
33
33
  } = require('../gsd-core/bin/lib/runtime-homes.cjs');
34
+ const {
35
+ applyWorktreeBaseRef,
36
+ readBaseRefFromSettings,
37
+ } = require('../gsd-core/bin/lib/worktree-base-ref.cjs');
34
38
 
35
39
  /**
36
40
  * Runtimes that register hyphen-form `name:` per #2808 AND copy agent bodies
@@ -1997,6 +2001,8 @@ function convertCopilotToolName(claudeTool) {
1997
2001
  if (claudeToCopilotTools[claudeTool]) {
1998
2002
  return claudeToCopilotTools[claudeTool];
1999
2003
  }
2004
+ // mcp__{tavily,ref,jina,exa,firecrawl}__* use the generic MCP passthrough like exa/firecrawl;
2005
+ // add explicit Copilot registry mappings when the io.github ids are confirmed (#657 follow-up)
2000
2006
  // Default: lowercase
2001
2007
  return claudeTool.toLowerCase();
2002
2008
  }
@@ -2795,14 +2801,22 @@ function convertClaudeAgentToClineAgent(content) {
2795
2801
  // ── End Cline converters ─────────────────────────────────────────────────────
2796
2802
 
2797
2803
  function convertSlashCommandsToCodexSkillMentions(content) {
2798
- // Convert colon-style skill invocations to Codex $ prefix
2804
+ // Colon-style /gsd: never appears as a filesystem path segment, so no boundary guard is needed (unlike the hyphen-style below).
2799
2805
  let converted = content.replace(/\/gsd:([a-z0-9-]+)/gi, (_, commandName) => {
2800
2806
  return `$gsd-${String(commandName).toLowerCase()}`;
2801
2807
  });
2802
2808
  // Convert hyphen-style command references (workflow output) to Codex $ prefix.
2803
- // Negative lookbehind excludes file paths like bin/gsd-tools.cjs where
2804
- // the slash is preceded by a word char, dot, or another slash.
2805
- converted = converted.replace(/(?<![a-zA-Z0-9./])\/gsd-([a-z0-9-]+)/gi, (_, commandName) => {
2809
+ // A real /gsd-<cmd> MENTION is defined positively by two boundaries, so any
2810
+ // in-path occurrence is excluded by construction (no denylist of preceding
2811
+ // chars to maintain — see #712, supersedes the #637/#704 lookbehind treadmill):
2812
+ // 1. Left boundary: opens at start-of-string, whitespace, or an inline-prose
2813
+ // delimiter (backtick/quote/paren/bracket) — e.g. `/gsd-execute-phase`.
2814
+ // 2. Right boundary: the command token is NOT followed by a path separator
2815
+ // `/` (a path continues: `/gsd-core/bin/...`; a command does not). The
2816
+ // `(?![a-z0-9/-])` also blocks regex backtracking to a shorter command.
2817
+ // This converts backtick-wrapped MENTIONS (`/gsd-foo`) while leaving backtick-
2818
+ // wrapped PATHS (`/gsd-core/workflows/update.md`) untouched (#712).
2819
+ converted = converted.replace(/(?<=^|[\s`"'([])\/gsd-([a-z0-9-]+)(?![a-z0-9/-])/gi, (_, commandName) => {
2806
2820
  return `$gsd-${String(commandName).toLowerCase()}`;
2807
2821
  });
2808
2822
  return converted;
@@ -8575,6 +8589,10 @@ function install(isGlobal, runtime = 'claude', options = {}) {
8575
8589
  // agentsSrc is declared here (let, not const) because installCodexConfig() inside the
8576
8590
  // Codex config block below also references it, and that block is outside the try scope.
8577
8591
  let agentsSrc = path.join(src, 'agents');
8592
+ // Capture upgrade signal BEFORE files are written (#683). Must be declared at function
8593
+ // scope (outside the try block below) so it is accessible in the settings section later.
8594
+ // Absent VERSION = fresh install; present VERSION = upgrade/re-install.
8595
+ const priorInstallExisted = fs.existsSync(path.join(targetDir, 'gsd-core', 'VERSION'));
8578
8596
  try {
8579
8597
  installerMigrationResult = runInstallerMigrations({
8580
8598
  configDir: targetDir,
@@ -10106,6 +10124,68 @@ function install(isGlobal, runtime = 'claude', options = {}) {
10106
10124
  ? buildHookCommand(targetDir, 'gsd-update-banner.js', hookOpts)
10107
10125
  : localCmd('gsd-update-banner.js'));
10108
10126
 
10127
+ // #683: Set worktree.baseRef:"head" in settings.local.json for local Claude installs.
10128
+ // Both fresh and upgrade paths apply only when worktrees are enabled for the project.
10129
+ // Never applies to global installs, non-Claude runtimes, or when the user already
10130
+ // has an explicit baseRef in EITHER settings.local.json OR settings.json (no-clobber).
10131
+ // Guard: skip entirely when settings is not a plain object (e.g. parsed to [] or primitive)
10132
+ // to avoid crashing applyWorktreeBaseRef on unexpected top-level shapes.
10133
+ if (isLocalClaude && settings !== null && typeof settings === 'object' && !Array.isArray(settings)) {
10134
+ // Read shared settings.json baseRef so no-clobber spans both files (#683 FIX 1).
10135
+ // shared settings.json no-clobber is checked here; settings.local.json no-clobber
10136
+ // is enforced inside applyWorktreeBaseRef itself.
10137
+ const sharedSettingsForBaseRef = readSettings(path.join(targetDir, 'settings.json')) || {};
10138
+ const sharedBaseRef = readBaseRefFromSettings(sharedSettingsForBaseRef);
10139
+
10140
+ // Compute worktrees-enabled ONCE for both fresh and upgrade paths (FIX A: DRY + consistency).
10141
+ // Read workflow.use_worktrees from .planning/config.json by walking up from
10142
+ // targetDir (same walk-up pattern as readGsdRuntimeProfileResolver). Defaults
10143
+ // to enabled (true) when the file is missing, unreadable, or the key is absent;
10144
+ // only boolean false disables (string "false" stays enabled).
10145
+ let worktreesEnabled = true; // default: enabled
10146
+ try {
10147
+ let probeDir = path.resolve(targetDir);
10148
+ for (let depth = 0; depth < 8; depth += 1) {
10149
+ const candidate = path.join(probeDir, '.planning', 'config.json');
10150
+ if (fs.existsSync(candidate)) {
10151
+ try {
10152
+ const parsed = JSON.parse(stripJsonComments(fs.readFileSync(candidate, 'utf-8')));
10153
+ if (parsed && typeof parsed === 'object' &&
10154
+ parsed.workflow && parsed.workflow.use_worktrees === false) {
10155
+ worktreesEnabled = false;
10156
+ }
10157
+ } catch {
10158
+ // Malformed config.json — treat as enabled (safe fallback).
10159
+ }
10160
+ break;
10161
+ }
10162
+ const parent = path.dirname(probeDir);
10163
+ if (parent === probeDir) break;
10164
+ probeDir = parent;
10165
+ }
10166
+ } catch {
10167
+ // Any unexpected error reading .planning — default to enabled.
10168
+ }
10169
+
10170
+ if (worktreesEnabled && sharedBaseRef === null) {
10171
+ if (!priorInstallExisted) {
10172
+ // Fresh install — apply no-clobber baseRef set.
10173
+ // canonical no-clobber logic: src/worktree-base-ref.cts applyWorktreeBaseRef (#683)
10174
+ const { changed } = applyWorktreeBaseRef(settings);
10175
+ if (changed) {
10176
+ console.log(` ${green}✓${reset} Set worktree.baseRef:"head" for Claude Code worktrees (forks phase worktrees off HEAD; #683)`);
10177
+ }
10178
+ } else {
10179
+ // Upgrade — auto-apply no-clobber baseRef set when worktrees are enabled.
10180
+ const { changed } = applyWorktreeBaseRef(settings);
10181
+ if (changed) {
10182
+ console.log(` ${green}✓${reset} Enabled worktree.baseRef:"head" for Claude Code worktrees (forks phase worktrees off HEAD; #683)`);
10183
+ }
10184
+ }
10185
+ }
10186
+ // When worktreesEnabled is false: do nothing, print nothing (both fresh and upgrade).
10187
+ }
10188
+
10109
10189
  persistActiveProfileMarker();
10110
10190
  return {
10111
10191
  settingsPath,
@@ -10943,6 +11023,7 @@ module.exports = {
10943
11023
  install,
10944
11024
  installAllRuntimes,
10945
11025
  uninstall,
11026
+ convertSlashCommandsToCodexSkillMentions,
10946
11027
  convertClaudeCommandToCodexSkill,
10947
11028
  convertClaudeToOpencodeFrontmatter,
10948
11029
  convertClaudeToKiloFrontmatter,
@@ -79,7 +79,8 @@ Modes:
79
79
  Run:
80
80
 
81
81
  ```bash
82
- node $HOME/.claude/gsd-core/bin/gsd-tools.cjs graphify query <term>
82
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi
83
+ gsd_run graphify query <term>
83
84
  ```
84
85
 
85
86
  Parse the JSON output and display results:
@@ -95,7 +96,8 @@ Parse the JSON output and display results:
95
96
  Run:
96
97
 
97
98
  ```bash
98
- node $HOME/.claude/gsd-core/bin/gsd-tools.cjs graphify status
99
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi
100
+ gsd_run graphify status
99
101
  ```
100
102
 
101
103
  Parse the JSON output and display:
@@ -119,7 +121,8 @@ Surface both so the agent can choose.
119
121
  Run:
120
122
 
121
123
  ```bash
122
- node $HOME/.claude/gsd-core/bin/gsd-tools.cjs graphify diff
124
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi
125
+ gsd_run graphify diff
123
126
  ```
124
127
 
125
128
  Parse the JSON output and display:
@@ -137,7 +140,8 @@ If no snapshot exists, suggest running `build` twice (first to create, second to
137
140
  Run the pre-flight check first:
138
141
 
139
142
  ```bash
140
- node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" graphify build
143
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi
144
+ gsd_run graphify build
141
145
  ```
142
146
 
143
147
  Parse the JSON output:
@@ -156,12 +160,13 @@ GSD > Building knowledge graph...
156
160
  Run the build, copy artifacts, write the diff snapshot, and report the summary in a single foreground Bash call so the whole pipeline survives to completion. Use a `timeout` of `600000` ms (10 minutes), which covers the `graphify.build_timeout` ceiling (default 300 s) with margin:
157
161
 
158
162
  ```bash
163
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi
159
164
  graphify update . \
160
165
  && cp graphify-out/graph.json .planning/graphs/graph.json \
161
166
  && { [ -f graphify-out/graph.html ] && cp graphify-out/graph.html .planning/graphs/graph.html || true; } \
162
167
  && cp graphify-out/GRAPH_REPORT.md .planning/graphs/GRAPH_REPORT.md \
163
- && node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" graphify build snapshot \
164
- && node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" graphify status
168
+ && gsd_run graphify build snapshot \
169
+ && gsd_run graphify status
165
170
  ```
166
171
 
167
172
  Do NOT pass `run_in_background: true`. Typical builds complete in 15-60 seconds and the entire chain must run foreground.
@@ -33,8 +33,12 @@ $ARGUMENTS
33
33
 
34
34
  <process>
35
35
  If `--from-gsd2` is in $ARGUMENTS:
36
- Run: `node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" from-gsd2`
37
- Pass `--path <dir>` if provided. Present the migration result to the user.
36
+ Run the reverse-migration (append `--path <dir>` if provided):
37
+ ```bash
38
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi
39
+ gsd_run from-gsd2
40
+ ```
41
+ Present the migration result to the user.
38
42
  Stop here (do not run the standard import workflow).
39
43
 
40
44
  Otherwise, execute the import workflow end-to-end.
@@ -22,8 +22,8 @@ Create executable phase prompts (PLAN.md files) for a roadmap phase with integra
22
22
  **Research-only mode (`--research-phase <N>`):** Spawn `gsd-phase-researcher` for phase `N`, write `RESEARCH.md`, then exit before the planner runs. Useful for cross-phase research, doc review before committing to a planning approach, and correction-without-replanning loops where iterating on research alone is dramatically cheaper than re-spawning the planner. Replaces the deleted research-phase command (#3042).
23
23
 
24
24
  **Research-only modifiers:**
25
- - **No flag** — when `RESEARCH.md` already exists, prompt the user to choose `update / view / skip`.
26
- - **`--research`** — force-refresh: re-spawn the researcher unconditionally, no prompt. Skips the existing-RESEARCH.md menu.
25
+ - **No flag** — when `RESEARCH.md` already exists, auto-uses it: emits a one-line notice and exits cleanly, no prompt.
26
+ - **`--research`** — force-refresh: re-spawn the researcher unconditionally, no prompt. Bypasses the existing-RESEARCH.md auto-use path.
27
27
  - **`--view`** — view-only: print existing `RESEARCH.md` to stdout. Does not spawn the researcher. Cheapest mode for the correction-without-replanning loop. If no `RESEARCH.md` exists yet, errors with a hint to drop `--view`.
28
28
 
29
29
  **Orchestrator role:** Parse arguments, validate phase, research domain (unless skipped), spawn gsd-planner, verify with gsd-plan-checker, iterate until pass or max iterations, present results.
@@ -21,6 +21,7 @@
21
21
  */
22
22
 
23
23
  const { execNpm } = require('./lib/shell-command-projection.cjs');
24
+ const { runMain } = require('./lib/cli-exit.cjs');
24
25
 
25
26
  // Sourced from the single Package Identity seam (#498), not re-typed. The seam
26
27
  // bakes the value from package.json at build time, so it is a code constant —
@@ -98,9 +99,9 @@ function main() {
98
99
  } else {
99
100
  process.stderr.write(`check-latest-version: ${r.reason}: ${r.detail}\n`);
100
101
  }
101
- process.exit(r.ok ? 0 : 1);
102
+ return r.ok ? 0 : 1;
102
103
  }
103
104
 
104
- if (require.main === module) main();
105
+ if (require.main === module) runMain(main);
105
106
 
106
107
  module.exports = { checkLatestVersion, CHECK_REASON, PACKAGE_NAME };