gsdd-cli 0.27.0 → 0.29.0

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 (84) hide show
  1. package/README.md +53 -22
  2. package/agents/DISTILLATION.md +2 -2
  3. package/agents/README.md +4 -4
  4. package/agents/approach-explorer.md +4 -4
  5. package/agents/executor.md +20 -20
  6. package/agents/integration-checker.md +2 -2
  7. package/agents/planner.md +10 -26
  8. package/agents/researcher.md +2 -2
  9. package/agents/roadmapper.md +6 -6
  10. package/agents/synthesizer.md +18 -18
  11. package/agents/verifier.md +3 -3
  12. package/bin/adapters/agents.mjs +3 -3
  13. package/bin/adapters/claude.mjs +16 -14
  14. package/bin/adapters/opencode.mjs +15 -12
  15. package/bin/gsdd.mjs +16 -13
  16. package/bin/lib/{models.mjs → config.mjs} +23 -16
  17. package/bin/lib/control-map.mjs +17 -488
  18. package/bin/lib/health-truth.mjs +8 -13
  19. package/bin/lib/health.mjs +25 -39
  20. package/bin/lib/init-flow.mjs +44 -38
  21. package/bin/lib/init-prompts.mjs +3 -3
  22. package/bin/lib/init-runtime.mjs +11 -30
  23. package/bin/lib/lifecycle-preflight.mjs +97 -410
  24. package/bin/lib/lifecycle-state.mjs +2 -1
  25. package/bin/lib/next.mjs +243 -20
  26. package/bin/lib/phase.mjs +706 -280
  27. package/bin/lib/plan-constants.mjs +0 -5
  28. package/bin/lib/rendering.mjs +64 -44
  29. package/bin/lib/runtime-freshness.mjs +18 -15
  30. package/bin/lib/state-dir.mjs +45 -0
  31. package/bin/lib/templates.mjs +59 -22
  32. package/bin/lib/work-context.mjs +12 -1
  33. package/bin/lib/workflows.mjs +0 -1
  34. package/bin/lib/workspace-root.mjs +11 -6
  35. package/distilled/DESIGN.md +89 -31
  36. package/distilled/EVIDENCE-INDEX.md +18 -5
  37. package/distilled/README.md +23 -33
  38. package/distilled/SKILL.md +9 -10
  39. package/distilled/templates/agents.block.md +5 -5
  40. package/distilled/templates/approach.md +3 -3
  41. package/distilled/templates/auth-matrix.md +2 -2
  42. package/distilled/templates/brownfield-change/CHANGE.md +1 -1
  43. package/distilled/templates/delegates/approach-explorer.md +5 -5
  44. package/distilled/templates/delegates/mapper-arch.md +3 -3
  45. package/distilled/templates/delegates/mapper-concerns.md +4 -4
  46. package/distilled/templates/delegates/mapper-quality.md +3 -3
  47. package/distilled/templates/delegates/mapper-tech.md +3 -3
  48. package/distilled/templates/delegates/plan-checker.md +18 -19
  49. package/distilled/templates/delegates/researcher-architecture.md +3 -3
  50. package/distilled/templates/delegates/researcher-features.md +3 -3
  51. package/distilled/templates/delegates/researcher-pitfalls.md +3 -3
  52. package/distilled/templates/delegates/researcher-stack.md +3 -3
  53. package/distilled/templates/delegates/researcher-synthesizer.md +7 -7
  54. package/distilled/templates/research/architecture.md +1 -1
  55. package/distilled/templates/research/pitfalls.md +1 -1
  56. package/distilled/templates/research/stack.md +1 -1
  57. package/distilled/templates/roadmap.md +4 -4
  58. package/distilled/templates/spec.md +2 -2
  59. package/distilled/templates/ui-proof.md +81 -181
  60. package/distilled/workflows/audit-milestone.md +23 -23
  61. package/distilled/workflows/complete-milestone.md +35 -35
  62. package/distilled/workflows/execute.md +34 -35
  63. package/distilled/workflows/map-codebase.md +30 -30
  64. package/distilled/workflows/new-milestone.md +18 -18
  65. package/distilled/workflows/new-project.md +45 -45
  66. package/distilled/workflows/pause.md +15 -15
  67. package/distilled/workflows/plan.md +106 -114
  68. package/distilled/workflows/progress.md +40 -39
  69. package/distilled/workflows/quick.md +49 -50
  70. package/distilled/workflows/resume.md +23 -22
  71. package/distilled/workflows/verify-work.md +7 -7
  72. package/distilled/workflows/verify.md +26 -26
  73. package/docs/BROWNFIELD-PROOF.md +1 -1
  74. package/docs/RUNTIME-SUPPORT.md +13 -13
  75. package/docs/USER-GUIDE.md +26 -21
  76. package/docs/claude/context-monitor.md +1 -1
  77. package/docs/proof/consumer-node-cli/README.md +1 -1
  78. package/package.json +3 -3
  79. package/bin/lib/closeout-report.mjs +0 -318
  80. package/bin/lib/evidence-contract.mjs +0 -325
  81. package/bin/lib/provenance.mjs +0 -390
  82. package/bin/lib/session-fingerprint.mjs +0 -223
  83. package/bin/lib/ui-proof.mjs +0 -1007
  84. package/distilled/workflows/plan-milestone-gaps.md +0 -204
