gsdd-cli 0.3.1 → 0.18.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 (62) hide show
  1. package/README.md +131 -67
  2. package/agents/DISTILLATION.md +15 -13
  3. package/agents/README.md +1 -1
  4. package/agents/planner.md +2 -0
  5. package/bin/adapters/agents.mjs +1 -0
  6. package/bin/adapters/claude.mjs +20 -4
  7. package/bin/adapters/codex.mjs +9 -1
  8. package/bin/adapters/opencode.mjs +20 -5
  9. package/bin/gsdd.mjs +24 -7
  10. package/bin/lib/cli-utils.mjs +1 -1
  11. package/bin/lib/evidence-contract.mjs +112 -0
  12. package/bin/lib/file-ops.mjs +161 -0
  13. package/bin/lib/health-truth.mjs +186 -0
  14. package/bin/lib/health.mjs +72 -67
  15. package/bin/lib/init-flow.mjs +50 -3
  16. package/bin/lib/init-prompts.mjs +22 -83
  17. package/bin/lib/init-runtime.mjs +47 -25
  18. package/bin/lib/init.mjs +3 -3
  19. package/bin/lib/lifecycle-preflight.mjs +333 -0
  20. package/bin/lib/lifecycle-state.mjs +293 -0
  21. package/bin/lib/models.mjs +19 -4
  22. package/bin/lib/phase.mjs +159 -18
  23. package/bin/lib/plan-constants.mjs +30 -0
  24. package/bin/lib/provenance.mjs +165 -0
  25. package/bin/lib/rendering.mjs +8 -0
  26. package/bin/lib/runtime-freshness.mjs +239 -0
  27. package/bin/lib/session-fingerprint.mjs +106 -0
  28. package/bin/lib/templates.mjs +17 -0
  29. package/distilled/DESIGN.md +733 -49
  30. package/distilled/EVIDENCE-INDEX.md +402 -0
  31. package/distilled/README.md +73 -33
  32. package/distilled/SKILL.md +89 -85
  33. package/distilled/templates/agents.block.md +13 -84
  34. package/distilled/templates/agents.md +0 -7
  35. package/distilled/templates/delegates/plan-checker.md +6 -3
  36. package/distilled/workflows/audit-milestone.md +56 -6
  37. package/distilled/workflows/complete-milestone.md +333 -0
  38. package/distilled/workflows/execute.md +201 -19
  39. package/distilled/workflows/map-codebase.md +17 -4
  40. package/distilled/workflows/new-milestone.md +262 -0
  41. package/distilled/workflows/new-project.md +7 -6
  42. package/distilled/workflows/pause.md +40 -6
  43. package/distilled/workflows/plan-milestone-gaps.md +183 -0
  44. package/distilled/workflows/plan.md +77 -11
  45. package/distilled/workflows/progress.md +107 -29
  46. package/distilled/workflows/quick.md +23 -12
  47. package/distilled/workflows/resume.md +135 -12
  48. package/distilled/workflows/verify-work.md +260 -0
  49. package/distilled/workflows/verify.md +159 -33
  50. package/docs/BROWNFIELD-PROOF.md +95 -0
  51. package/docs/RUNTIME-SUPPORT.md +77 -0
  52. package/docs/USER-GUIDE.md +439 -0
  53. package/docs/VERIFICATION-DISCIPLINE.md +59 -0
  54. package/docs/claude/context-monitor.md +98 -0
  55. package/docs/proof/consumer-node-cli/README.md +37 -0
  56. package/docs/proof/consumer-node-cli/ROADMAP.md +14 -0
  57. package/docs/proof/consumer-node-cli/SPEC.md +17 -0
  58. package/docs/proof/consumer-node-cli/brief.md +9 -0
  59. package/docs/proof/consumer-node-cli/phases/01-foundation/01-01-PLAN.md +34 -0
  60. package/docs/proof/consumer-node-cli/phases/01-foundation/01-01-SUMMARY.md +10 -0
  61. package/docs/proof/consumer-node-cli/phases/01-foundation/01-VERIFICATION.md +30 -0
  62. package/package.json +38 -29
@@ -7,16 +7,21 @@ import { existsSync, readFileSync, readdirSync } from 'fs';
7
7
  import { join } from 'path';
8
8
  import { readManifest, detectModifications } from './manifest.mjs';
9
9
  import { output } from './cli-utils.mjs';
10
+ import { runTruthChecks, TRUTH_CHECK_IDS } from './health-truth.mjs';
11
+ import { evaluateLifecycleState } from './lifecycle-state.mjs';
12
+ import { evaluateRuntimeFreshness } from './runtime-freshness.mjs';
10
13
 