@@ -123,7 +123,7 @@ Do not return a flat symptom list when the same underlying breakage explains mul
123
123
 
124
124
  Visual correctness, live interaction quality, and some external integrations still need explicit human checks.
125
125
 
126
- For UI proof slots, fail closed unless observed proof is matched to the exact claim, route/state, observation, evidence kind, artifact path or manual step, privacy metadata, result, and claim limit. For live UI runtime proof, expect `agent-browser` as the default captured tool unless the observed bundle explains a project-native equivalent or an availability constraint; do not fail solely because another browser tool was used, but downgrade vague proof that lacks exact route/state, viewport coverage or rationale, interactive steps/refs where relevant, screenshot/report artifacts, or relevant console/network observations. Existing Playwright/package-script browser tests count as canonical repeatable regression evidence, not as a replacement for scoped runtime proof when the slot requires `runtime`. Artifact metadata must include `visibility`, `retention`, `sensitivity`, and `safe_to_publish`; local-only or unsafe artifacts cannot back public, tracked, delivery, release, or publication proof claims, and `gsdd ui-proof validate`/`gsdd health` metadata failures block the stronger proof claim. Screenshots, traces, reports, Gherkin, a11y scans, E2E outputs, manual notes, source annotations, AST/cAST findings, semantic search, comments, and Semble-like retrieval do not satisfy proof by existence alone. Human acceptance records risk, waiver, deferment, proof debt, or a narrowed claim; it does not upgrade missing or mismatched non-human proof to `satisfied`.
126
+ For browser proof, fail closed unless observed proof is matched to the exact route/state, viewport, observation, evidence kind, artifact path or manual step, privacy/safety note, result, and claim limit. For live UI runtime proof, expect `agent-browser` as the default captured tool unless the observation record explains a project-native equivalent or an availability constraint; do not fail solely because another browser tool was used, but downgrade vague proof that lacks exact route/state, viewport coverage or rationale, interactive steps/refs where relevant, screenshot/report artifacts, relevant console/network observations, or a narrowed claim limit. Existing Playwright/package-script browser tests count as canonical repeatable regression evidence, not as a replacement for scoped runtime proof when browser proof requires runtime observation. Local-only or unsafe artifacts cannot back public, tracked, delivery, release, or publication proof claims unless sanitized. Screenshots, traces, reports, Gherkin, a11y scans, E2E outputs, manual notes, source annotations, AST/cAST findings, semantic search, comments, and Semble-like retrieval do not satisfy proof by existence alone. Human acceptance records risk, waiver, deferment, proof debt, or a narrowed claim; it does not upgrade missing or mismatched non-human proof to satisfied proof. Use the failure-cause names in `distilled/references/proof-rules.md` when proof fails or is partial.
127
127
 
128
128
  ## Step 9: Determine overall status
129
129
 
@@ -188,7 +188,7 @@ Orphaned requirements must be reported even if the overall phase otherwise looks
188
188
  </requirements_coverage>
189
189
 
190
190
  <output>
191
- Write `.planning/phases/{phase_dir}/{phase_num}-VERIFICATION.md`.
191
+ Write `.work/phases/{phase_dir}/{phase_num}-VERIFICATION.md`.
192
192
 
193
193
  Keep the current GSDD report schema:
194
194
  - base frontmatter: `phase`, `verified`, `status`, `score`
@@ -275,7 +275,7 @@ Return summary example:
275
275
  ```yaml
276
276
  status: "gaps_found"
277
277
  score: "2/3 must-haves verified"
278
- report: ".planning/phases/01-foundation/01-VERIFICATION.md"
278
+ report: ".work/phases/01-foundation/01-VERIFICATION.md"
279
279
  gaps:
280
280
  - truth: "Users can create a user from the page"
281
281
  reason: "POST handler returns placeholder data"
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
 
4
- function createRootAgentsAdapter({ cwd, renderAgentsBoundedBlock, renderAgentsFileContent, upsertBoundedBlock }, name = 'agents') {
4
+ function createRootAgentsAdapter({ cwd, stateDirName = '.work', renderAgentsBoundedBlock, renderAgentsFileContent, upsertBoundedBlock }, name = 'agents') {
5
5
  return {
6
6
  id: 'agents',
7
7
  name,
@@ -15,10 +15,10 @@ function createRootAgentsAdapter({ cwd, renderAgentsBoundedBlock, renderAgentsFi
15
15
  },
16
16
  generate() {
17
17
  const agentsPath = join(cwd, 'AGENTS.md');
18
- const block = renderAgentsBoundedBlock();
18
+ const block = renderAgentsBoundedBlock({ stateDirName });
19
19
 
20
20
  if (!existsSync(agentsPath)) {
21
- writeFileSync(agentsPath, renderAgentsFileContent());
21
+ writeFileSync(agentsPath, renderAgentsFileContent({ stateDirName }));
22
22
  return;
23
23
  }
24
24
 
@@ -5,6 +5,7 @@ import {
5
5
  MAX_CHECKER_CYCLES,
6
6
  CHECKER_STATUSES,
7
7
  } from '../lib/plan-constants.mjs';
8
+ import { localizeStateDirReferences } from '../lib/rendering.mjs';
8
9
 
9
10
  const CLAUDE_MODEL_PROFILES = {
10
11
  quality: 'opus',
@@ -36,7 +37,7 @@ ${delegateContent.trim()}
36
37
  `;
37
38
  }
38
39
 