11
14
  /**
12
15
  * Factory function returning the health command.
13
- * ctx must provide: { frameworkVersion }
16
+ * ctx should provide: { frameworkVersion, workflows }
14
17
  */
15
18
  export function createCmdHealth(ctx) {
16
19
  return async function cmdHealth(...healthArgs) {
17
20
  const jsonMode = healthArgs.includes('--json');
18
21
  const cwd = process.cwd();
19
22
  const planningDir = join(cwd, '.planning');
23
+ const frameworkSourceMode = isFrameworkSourceRepo(cwd);
24
+ const healthCheckIds = ['E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7', 'E8', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', ...TRUTH_CHECK_IDS, 'I1', 'I2', 'I3'];
20
25
 
21
26
  // Pre-init guard
22
27
  if (!existsSync(join(planningDir, 'config.json'))) {
@@ -38,8 +43,10 @@ export function createCmdHealth(ctx) {
38
43
  // E1: config.json missing (already handled by pre-init guard, but keep for completeness)
39
44
  // E2: config.json missing required fields
40
45
  let config = null;
46
+ let configOk = false;
41
47
  try {
42
48
  config = JSON.parse(readFileSync(join(planningDir, 'config.json'), 'utf-8'));
49
+ configOk = true;
43
50
  const requiredFields = ['researchDepth', 'modelProfile', 'initVersion'];
44
51
  const missing = requiredFields.filter((f) => !(f in config));
45
52
  if (missing.length > 0) {
@@ -56,10 +63,11 @@ export function createCmdHealth(ctx) {
56
63
  const delegatesDir = join(templatesDir, 'delegates');
57
64
  const hasRolesDir = hasTemplatesDir && existsSync(rolesDir);
58
65
  const hasDelegatesDir = hasTemplatesDir && existsSync(delegatesDir);
66
+ const skipInstalledTemplateChecks = !hasTemplatesDir && frameworkSourceMode;
59
67
 
60
- if (!hasTemplatesDir) {
68
+ if (!hasTemplatesDir && !skipInstalledTemplateChecks) {
61
69
  errors.push({ id: 'E3', severity: 'ERROR', message: '.planning/templates/ missing', fix: 'Run `gsdd update --templates`' });
62
- } else {
70
+ } else if (hasTemplatesDir) {
63
71
  // E4: roles/ missing or empty
64
72
  if (!hasRolesDir) {
65
73
  errors.push({ id: 'E4', severity: 'ERROR', message: '.planning/templates/roles/ missing', fix: 'Run `gsdd update --templates`' });
@@ -79,13 +87,42 @@ export function createCmdHealth(ctx) {
79
87
  errors.push({ id: 'E5', severity: 'ERROR', message: '.planning/templates/delegates/ has 0 delegate files', fix: 'Run `gsdd update --templates`' });
80
88
  }
81
89
  }
90
+
91
+ // E6: research/ missing or empty
92
+ const researchDir = join(templatesDir, 'research');
93
+ if (!existsSync(researchDir)) {
94
+ errors.push({ id: 'E6', severity: 'ERROR', message: '.planning/templates/research/ missing', fix: 'Run `gsdd update --templates`' });
95
+ } else {
96
+ const researchFiles = readdirSync(researchDir).filter((f) => f.endsWith('.md'));
97
+ if (researchFiles.length === 0) {
98
+ errors.push({ id: 'E6', severity: 'ERROR', message: '.planning/templates/research/ has 0 template files', fix: 'Run `gsdd update --templates`' });
99
+ }
100
+ }
101
+
102
+ // E7: codebase/ missing or empty
103
+ const codebaseDir = join(templatesDir, 'codebase');
104
+ if (!existsSync(codebaseDir)) {
105
+ errors.push({ id: 'E7', severity: 'ERROR', message: '.planning/templates/codebase/ missing', fix: 'Run `gsdd update --templates`' });
106
+ } else {
107
+ const codebaseFiles = readdirSync(codebaseDir).filter((f) => f.endsWith('.md'));
108
+ if (codebaseFiles.length === 0) {
109
+ errors.push({ id: 'E7', severity: 'ERROR', message: '.planning/templates/codebase/ has 0 template files', fix: 'Run `gsdd update --templates`' });
110
+ }
111
+ }
112
+
113
+ // E8: critical root template files missing
114
+ const requiredRootFiles = ['spec.md', 'roadmap.md', 'auth-matrix.md'];
115
+ const missingRoot = requiredRootFiles.filter((f) => !existsSync(join(templatesDir, f)));
116
+ if (missingRoot.length > 0) {
117
+ errors.push({ id: 'E8', severity: 'ERROR', message: `.planning/templates/ missing critical root files: ${missingRoot.join(', ')}`, fix: 'Run `gsdd update --templates`' });
118
+ }
82
119
  }
83
120
 
84
121
  // --- WARNING checks ---
85
122
 
86
123
  // W1: generation-manifest.json missing
87
- const manifest = readManifest(planningDir);
88
- if (!manifest) {
124
+ const manifest = skipInstalledTemplateChecks ? null : readManifest(planningDir);
125
+ if (!manifest && !skipInstalledTemplateChecks) {
89
126
  warnings.push({ id: 'W1', severity: 'WARN', message: 'generation-manifest.json missing', fix: 'Run `gsdd update --templates` to create' });
90
127
  }
91
128
 
@@ -115,36 +152,28 @@ export function createCmdHealth(ctx) {
115
152
  const roadmapPath = join(planningDir, 'ROADMAP.md');
116
153
  const phasesDir = join(planningDir, 'phases');
117
154
  const roadmap = existsSync(roadmapPath) ? readFileSync(roadmapPath, 'utf-8') : null;
118
- const phaseArtifacts = existsSync(phasesDir) ? listPhaseArtifacts(phasesDir) : [];
155
+ const lifecycle = evaluateLifecycleState({ planningDir });
119
156
 
120
157
  if (roadmap && existsSync(phasesDir)) {
121
- const phaseNums = [];
122
- for (const line of roadmap.split('\n')) {
123
- const match = line.match(/^\s*[-*]\s*\[[ x-]\]\s*\*\*Phase\s+(\d+)/i);
124
- if (match) phaseNums.push(parseInt(match[1], 10));
125
- }
126
- for (const num of phaseNums) {
127
- const padded = String(num).padStart(2, '0');
128
- const hasFile = phaseArtifacts.some((artifact) => artifact.phasePrefix === padded || artifact.phasePrefix === String(num));
129
- if (!hasFile) {
130
- warnings.push({ id: 'W4', severity: 'WARN', message: `ROADMAP.md references Phase ${num} but no files found in .planning/phases/`, fix: 'Create missing phase dirs or update ROADMAP' });
131
- }
158
+ for (const phase of lifecycle.phases.filter((entry) => entry.status !== 'not_started' && !entry.hasArtifacts)) {
159
+ warnings.push({
160
+ id: 'W4',
161
+ severity: 'WARN',
162
+ message: `ROADMAP.md references active Phase ${phase.number} but no files found in .planning/phases/`,
163
+ fix: 'Create missing phase dirs or update ROADMAP',
164
+ });
132
165
  }
133
166
  }
134
167
 
135
168
  // W5: Phase dir has PLAN but no SUMMARY (stale in-progress)
136
- if (phaseArtifacts.length > 0) {
137
- const plans = phaseArtifacts.filter((artifact) => artifact.name.includes('PLAN'));
138
- for (const plan of plans) {
139
- const prefix = plan.name.split('-PLAN')[0];
140
- const hasSummary = phaseArtifacts.some((artifact) =>
141
- artifact.dir === plan.dir &&
142
- artifact.name.startsWith(prefix) &&
143
- artifact.name.includes('SUMMARY')
144
- );
145
- if (!hasSummary) {
146
- warnings.push({ id: 'W5', severity: 'WARN', message: `${plan.displayPath} exists but no matching SUMMARY found (stale in-progress?)`, fix: 'Resume or complete the phase' });
147
- }
169
+ if (lifecycle.incompletePlans.length > 0) {
170
+ for (const plan of lifecycle.incompletePlans) {
171
+ warnings.push({
172
+ id: 'W5',
173
+ severity: 'WARN',
174
+ message: `${plan.displayPath} exists but no matching SUMMARY found (stale in-progress?)`,
175
+ fix: 'Resume or complete the phase',
176
+ });
148
177
  }
149
178
  }
150
179
 
@@ -160,6 +189,12 @@ export function createCmdHealth(ctx) {
160
189
  warnings.push({ id: 'W6', severity: 'WARN', message: 'No adapter surfaces detected', fix: 'Run `gsdd init --tools <platform>`' });
161
190
  }
162
191
 
192
+ const runtimeFreshnessReport = configOk && Array.isArray(ctx.workflows)
193
+ ? evaluateRuntimeFreshness({ cwd, workflows: ctx.workflows })
194
+ : null;
195
+
196
+ warnings.push(...runTruthChecks(planningDir, cwd, healthCheckIds, { runtimeFreshnessReport }));
197
+
163
198
  // --- INFO checks ---
164
199
 
165
200
  // I1: generation manifest was produced by a different framework version
@@ -168,20 +203,12 @@ export function createCmdHealth(ctx) {
168
203
  }
169
204
 
170
205
  // I2: Phase completion count
171
- if (roadmap) {
172
- const lines = roadmap.split('\n');
173
- let total = 0;
174
- let done = 0;
175
- for (const line of lines) {
176
- const match = line.match(/^\s*[-*]\s*\[([x ]|-)\]\s*\*\*Phase\s+\d+/i);
177
- if (match) {
178
- total++;
179
- if (match[1] === 'x') done++;
180
- }
181
- }
182
- if (total > 0) {
183
- info.push({ id: 'I2', severity: 'INFO', message: `Phases: ${done}/${total} completed` });
184
- }
206
+ if (lifecycle.counts.total > 0) {
207
+ info.push({
208
+ id: 'I2',
209
+ severity: 'INFO',
210
+ message: `Phases: ${lifecycle.counts.completed}/${lifecycle.counts.total} completed`,
211
+ });
185
212
  }
186
213
 
187
214
  // I3: Which adapters are installed
@@ -220,29 +247,7 @@ export function createCmdHealth(ctx) {
220
247
  };
221
248
  }
222
249
 
223
- function listPhaseArtifacts(phasesDir) {
224
- const artifacts = [];
225
- for (const entry of readdirSync(phasesDir, { withFileTypes: true })) {
226
- const entryPath = join(phasesDir, entry.name);
227
- if (entry.isFile()) {
228
- artifacts.push(createPhaseArtifact('', entry.name));
229
- continue;
230
- }
231
- if (!entry.isDirectory()) continue;
232
- for (const child of readdirSync(entryPath, { withFileTypes: true })) {
233
- if (child.isFile()) {
234
- artifacts.push(createPhaseArtifact(entry.name, child.name));
235
- }
236
- }
237
- }
238
- return artifacts;
250
+ function isFrameworkSourceRepo(cwd) {
251
+ return existsSync(join(cwd, 'distilled', 'templates')) && existsSync(join(cwd, 'distilled', 'workflows'));
239
252
  }
240
253
 
241
- function createPhaseArtifact(dir, name) {
242
- return {
243
- dir,
244
- name,
245
- displayPath: dir ? `${dir}/${name}` : name,
246
- phasePrefix: name.match(/^(\d+(?:\.\d+)?)-/)?.[1] || dir.match(/^(\d+(?:\.\d+)?)-/)?.[1] || null,
247
- };
248
- }
@@ -3,7 +3,7 @@ import { join, isAbsolute } from 'path';
3
3
  import { renderSkillContent } from './rendering.mjs';
4
4
  import { buildManifest, writeManifest } from './manifest.mjs';
5
5
  import { parseFlagValue, parseToolsFlag, parseAutoFlag } from './cli-utils.mjs';
6
- import { buildDefaultConfig } from './models.mjs';
6
+ import { buildDefaultConfig, COST_PROFILES, RIGOR_PROFILES } from './models.mjs';
7
7
  import { installProjectTemplates, refreshTemplates } from './templates.mjs';
8
8
  import {
9
9
  detectPlatforms,
@@ -15,9 +15,34 @@ import {
15
15
  } from './init-runtime.mjs';
16
16
  import { createInitPromptApi } from './init-prompts.mjs';
17
17
 
18
+ function validateKindContract(adapter, cwd) {
19
+ if (!adapter.subagentFiles) return;
20
+ if (adapter.kind === 'native_capable') {
21
+ const missing = adapter.subagentFiles
22
+ .map(f => join(cwd, f))
23
+ .filter(p => !existsSync(p));
24
+ if (missing.length > 0) {
25
+ console.warn(
26
+ `[WARN] ${adapter.name} adapter (kind=native_capable) missing expected subagent files:\n` +
27
+ missing.map(p => ` - ${p}`).join('\n')
28
+ );
29
+ }
30
+ } else if (adapter.kind === 'governance_only') {
31
+ const unexpected = adapter.subagentFiles
32
+ .map(f => join(cwd, f))
33
+ .filter(p => existsSync(p));
34
+ if (unexpected.length > 0) {
35
+ console.warn(
36
+ `[WARN] ${adapter.name} adapter (kind=governance_only) unexpectedly generated subagent files:\n` +
37
+ unexpected.map(p => ` - ${p}`).join('\n')
38
+ );
39
+ }
40
+ }
41
+ }
42
+
18
43
  export function createCmdInit(ctx) {
19
44
  return async function cmdInit(...initArgs) {
20
- console.log('gsdd init - setting up SDD workflow\n');
45
+ console.log('gsdd init - setting up GSDD workflow\n');
21
46
 
22
47
  const isAuto = parseAutoFlag(initArgs);
23
48
  const toolsFlag = parseFlagValue(initArgs, '--tools');
@@ -86,6 +111,7 @@ export function createCmdInit(ctx) {
86
111
 
87
112
  for (const adapter of resolveAdapters(ctx.adapters, interactiveSession.adapterTargets)) {
88
113
  adapter.generate();
114
+ validateKindContract(adapter, ctx.cwd);
89
115
  console.log(` - ${adapter.summary('generated')}`);
90
116
  }
91
117
 
@@ -93,7 +119,8 @@ export function createCmdInit(ctx) {
93
119
  writeManifest(ctx.planningDir, manifest);
94
120
  console.log(' - wrote generation manifest');
95
121
 
96
- console.log('\nSDD initialized.');
122
+ console.log('\nGSDD initialized.');
123
+ printInitSummary(interactiveSession.config ?? buildDefaultConfig({ autoAdvance: isAuto }));
97
124
  console.log('Next: run the new-project workflow to produce SPEC.md and ROADMAP.md:\n');
98
125
  printPostInitRouting(interactiveSession.selectedRuntimes);
99
126
  };
@@ -132,6 +159,7 @@ export function createCmdUpdate(ctx) {
132
159
  console.log(` - would update ${adapter.name} adapter`);
133
160
  } else {
134
161
  adapter.generate();
162
+ validateKindContract(adapter, ctx.cwd);
135
163
  console.log(` - ${adapter.summary('updated')}`);
136
164
  }
137
165
  updated = true;
@@ -218,3 +246,22 @@ function printPostInitRouting(selectedRuntimes) {
218
246
  }
219
247
  console.log('');
220
248
  }
249
+
250
+ function printInitSummary(config) {
251
+ const rigor = Object.entries(RIGOR_PROFILES).find(([, profile]) => (
252
+ profile.researchDepth === config.researchDepth
253
+ && profile.workflow.research === config.workflow?.research
254
+ && profile.workflow.discuss === config.workflow?.discuss
255
+ && profile.workflow.planCheck === config.workflow?.planCheck
256
+ && profile.workflow.verifier === config.workflow?.verifier
257
+ ))?.[0] ?? 'balanced';
258
+ const cost = Object.entries(COST_PROFILES).find(([, profile]) => (
259
+ profile.modelProfile === config.modelProfile
260
+ && profile.parallelization === config.parallelization
261
+ ))?.[0] ?? 'balanced';
262
+
263
+ console.log(`Rigor: ${rigor} Cost: ${cost} Track .planning/ in git: ${config.commitDocs ? 'yes' : 'no'}`);
264
+ console.log('Workflows: new-project → plan → execute → verify → progress');
265
+ console.log('Edit .planning/config.json to fine-tune (verifier, gitProtocol, individual workflow flags).');
266
+ console.log('');
267
+ }
@@ -1,5 +1,5 @@
1
1
  import * as readline from 'readline';
2
- import { DEFAULT_GIT_PROTOCOL, normalizeModelProfile } from './models.mjs';
2
+ import { DEFAULT_GIT_PROTOCOL, resolveCost, resolveRigor } from './models.mjs';
3
3
  import { buildRuntimeChoices, INIT_VERSION, resolveWizardAdapterTargets } from './init-runtime.mjs';
4
4
 
5
5
  const ANSI = {
@@ -51,112 +51,51 @@ export function createInitPromptApi({ input = process.stdin, output = process.st
51
51
  export async function promptForConfig(cwd, { input = process.stdin, output = process.stdout } = {}) {
52
52
  output.write(`\n${ANSI.bold}Step 3 of 3 - Configure planning defaults${ANSI.reset}\n`);
53
53
 
54
- const researchDepth = await promptSingleSelect({
54
+ const rigor = await promptSingleSelect({
55
55
  input,
56
56
  output,
57
- title: 'Research depth',
57
+ title: 'Rigor',
58
58
  choices: [
59
- { value: 'balanced', label: 'balanced', description: 'SOTA research per phase (recommended)' },
60
- { value: 'fast', label: 'fast', description: 'Skip deeper domain research and plan from current context' },
61
- { value: 'deep', label: 'deep', description: 'Exhaustive research sweeps and parallel researchers' },
59
+ { value: 'quick', label: 'quick', description: 'Faster setup with lighter planning rigor' },
60
+ { value: 'balanced', label: 'balanced', description: 'Recommended default for most projects' },
61
+ { value: 'thorough', label: 'thorough', description: 'Maximum research and plan review rigor' },
62
62
  ],
63
- defaultIndex: 0,
63
+ defaultIndex: 1,
64
64
  });
65
- const parallelization = await promptSingleSelect({
65
+ const cost = await promptSingleSelect({
66
66
  input,
67
67
  output,
68
- title: 'Parallelization',
69
- choices: yesNoChoices('Run independent agents in parallel?', true),
70
- defaultIndex: 0,
68
+ title: 'Cost',
69
+ choices: [
70
+ { value: 'budget', label: 'budget', description: 'Lower-cost model profile and no parallelization' },
71
+ { value: 'balanced', label: 'balanced', description: 'Recommended cost/quality tradeoff' },
72
+ { value: 'quality', label: 'quality', description: 'Maximum quality with parallel work enabled' },
73
+ ],
74
+ defaultIndex: 1,
71
75
  });
72
76
  const commitDocs = await promptSingleSelect({
73
77
  input,
74
78
  output,
75
79
  title: 'Planning docs in git',
76
- choices: yesNoChoices('Track .planning/ in git?', true),
77
- defaultIndex: 0,
78
- });
79
- const modelProfile = await promptSingleSelect({
80
- input,
81
- output,
82
- title: 'Model profile',
83
80
  choices: [
84
- { value: 'balanced', label: 'balanced', description: 'Capable default for most agents (recommended)' },
85
- { value: 'quality', label: 'quality', description: 'Most capable model for research and roadmap work' },
86
- { value: 'budget', label: 'budget', description: 'Cheapest and fastest model profile' },
81
+ { value: true, label: 'yes', description: 'Track .planning/ in git.' },
82
+ { value: false, label: 'no', description: 'Keep .planning/ local only.' },
87
83
  ],
88
84
  defaultIndex: 0,
89
85
  });
90
- const workflowResearch = await promptSingleSelect({
91
- input,
92
- output,
93
- title: 'Workflow research',
94
- choices: yesNoChoices('Research before planning each phase?', true),
95
- defaultIndex: 0,
96
- });
97
- const workflowDiscuss = await promptSingleSelect({
98
- input,
99
- output,
100
- title: 'Approach discussion',
101
- choices: yesNoChoices('Explore approaches with the user before planning?', false),
102
- defaultIndex: 0,
103
- });
104
- const workflowPlanCheck = await promptSingleSelect({
105
- input,
106
- output,
107
- title: 'Plan checking',
108
- choices: yesNoChoices('Run the fresh-context plan checker before execution?', true),
109
- defaultIndex: 0,
110
- });
111
- const workflowVerifier = await promptSingleSelect({
112
- input,
113
- output,
114
- title: 'Phase verification',
115
- choices: yesNoChoices('Run phase verification after execution?', true),
116
- defaultIndex: 0,
117
- });
118
86
 
119
- const gitProtocol = await promptGitProtocol(cwd, { input, output });
87
+ const rigorConfig = resolveRigor(rigor);
88
+ const costConfig = resolveCost(cost);
120
89
 
121
90
  return {
122
- researchDepth,
123
- parallelization,
91
+ ...rigorConfig,
92
+ ...costConfig,
124
93
  commitDocs,
125
- modelProfile: normalizeModelProfile(modelProfile),
126
- workflow: {
127
- research: workflowResearch,
128
- discuss: workflowDiscuss,
129
- planCheck: workflowPlanCheck,
130
- verifier: workflowVerifier,
131
- },
132
- gitProtocol,
94
+ gitProtocol: { ...DEFAULT_GIT_PROTOCOL },
133
95
  initVersion: INIT_VERSION,
134
96
  };
135
97
  }
136
98
 
137
- export async function promptGitProtocol(cwd, { input = process.stdin, output = process.stdout } = {}) {
138
- if (typeof input.resume === 'function') input.resume();
139
- output.write(`\n${ANSI.bold}Version control guidance${ANSI.reset}\n`);
140
- output.write(`${ANSI.dim}This is advisory only. Repo or user conventions still win.${ANSI.reset}\n`);
141
- const rl = readline.createInterface({ input, output });
142
- const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
143
-
144
- try {
145
- let branch = await askQuestion(' Branching guidance (Enter for default): ');
146
- branch = branch.trim() || DEFAULT_GIT_PROTOCOL.branch;
147
-
148
- let commit = await askQuestion(' Commit guidance (Enter for default): ');
149
- commit = commit.trim() || DEFAULT_GIT_PROTOCOL.commit;
150
-
151
- let pr = await askQuestion(' PR guidance (Enter for default): ');
152
- pr = pr.trim() || DEFAULT_GIT_PROTOCOL.pr;
153
-
154
- return { branch, commit, pr };
155
- } finally {
156
- rl.close();
157
- }
158
- }
159
-
160
99
  export function yesNoChoices(prompt, defaultYes) {
161
100
  return [
162
101
  { value: true, label: 'yes', description: `${prompt} Yes.` },
@@ -2,37 +2,37 @@ const RUNTIME_OPTIONS = [
2
2
  {
3
3
  id: 'claude',
4
4
  label: 'Claude Code',
5
- description: 'Native skills, commands, and agents',
5
+ description: 'Directly validated native skills, commands, and agents with local freshness checks',
6
6
  kind: 'native',
7
7
  },
8
8
  {
9
9
  id: 'opencode',
10
10
  label: 'OpenCode',
11
- description: 'Native slash commands and agents',
11
+ description: 'Directly validated native slash commands and agents with local freshness checks',
12
12
  kind: 'native',
13
13
  },
14
14
  {
15
15
  id: 'codex',
16
16
  label: 'Codex CLI',
17
- description: 'Portable skills plus native checker agents',
17
+ description: 'Directly validated portable skills plus native checker agents with local freshness checks',
18
18
  kind: 'native',
19
19
  },
20
20
  {
21
21
  id: 'cursor',
22
22
  label: 'Cursor',
23
- description: 'Skills-native slash commands from .agents/skills/',
23
+ description: 'Qualified support via skills-native slash commands from .agents/skills/ with the same local freshness checks',
24
24
  kind: 'skills_native',
25
25
  },
26
26
  {
27
27
  id: 'copilot',
28
28
  label: 'GitHub Copilot',
29
- description: 'Skills-native slash commands from .agents/skills/',
29
+ description: 'Qualified support via skills-native slash commands from .agents/skills/ with the same local freshness checks',
30
30
  kind: 'skills_native',
31
31
  },
32
32
  {
33
33
  id: 'gemini',
34
34
  label: 'Gemini CLI',
35
- description: 'Skills-native slash commands from .agents/skills/',
35
+ description: 'Qualified support via skills-native slash commands from .agents/skills/ with the same local freshness checks',
36
36
  kind: 'skills_native',
37
37
  },
38
38
  ];
@@ -161,25 +161,31 @@ export function getPostInitRoutingLines(selectedRuntimes) {
161
161
 
162
162
  export function getHelpText() {
163
163
  return `
164
- gsdd - GSD Distilled CLI
165
- Spec-Driven Development for AI coding agents.
164
+ gsdd - Workspine CLI
165
+ Portable multi-runtime software delivery framework for AI coding agents.
166
166
 
167
167
  Usage: gsdd <command> [args]
168
168
 
169
- Commands:
170
- init [--tools <platform>] [--auto] [--brief <file>]
171
- Launch guided install wizard in TTYs, or use --tools for manual/headless setup
172
- --auto: non-interactive mode with smart defaults (requires --tools)
173
- --brief <file>: copy project brief to .planning/PROJECT_BRIEF.md
169
+ Commands:
170
+ init [--tools <platform>] [--auto] [--brief <file>]
171
+ Launch guided install wizard in TTYs, or use --tools for manual/headless setup
172
+ --auto: non-interactive mode with smart defaults (requires --tools)
173
+ --brief <file>: copy project brief to .planning/PROJECT_BRIEF.md
174
174
  update [--tools <platform>] [--templates] [--dry]
175
175
  Regenerate adapters from latest framework sources
176
176
  --templates: also refresh .planning/templates/ and roles
177
177
  --dry: preview changes without writing files
178
- health [--json] Check workspace integrity (healthy/degraded/broken)
179
- models [subcommand] Inspect or update model profile / runtime overrides
180
- find-phase [N] Show phase info as JSON (for agent consumption)
181
- verify <N> Run artifact checks for phase N
182
- scaffold phase <N> [name] Create a new phase plan file
178
+ health [--json] Check workspace integrity (healthy/degraded/broken)
179
+ models [subcommand] Inspect or update model profile / runtime overrides
180
+ find-phase [N] Show phase info as JSON (for agent consumption)
181
+ verify <N> Run artifact checks for phase N
182
+ scaffold phase <N> [name] Create a new phase plan file
183
+ file-op <copy|delete|regex-sub>
184
+ Run deterministic workspace-confined file copy/delete/text mutation
185
+ phase-status <N> <status> Update ROADMAP.md phase status ([ ] / [-] / [x])
186
+ lifecycle-preflight <surface> [phase]
187
+ Inspect deterministic lifecycle gate results for a workflow surface
188
+ help Show this summary
183
189
 
184
190
  Platforms (for --tools):
185
191
  claude Generate Claude Code skills (.claude/skills/gsdd-*), commands (.claude/commands/gsdd-*.md), and native agents (.claude/agents/gsdd-*.md)
@@ -189,14 +195,18 @@ Platforms (for --tools):
189
195
  cursor Generate root AGENTS.md governance block; workflows are already discovered natively from .agents/skills/ (legacy alias kept for backward compatibility)
190
196
  copilot Generate root AGENTS.md governance block; workflows are already discovered natively from .agents/skills/ (legacy alias kept for backward compatibility)
191
197
  gemini Generate root AGENTS.md governance block; workflows are already discovered natively from .agents/skills/ (legacy alias kept for backward compatibility)
192
- all Generate all adapters (Claude, OpenCode, Codex, AGENTS-based surfaces)
198
+ all Generate all adapters (Claude, OpenCode, Codex, AGENTS.md, Cursor, Copilot, Gemini)
193
199
 
194
200
  Notes:
195
201
  - init always generates open-standard skills at .agents/skills/gsdd-* (portable workflow entrypoints)
202
+ - Workspine is the public product name; the retained package, command, workflow, and workspace contracts stay gsdd-cli, gsdd, gsdd-*, and .planning/
196
203
  - running plain \`gsdd init\` in a terminal opens the guided runtime-selection wizard
197
204
  - the wizard lets you pick runtimes first, then separately decide whether repo-wide AGENTS.md governance is worth installing
205
+ - \`gsdd health\` compares any installed generated runtime surfaces against current render output and points back to \`gsdd update\` when they drift
206
+ - directly validated launch surfaces in this repo are Claude Code, OpenCode, and Codex CLI
207
+ - Cursor, Copilot, and Gemini are qualified support through the shared .agents/skills/ surface plus optional governance
198
208
  - --tools remains the advanced/manual path and preserves legacy runtime aliases for backward compatibility
199
- - --tools codex generates .codex/agents/gsdd-plan-checker.toml (portable skill is the entry surface)
209
+ - --tools codex generates .codex/agents/gsdd-plan-checker.toml (portable skill is the entry surface; $gsdd-plan is plan-only until explicit $gsdd-execute)
200
210
  - root AGENTS.md is only written on init when explicitly requested via --tools agents, --tools all, or the wizard governance opt-in
201
211
 
202
212
  Examples:
@@ -217,8 +227,20 @@ Examples:
217
227
  npx gsdd-cli verify 1
218
228
  npx gsdd-cli scaffold phase 4 Payments
219
229
 
220
- Workflows (run via skills/adapters generated by init, not direct CLI):
221
- map-codebase Map or refresh codebase (.agents/skills/gsdd-map-codebase/)
222
- audit-milestone Audit a completed milestone (.agents/skills/gsdd-audit-milestone/)
223
- `;
224
- }
230
+ Workflows (run via skills/adapters generated by init, not direct CLI):
231
+ gsdd-new-project Full initializer: questioning, brownfield audit, research, spec, roadmap
232
+ gsdd-map-codebase Map or refresh brownfield codebase context
233
+ gsdd-plan Research, plan, and fresh-context plan check for a phase
234
+ gsdd-execute Execute a phase plan and write phase summaries
235
+ gsdd-verify Verify a completed phase with 3-level checks
236
+ gsdd-verify-work Conversational UAT validation for user-facing behavior
237
+ gsdd-audit-milestone Cross-phase integration, requirements coverage, and E2E audit
238
+ gsdd-complete-milestone Archive a shipped milestone and collapse roadmap state
239
+ gsdd-new-milestone Start the next milestone with goals, requirements, and phases
240
+ gsdd-plan-milestone-gaps Turn milestone-audit gaps into closure phases
241
+ gsdd-quick Bounded brownfield lane for sub-hour work
242
+ gsdd-pause Save session context to checkpoint
243
+ gsdd-resume Restore context and route to the next action
244
+ gsdd-progress Read-only status and routing surface
245
+ `;
246
+ }
package/bin/lib/init.mjs CHANGED
@@ -4,9 +4,9 @@ import { createCmdInit, createCmdUpdate } from './init-flow.mjs';
4
4
  import { createInitPromptApi, promptChoiceList } from './init-prompts.mjs';
5
5
  import { buildRuntimeChoices, detectPlatforms, getHelpText, normalizeRequestedTools } from './init-runtime.mjs';
6
6
 
7
- function cmdHelp() {
8
- console.log(getHelpText());
9
- }
7
+ function cmdHelp() {
8
+ console.log(getHelpText().trimEnd());
9
+ }
10
10
 
11
11
  export {
12
12
  buildRuntimeChoices,