39
- function renderClaudePlanSkill({ portableContractPath = '.agents/skills/gsdd-plan/SKILL.md' } = {}) {
40
+ function renderClaudePlanSkill({ portableContractPath = '.agents/skills/gsdd-plan/SKILL.md', stateDirName = '.work' } = {}) {
40
41
  const contractSection = portableContractPath
41
42
  ? `Portable contract:
42
43
  - Read \`${portableContractPath}\` first. That file remains the canonical vendor-agnostic plan contract.
@@ -44,13 +45,13 @@ function renderClaudePlanSkill({ portableContractPath = '.agents/skills/gsdd-pla
44
45
  - If the portable skill says plan is still a stub, treat that as a portability-status warning for the generic surface, not as a stop signal for this Claude-native adapter path.`
45
46
  : `Workflow contract:
46
47
  - This globally installed skill is the canonical Claude-native \`gsdd-plan\` workflow contract.
47
- - Keep the workflow portable: use repo-local \`.planning/\` artifacts for project state and do not require repo-local \`.agents/skills/\` files to exist.
48
+ - Keep the workflow portable: use repo-local \`.work/\` artifacts for project state and do not require repo-local \`.agents/skills/\` files to exist.
48
49
  - Do not claim that other runtimes have the same behavior unless their own adapters explicitly implement and prove it.`;
49
50
  const planningContract = portableContractPath
50
51
  ? `\`${portableContractPath}\``
51
52
  : 'this skill';
52
53
 
53
- return `---
54
+ const content = `---
54
55
  name: gsdd-plan
55
56
  description: Claude-native Phase planning with fresh-context plan checking for GSDD
56
57
  argument-hint: [phase-number]
@@ -67,25 +68,25 @@ Native Claude adapter rule:
67
68
  - Do NOT claim that other runtimes have the same behavior unless their own adapters explicitly implement and prove it.
68
69
 
69
70
  Execution flow:
70
- 1. Read \`.planning/SPEC.md\`, \`.planning/ROADMAP.md\`, \`.planning/config.json\`, relevant phase research, and any existing phase plan files.
71
+ 1. Read \`.work/SPEC.md\`, \`.work/ROADMAP.md\`, \`.work/config.json\`, relevant phase research, and any existing phase plan files.
71
72
  2. Resolve the target phase from the command arguments. If no phase is provided, choose the first roadmap phase that is not complete.
72
73
  3. **Approach exploration** (before planning):
73
- a. Check \`.planning/config.json\` for \`workflow.discuss\`. If \`false\` or missing, skip to step 4 and report \`reduced_alignment\` in the summary.
74
+ a. Check \`.work/config.json\` for \`workflow.discuss\`. If \`false\` or missing, skip to step 4 and report \`reduced_alignment\` in the summary.
74
75
  b. Check if \`{phase_dir}/{padded_phase}-APPROACH.md\` exists. If it does, offer the user: "Use existing" / "Update it" / "View it". If "Use existing", load decisions, then validate the alignment proof before step 4; proofless or invalid existing APPROACH.md must be updated, not silently trusted.
75
- c. If no APPROACH.md exists (or user chose "Update"): invoke the native \`gsdd-approach-explorer\` subagent with the phase goal, requirement IDs, project config from \`.planning/config.json\` (especially \`workflow.discuss\`), SPEC locked decisions, phase research, and relevant codebase files.
76
+ c. If no APPROACH.md exists (or user chose "Update"): invoke the native \`gsdd-approach-explorer\` subagent with the phase goal, requirement IDs, project config from \`.work/config.json\` (especially \`workflow.discuss\`), SPEC locked decisions, phase research, and relevant codebase files.
76
77
  d. The explorer runs a GSD-style interactive conversation with the user (gray areas, research, deep-dive questions, assumptions) and writes APPROACH.md.
77
78
  e. Before planning, confirm APPROACH.md records all canonical proof fields: \`alignment_status\`, \`alignment_method\`, \`user_confirmed_at\`, \`explicit_skip_approved\`, \`skip_scope\`, \`skip_rationale\`, and \`confirmed_decisions\`. For \`alignment_status: user_confirmed\`, \`confirmed_decisions\` must name the locked decisions and skip fields may be \`false\`/\`N/A\`; for \`alignment_status: approved_skip\`, \`explicit_skip_approved: true\`, \`skip_scope\`, and \`skip_rationale\` must be substantive. Agent-only "No questions needed" is not valid proof under \`workflow.discuss: true\`.
78
79
  f. Load APPROACH.md decisions as locked constraints alongside SPEC.md decisions.
79
80
  4. Produce the initial phase plan according to ${planningContract}. Pass APPROACH.md decisions (if any) as locked constraints to the planner.
80
- 5. If \`.planning/config.json\` has \`workflow.planCheck: false\`, stop after planner self-check and explicitly report reduced assurance. This only skips the independent checker; it does not skip the step 3 alignment-proof gate when \`workflow.discuss: true\`.
81
+ 5. If \`.work/config.json\` has \`workflow.planCheck: false\`, stop after planner self-check and explicitly report reduced assurance. This only skips the independent checker; it does not skip the step 3 alignment-proof gate when \`workflow.discuss: true\`.
81
82
  6. If \`workflow.planCheck: true\`, invoke the native \`gsdd-plan-checker\` subagent with fresh context.
82
83
  7. Pass only explicit inputs to the checker:
83
84
  - target phase goal and requirement IDs
84
- - relevant locked decisions / deferred items from \`.planning/SPEC.md\`
85
- - project config from \`.planning/config.json\`, especially \`workflow.discuss\` and \`workflow.planCheck\`
86
- - approach decisions from \`.planning/phases/*-APPROACH.md\` (if exists)
85
+ - relevant locked decisions / deferred items from \`.work/SPEC.md\`
86
+ - project config from \`.work/config.json\`, especially \`workflow.discuss\` and \`workflow.planCheck\`
87
+ - approach decisions from \`.work/phases/*-APPROACH.md\` (if exists)
87
88
  - relevant phase research file(s)
88
- - produced \`.planning/phases/*-PLAN.md\` file(s)
89
+ - produced \`.work/phases/*-PLAN.md\` file(s)
89
90
  8. Require the checker to return a single JSON object with this shape:
90
91
  {
91
92
  "status": "issues_found",
@@ -115,6 +116,7 @@ Return a concise orchestration summary:
115
116
 
116
117
  Never return raw checker JSON without summarizing it.
117
118
  `;
119
+ return localizeStateDirReferences(content, { stateDirName });
118
120
  }
119
121
 
120
122
  function renderClaudePlanCommand({ skillPath = '.claude/skills/gsdd-plan/SKILL.md' } = {}) {
@@ -133,7 +135,7 @@ Rules:
133
135
  `;
134
136
  }
135
137
 
136
- function createClaudeAdapter({ cwd, workflows, renderSkillContent, getDelegateContent, resolveRuntimeAgentModel }) {
138
+ function createClaudeAdapter({ cwd, workflows, stateDirName = '.work', renderSkillContent, getDelegateContent, resolveRuntimeAgentModel }) {
137
139
  const skillsDir = join(cwd, '.claude', 'skills');
138
140
  const commandsDir = join(cwd, '.claude', 'commands');
139
141
  const agentsDir = join(cwd, '.claude', 'agents');
@@ -169,8 +171,8 @@ function createClaudeAdapter({ cwd, workflows, renderSkillContent, getDelegateCo
169
171
  const dir = join(skillsDir, workflow.name);
170
172
  mkdirSync(dir, { recursive: true });
171
173
  const content = workflow.name === 'gsdd-plan'
172
- ? renderClaudePlanSkill()
173
- : renderSkillContent(workflow);
174
+ ? renderClaudePlanSkill({ stateDirName })
175
+ : renderSkillContent(workflow, { stateDirName });
174
176
  writeFileSync(join(dir, 'SKILL.md'), content);
175
177
  }
176
178
 
@@ -6,6 +6,7 @@ import {
6
6
  MAX_CHECKER_CYCLES,
7
7
  CHECKER_STATUSES,
8
8
  } from '../lib/plan-constants.mjs';
9
+ import { localizeStateDirReferences } from '../lib/rendering.mjs';
9
10
 
10
11
  function expandHome(filePath) {
11
12
  if (!filePath) return filePath;
@@ -145,8 +146,8 @@ ${delegateContent.trim()}
145
146
  `;
146
147
  }
147
148
 
148
- function renderOpenCodePlanCommand({ skillPath = '.agents/skills/gsdd-plan/SKILL.md' } = {}) {
149
- return `---
149
+ function renderOpenCodePlanCommand({ skillPath = '.agents/skills/gsdd-plan/SKILL.md', stateDirName = '.work' } = {}) {
150
+ const content = `---
150
151
  description: OpenCode-native phase planning with fresh-context plan checking for GSDD
151
152
  subtask: false
152
153
  ---
@@ -165,25 +166,25 @@ Native OpenCode adapter rule:
165
166
  - Do NOT claim that other runtimes have the same behavior unless their own adapters explicitly implement and prove it.
166
167
 
167
168
  Execution flow:
168
- 1. Read \`.planning/SPEC.md\`, \`.planning/ROADMAP.md\`, \`.planning/config.json\`, relevant phase research, and any existing phase plan files.
169
+ 1. Read \`.work/SPEC.md\`, \`.work/ROADMAP.md\`, \`.work/config.json\`, relevant phase research, and any existing phase plan files.
169
170
  2. Resolve the target phase from the command arguments. If no phase is provided, choose the first roadmap phase that is not complete.
170
171
  3. **Approach exploration** (before planning):
171
- a. Check \`.planning/config.json\` for \`workflow.discuss\`. If \`false\` or missing, skip to step 4 and report \`reduced_alignment\` in the summary.
172
+ a. Check \`.work/config.json\` for \`workflow.discuss\`. If \`false\` or missing, skip to step 4 and report \`reduced_alignment\` in the summary.
172
173
  b. Check if \`{phase_dir}/{padded_phase}-APPROACH.md\` exists. If it does, offer the user: "Use existing" / "Update it" / "View it". If "Use existing", load decisions, then validate the alignment proof before step 4; proofless or invalid existing APPROACH.md must be updated, not silently trusted.
173
- c. If no APPROACH.md exists (or user chose "Update"): invoke the \`gsdd-approach-explorer\` subagent with the phase goal, requirement IDs, project config from \`.planning/config.json\` (especially \`workflow.discuss\`), SPEC locked decisions, phase research, and relevant codebase files.
174
+ c. If no APPROACH.md exists (or user chose "Update"): invoke the \`gsdd-approach-explorer\` subagent with the phase goal, requirement IDs, project config from \`.work/config.json\` (especially \`workflow.discuss\`), SPEC locked decisions, phase research, and relevant codebase files.
174
175
  d. The explorer runs a GSD-style interactive conversation with the user (gray areas, research, deep-dive questions, assumptions) and writes APPROACH.md.
175
176
  e. Before planning, confirm APPROACH.md records all canonical proof fields: \`alignment_status\`, \`alignment_method\`, \`user_confirmed_at\`, \`explicit_skip_approved\`, \`skip_scope\`, \`skip_rationale\`, and \`confirmed_decisions\`. For \`alignment_status: user_confirmed\`, \`confirmed_decisions\` must name the locked decisions and skip fields may be \`false\`/\`N/A\`; for \`alignment_status: approved_skip\`, \`explicit_skip_approved: true\`, \`skip_scope\`, and \`skip_rationale\` must be substantive. Agent-only "No questions needed" is not valid proof under \`workflow.discuss: true\`.
176
177
  f. Load APPROACH.md decisions as locked constraints alongside SPEC.md decisions.
177
178
  4. Produce the initial phase plan according to \`${skillPath}\`. Pass APPROACH.md decisions (if any) as locked constraints to the planner.
178
- 5. If \`.planning/config.json\` has \`workflow.planCheck: false\`, stop after planner self-check and explicitly report reduced assurance. This only skips the independent checker; it does not skip the step 3 alignment-proof gate when \`workflow.discuss: true\`.
179
+ 5. If \`.work/config.json\` has \`workflow.planCheck: false\`, stop after planner self-check and explicitly report reduced assurance. This only skips the independent checker; it does not skip the step 3 alignment-proof gate when \`workflow.discuss: true\`.
179
180
  6. If \`workflow.planCheck: true\`, invoke the hidden \`gsdd-plan-checker\` subagent with fresh context.
180
181
  7. Pass only explicit inputs to the checker:
181
182
  - target phase goal and requirement IDs
182
- - relevant locked decisions / deferred items from \`.planning/SPEC.md\`
183
- - project config from \`.planning/config.json\`, especially \`workflow.discuss\` and \`workflow.planCheck\`
184
- - approach decisions from \`.planning/phases/*-APPROACH.md\` (if exists)
183
+ - relevant locked decisions / deferred items from \`.work/SPEC.md\`
184
+ - project config from \`.work/config.json\`, especially \`workflow.discuss\` and \`workflow.planCheck\`
185
+ - approach decisions from \`.work/phases/*-APPROACH.md\` (if exists)
185
186
  - relevant phase research file(s)
186
- - produced \`.planning/phases/*-PLAN.md\` file(s)
187
+ - produced \`.work/phases/*-PLAN.md\` file(s)
187
188
  8. Require the checker to return a single JSON object with this shape:
188
189
  {
189
190
  "status": "issues_found",
@@ -213,11 +214,13 @@ Return a concise orchestration summary:
213
214
 
214
215
  Never return raw checker JSON without summarizing it.
215
216
  `;
217
+ return localizeStateDirReferences(content, { stateDirName });
216
218
  }
217
219
 
218
220
  function createOpenCodeAdapter({
219
221
  cwd,
220
222
  workflows,
223
+ stateDirName = '.work',
221
224
  renderOpenCodeCommandContent,
222
225
  getDelegateContent,
223
226
  getRuntimeModelOverride,
@@ -247,8 +250,8 @@ function createOpenCodeAdapter({
247
250
  mkdirSync(commandsDir, { recursive: true });
248
251
  for (const workflow of workflows) {
249
252
  const content = workflow.name === 'gsdd-plan'
250
- ? renderOpenCodePlanCommand()
251
- : renderOpenCodeCommandContent(workflow);
253
+ ? renderOpenCodePlanCommand({ stateDirName })
254
+ : renderOpenCodeCommandContent(workflow, { stateDirName });
252
255
  writeFileSync(
253
256
  join(commandsDir, `${workflow.name}.md`),
254
257
  content
package/bin/gsdd.mjs CHANGED
@@ -11,19 +11,16 @@ import {
11
11
  upsertBoundedBlock,
12
12
  getDelegateContent,
13
13
  } from './lib/rendering.mjs';
14
- import { loadProjectModelConfig, getRuntimeModelOverride, resolveRuntimeAgentModel, cmdModels, cmdRigor } from './lib/models.mjs';
14
+ import { loadProjectModelConfig, getRuntimeModelOverride, resolveRuntimeAgentModel, cmdModels, cmdRigor } from './lib/config.mjs';
15
15
  import { createCmdInit, createCmdUpdate, cmdHelp } from './lib/init.mjs';
16
16
  import { createCmdInstall } from './lib/global-install.mjs';
17
17
  import { cmdFindPhase, cmdVerify, cmdScaffold, cmdPhaseStatus } from './lib/phase.mjs';
18
18
  import { cmdFileOp } from './lib/file-ops.mjs';
19
19
  import { createCmdHealth } from './lib/health.mjs';
20
20
  import { cmdLifecyclePreflight } from './lib/lifecycle-preflight.mjs';
21
- import { cmdSessionFingerprint } from './lib/session-fingerprint.mjs';
22
- import { cmdUiProof } from './lib/ui-proof.mjs';
23
- import { cmdControlMap } from './lib/control-map.mjs';
24
- import { createCmdCloseoutReport } from './lib/closeout-report.mjs';
25
21
  import { createCmdNext } from './lib/next.mjs';
26
22
  import { resolveWorkspaceContext } from './lib/workspace-root.mjs';
23
+ import { resolveStateDir } from './lib/state-dir.mjs';
27
24
  import { FRAMEWORK_VERSION, WORKFLOWS } from './lib/workflows.mjs';
28
25
  const __filename = fileURLToPath(import.meta.url);
29
26
  const __dirname = dirname(__filename);
@@ -35,9 +32,11 @@ const IS_MAIN = process.argv[1] ? realpathSync(process.argv[1]) === realpathSync
35
32
  const [,, command, ...args] = process.argv;
36
33
 
37
34
  function createCliContext(cwd = process.cwd()) {
35
+ const state = resolveStateDir(cwd);
38
36
  return {
39
37
  cwd,
40
- planningDir: join(cwd, '.planning'),
38
+ planningDir: state.dir,
39
+ stateDirName: state.name,
41
40
  distilledDir: DISTILLED_DIR,
42
41
  agentsDir: AGENTS_DIR,
43
42
  packageName: PACKAGE_JSON.name,
@@ -50,6 +49,7 @@ function createCliContext(cwd = process.cwd()) {
50
49
  adapters: createAdapterRegistry({
51
50
  cwd,
52
51
  workflows: WORKFLOWS,
52
+ stateDirName: state.name,
53
53
  renderAgentsBoundedBlock,
54
54
  renderAgentsFileContent,
55
55
  renderOpenCodeCommandContent,
@@ -67,7 +67,6 @@ const INIT_CONTEXT = createCliContext(process.cwd());
67
67
  const cmdInit = createCmdInit(INIT_CONTEXT);
68
68
  const cmdInstall = createCmdInstall(INIT_CONTEXT);
69
69
  const cmdHealth = createCmdHealth(INIT_CONTEXT);
70
- const cmdCloseoutReport = createCmdCloseoutReport(INIT_CONTEXT);
71
70
  const cmdNext = createCmdNext(INIT_CONTEXT);
72
71
 
73
72
  const cmdUpdate = (...updateArgs) => {
@@ -89,10 +88,6 @@ const COMMANDS = {
89
88
  next: cmdNext,
90
89
  'file-op': cmdFileOp,
91
90
  'lifecycle-preflight': cmdLifecyclePreflight,
92
- 'session-fingerprint': cmdSessionFingerprint,
93
- 'ui-proof': cmdUiProof,
94
- 'control-map': cmdControlMap,
95
- 'closeout-report': cmdCloseoutReport,
96
91
  'find-phase': cmdFindPhase,
97
92
  'phase-status': cmdPhaseStatus,
98
93
  verify: cmdVerify,
@@ -118,5 +113,13 @@ async function runCli(cliCommand = command, ...cliArgs) {
118
113
  await COMMANDS[cliCommand](...normalizedArgs);
119
114
  }
120
115
 
121
- if (IS_MAIN) await runCli();
122
- export { cmdHelp, cmdInit, cmdInstall, cmdUpdate, cmdModels, cmdRigor, cmdHealth, cmdNext, cmdFileOp, cmdLifecyclePreflight, cmdSessionFingerprint, cmdUiProof, cmdControlMap, cmdCloseoutReport, cmdFindPhase, cmdPhaseStatus, cmdVerify, cmdScaffold, runCli, FRAMEWORK_VERSION, createCliContext };
116
+ if (IS_MAIN) {
117
+ await runCli();
118
+ // D-47: interactive prompts (raw-mode keypress pickers) can leave stdin
119
+ // referenced; release it so the process exits when work is done.
120
+ if (process.stdin.isTTY) {
121
+ process.stdin.pause();
122
+ if (typeof process.stdin.unref === 'function') process.stdin.unref();
123
+ }
124
+ }
125
+ export { cmdHelp, cmdInit, cmdInstall, cmdUpdate, cmdModels, cmdRigor, cmdHealth, cmdNext, cmdFileOp, cmdLifecyclePreflight, cmdFindPhase, cmdPhaseStatus, cmdVerify, cmdScaffold, runCli, FRAMEWORK_VERSION, createCliContext };
@@ -8,6 +8,7 @@ import { join } from 'path';
8
8
  import { CLAUDE_MODEL_PROFILES } from '../adapters/claude.mjs';
9
9
  import { detectOpenCodeConfiguredModel } from '../adapters/opencode.mjs';
10
10
  import { parseFlagValue, output } from './cli-utils.mjs';
11
+ import { resolveStateDir } from './state-dir.mjs';
11
12
 
12
13
  export const DEFAULT_GIT_PROTOCOL = {
13
14
  branch: 'Follow the existing repo or team branching convention. Use a feature branch for significant changes when no convention exists.',
@@ -83,11 +84,15 @@ export function buildDefaultConfig({ autoAdvance = false } = {}) {
83
84
  }
84
85
 
85
86
  export function isProjectInitialized(cwd = process.cwd()) {
86
- return existsSync(join(cwd, '.planning', 'config.json'));
87
+ return existsSync(join(resolveStateDir(cwd).dir, 'config.json'));
88
+ }
89
+
90
+ function configPathLabel(cwd = process.cwd()) {
91
+ return `${resolveStateDir(cwd).name}/config.json`;
87
92
  }
88
93
 
89
94
  export function loadProjectModelConfig(cwd = process.cwd()) {
90
- const configPath = join(cwd, '.planning', 'config.json');
95
+ const configPath = join(resolveStateDir(cwd).dir, 'config.json');
91
96
  if (!existsSync(configPath)) return buildDefaultConfig();
92
97
 
93
98
  try {
@@ -96,35 +101,37 @@ export function loadProjectModelConfig(cwd = process.cwd()) {
96
101
  ...JSON.parse(readFileSync(configPath, 'utf-8')),
97
102
  };
98
103
  } catch (e) {
99
- console.error(`WARNING: .planning/config.json is malformed (${e.message}). Using defaults.`);
104
+ console.error(`WARNING: ${configPathLabel(cwd)} is malformed (${e.message}). Using defaults.`);
100
105
  return buildDefaultConfig();
101
106
  }
102
107
  }
103
108
 
104
109
  function loadConfigForMutation(cwd = process.cwd()) {
105
- const configPath = join(cwd, '.planning', 'config.json');
110
+ const state = resolveStateDir(cwd);
111
+ const configPath = join(state.dir, 'config.json');
112
+ const pathLabel = `${state.name}/config.json`;
106
113
  let raw;
107
114
  try {
108
115
  raw = readFileSync(configPath, 'utf-8');
109
116
  } catch (e) {
110
- return { ok: false, error: `could not read config file (${e.message})` };
117
+ return { ok: false, pathLabel, error: `could not read config file (${e.message})` };
111
118
  }
112
119
  try {
113
- return { ok: true, config: { ...buildDefaultConfig(), ...JSON.parse(raw) } };
120
+ return { ok: true, pathLabel, config: { ...buildDefaultConfig(), ...JSON.parse(raw) } };
114
121
  } catch (e) {
115
- return { ok: false, error: `malformed JSON (${e.message})` };
122
+ return { ok: false, pathLabel, error: `malformed JSON (${e.message})` };
116
123
  }
117
124
  }
118
125
 
119
126
  export function ensureProjectConfig(cwd = process.cwd()) {
120
- mkdirSync(join(cwd, '.planning'), { recursive: true });
127
+ mkdirSync(resolveStateDir(cwd).dir, { recursive: true });
121
128
  const config = loadProjectModelConfig(cwd);
122
129
  writeProjectConfig(config, cwd);
123
130
  return config;
124
131
  }
125
132
 
126
133
  export function writeProjectConfig(config, cwd = process.cwd()) {
127
- const configPath = join(cwd, '.planning', 'config.json');
134
+ const configPath = join(resolveStateDir(cwd).dir, 'config.json');
128
135
  writeFileSync(configPath, JSON.stringify(config, null, 2));
129
136
  }
130
137
 
@@ -272,7 +279,7 @@ function cmdModelsProfile(profile) {
272
279
 
273
280
  const result = loadConfigForMutation();
274
281
  if (!result.ok) {
275
- console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
282
+ console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
276
283
  process.exitCode = 1;
277
284
  return;
278
285
  }
@@ -306,7 +313,7 @@ function cmdModelsAgentProfile(args) {
306
313
 
307
314
  const result = loadConfigForMutation();
308
315
  if (!result.ok) {
309
- console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
316
+ console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
310
317
  process.exitCode = 1;
311
318
  return;
312
319
  }
@@ -334,7 +341,7 @@ function cmdModelsClearAgentProfile(args) {
334
341
 
335
342
  const result = loadConfigForMutation();
336
343
  if (!result.ok) {
337
- console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
344
+ console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
338
345
  process.exitCode = 1;
339
346
  return;
340
347
  }
@@ -384,7 +391,7 @@ function cmdModelsSetRuntimeOverride(args) {
384
391
 
385
392
  const result = loadConfigForMutation();
386
393
  if (!result.ok) {
387
- console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
394
+ console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
388
395
  process.exitCode = 1;
389
396
  return;
390
397
  }
@@ -420,7 +427,7 @@ function cmdModelsClearRuntimeOverride(args) {
420
427
 
421
428
  const result = loadConfigForMutation();
422
429
  if (!result.ok) {
423
- console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
430
+ console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
424
431
  process.exitCode = 1;
425
432
  return;
426
433
  }
@@ -499,7 +506,7 @@ function cmdRigorSetProfile(level) {
499
506
  }
500
507
  const result = loadConfigForMutation();
501
508
  if (!result.ok) {
502
- console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running rigor mutations.`);
509
+ console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running rigor mutations.`);
503
510
  process.exitCode = 1;
504
511
  return;
505
512
  }
@@ -529,7 +536,7 @@ function cmdRigorSetStep(step, level) {
529
536
  }
530
537
  const result = loadConfigForMutation();
531
538
  if (!result.ok) {
532
- console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running rigor mutations.`);
539
+ console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running rigor mutations.`);
533
540
  process.exitCode = 1;
534
541
  return;
535
542
  }