roadmapsmith 0.9.33 → 0.9.34

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roadmapsmith",
3
- "version": "0.9.33",
3
+ "version": "0.9.34",
4
4
  "description": "Evidence-backed ROADMAP.md workflows for AI coding agents, with canonical RoadmapSmith status and maintain surfaces plus advanced sync/generate tools and legacy compatibility aliases.",
5
5
  "author": {
6
6
  "name": "PapiScholz"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roadmapsmith",
3
- "version": "0.9.33",
3
+ "version": "0.9.34",
4
4
  "description": "Evidence-backed ROADMAP.md workflows for AI coding agents, with canonical RoadmapSmith status and maintain surfaces plus advanced sync/generate tools and legacy compatibility aliases.",
5
5
  "author": {
6
6
  "name": "PapiScholz"
package/README.md CHANGED
@@ -8,7 +8,7 @@ This package owns the RoadmapSmith CLI, validator, sync engine, host setup files
8
8
 
9
9
  - `roadmapsmith setup`
10
10
  - `roadmapsmith zero`
11
- - `roadmapsmith maintain`
11
+ - `roadmapsmith maintain [--dry-run]`
12
12
  - `roadmapsmith status [--json]`
13
13
  - `roadmapsmith validate [--json] [--strict]`
14
14
  - `roadmapsmith update [--task <stable-id> --evidence "<single-line evidence>"]`
@@ -40,11 +40,39 @@ This package owns the RoadmapSmith CLI, validator, sync engine, host setup files
40
40
 
41
41
  ### Zero Mode
42
42
 
43
- Use for empty or low-context repositories. `roadmapsmith zero` runs the terminal interview, updates config, and generates the first roadmap.
43
+ Use for empty or low-context repositories. `roadmapsmith zero` runs the terminal interview when TTY is available, or consumes a complete brief from config plus flags in non-interactive environments.
44
+
45
+ Non-interactive inputs:
46
+
47
+ - `--product-name`
48
+ - `--primary-user`
49
+ - `--problem-statement`
50
+ - `--target-outcome`
51
+ - `--anti-goal` (repeatable)
52
+ - `--preferred-stack`
53
+ - `--constraint` (repeatable)
54
+ - `--done-criterion` (repeatable)
55
+
56
+ Without TTY, Zero Mode requires a complete brief from config/flags and fails clearly when required fields are missing.
44
57
 
45
58
  ### Sync/Audit Mode
46
59
 
47
- Use for repositories that already contain code, tests, docs, TODOs, or an existing roadmap. `roadmapsmith maintain` is the preserve-first one-command flow.
60
+ Use for repositories that already contain code, tests, docs, TODOs, or an existing roadmap. `roadmapsmith maintain` is the preserve-first one-command flow for an existing managed roadmap block.
61
+
62
+ If `ROADMAP.md` is authored and non-empty but has no `<!-- rs:managed:* -->` block:
63
+
64
+ - `maintain` refuses to seed managed content implicitly
65
+ - `update` is the conservative inline-annotation path
66
+ - `generate` is the explicit managed-section creation path
67
+
68
+ For write-capable surfaces (`maintain`, `generate`, `sync`, `update`), prefer `--dry-run` first.
69
+
70
+ ## Managed Block Ownership
71
+
72
+ - `maintain` owns the managed roadmap block only.
73
+ - `update` can annotate existing task lines even when no managed block exists.
74
+ - `generate` is the explicit path that creates or replaces managed roadmap content.
75
+ - Manually inserting empty `<!-- rs:managed:start -->` markers is no longer required as a workaround.
48
76
 
49
77
  ## Verification Model
50
78
 
package/bin/cli.js CHANGED
@@ -14,14 +14,21 @@ const { generateRoadmapDocument } = require('../src/generator');
14
14
  const { parseRoadmap, tasksInManagedBlock } = require('../src/parser');
15
15
  const { buildValidationContext, validateTasks, auditValidation, CONFIDENCE_RANK, applyMinimumConfidence } = require('../src/validator');
16
16
  const { applySync } = require('../src/sync');
17
- const { buildZeroModeConfigPatch, buildZeroModeDefaults, collectZeroModeAnswers, isInteractiveTerminal } = require('../src/zero');
17
+ const {
18
+ buildZeroModeConfigPatch,
19
+ collectZeroModeAnswers,
20
+ formatMissingZeroModeFields,
21
+ getMissingZeroModeFields,
22
+ isInteractiveTerminal,
23
+ resolveZeroModeAnswers
24
+ } = require('../src/zero');
18
25
 
19
26
  function printHelp() {
20
27
  console.log([
21
28
  'Usage:',
22
29
  ' Canonical commands:',
23
- ' roadmapsmith zero [--project-root <path>] [--config <path>]',
24
- ' roadmapsmith maintain [--project-root <path>] [--config <path>] [--roadmap-file <path>] [--full-regen] [--refresh-annotations]',
30
+ ' roadmapsmith zero [--project-root <path>] [--config <path>] [--product-name <text>] [--primary-user <text>] [--problem-statement <text>] [--target-outcome <text>] [--anti-goal <text> ...] [--preferred-stack <text>] [--constraint <text> ...] [--done-criterion <text> ...]',
31
+ ' roadmapsmith maintain [--project-root <path>] [--config <path>] [--roadmap-file <path>] [--dry-run] [--full-regen] [--refresh-annotations]',
25
32
  ' roadmapsmith status [--roadmap-file <path>] [--project-root <path>] [--config <path>] [--json]',
26
33
  ' roadmapsmith validate [--roadmap-file <path>] [--project-root <path>] [--config <path>] [--task <id|text>] [--json] [--strict]',
27
34
  ' roadmapsmith update [--task <stable-id> --evidence <text>] [--roadmap-file <path>] [--project-root <path>] [--config <path>] [--dry-run]',
@@ -140,6 +147,42 @@ function formatSetupVerb(result, dryRun) {
140
147
  return result.before == null ? 'Created' : 'Updated';
141
148
  }
142
149
 
150
+ function emitPreWriteWarning(roadmapFile, options = {}) {
151
+ if (options.dryRun || options.suppressWarning) {
152
+ return;
153
+ }
154
+ if (options.warningState && options.warningState.emitted) {
155
+ return;
156
+ }
157
+
158
+ const commandName = options.commandName || 'roadmapsmith';
159
+ console.log(`Warning: ${commandName} will modify ${roadmapFile}. Review the preview first with --dry-run.`);
160
+ if (options.managedSectionNote) {
161
+ console.log(options.managedSectionNote);
162
+ }
163
+
164
+ if (options.warningState) {
165
+ options.warningState.emitted = true;
166
+ }
167
+ }
168
+
169
+ function assertMaintainCanWrite(roadmapFile, content) {
170
+ if (content == null || String(content).trim().length === 0) {
171
+ return;
172
+ }
173
+
174
+ const parsed = parseRoadmap(content);
175
+ if (parsed.hasManagedBlock) {
176
+ return;
177
+ }
178
+
179
+ throw new Error(
180
+ `Refusing to let roadmapsmith maintain seed a managed section into an authored roadmap without <!-- rs:managed:start --> markers: ${roadmapFile}\n` +
181
+ 'For conservative inline annotations, run `roadmapsmith update --dry-run` then `roadmapsmith update`.\n' +
182
+ 'To intentionally create a managed section, run `roadmapsmith generate --dry-run` then `roadmapsmith generate`.'
183
+ );
184
+ }
185
+
143
186
  function runInitCommand(projectRoot, config, flags) {
144
187
  const roadmapFile = resolveRoadmapFile(projectRoot, config, flags['roadmap-file']);
145
188
  const agentsFile = resolveAgentsFile(projectRoot, config, flags['agents-file']);
@@ -187,6 +230,13 @@ function runGenerateCommand(projectRoot, config, flags, options = {}) {
187
230
  forceFullRegenerate: options.forceFullRegenerate === true || isEnabled(flags['full-regen'])
188
231
  });
189
232
 
233
+ emitPreWriteWarning(roadmapFile, {
234
+ commandName: options.commandName || 'roadmapsmith generate',
235
+ dryRun,
236
+ suppressWarning: options.suppressPreWriteWarning,
237
+ warningState: options.warningState,
238
+ managedSectionNote: 'This command may create or replace a managed roadmap section depending on the current file state and flags.'
239
+ });
190
240
  const writeResult = writeText(roadmapFile, document, { dryRun });
191
241
  if (dryRun) {
192
242
  if (writeResult.changed) {
@@ -229,6 +279,12 @@ function runSyncCommand(projectRoot, config, flags, options = {}) {
229
279
  const forceRefresh = isEnabled(flags['refresh-annotations']);
230
280
  const next = applySync(content, syncTasks, results, { forceRefresh });
231
281
  const dryRun = isEnabled(flags['dry-run']);
282
+ emitPreWriteWarning(roadmapFile, {
283
+ commandName: options.commandName || 'roadmapsmith sync',
284
+ dryRun,
285
+ suppressWarning: options.suppressPreWriteWarning,
286
+ warningState: options.warningState
287
+ });
232
288
  const writeResult = writeText(roadmapFile, next, { dryRun });
233
289
 
234
290
  if (dryRun) {
@@ -266,7 +322,7 @@ function runUpdateCommand(projectRoot, config, flags) {
266
322
  const hasTask = flags.task != null;
267
323
  const hasEvidence = flags.evidence != null;
268
324
  if (!hasTask && !hasEvidence) {
269
- runSyncCommand(projectRoot, config, flags);
325
+ runSyncCommand(projectRoot, config, flags, { commandName: 'roadmapsmith update' });
270
326
  return;
271
327
  }
272
328
  if (!hasTask || !hasEvidence || Array.isArray(flags.task) || Array.isArray(flags.evidence)) {
@@ -306,6 +362,10 @@ function runUpdateCommand(projectRoot, config, flags) {
306
362
 
307
363
  const next = applySync(draft, [draftTask], { [taskId]: result });
308
364
  const dryRun = isEnabled(flags['dry-run']);
365
+ emitPreWriteWarning(roadmapFile, {
366
+ commandName: 'roadmapsmith update',
367
+ dryRun
368
+ });
309
369
  const writeResult = writeText(roadmapFile, next, { dryRun });
310
370
  if (dryRun) {
311
371
  if (writeResult.changed) {
@@ -371,40 +431,69 @@ function runStatusCommand(projectRoot, config, flags, options = {}) {
371
431
  async function runZeroCommand(projectRoot, flags) {
372
432
  const configPath = resolveConfigPath({ projectRoot, configPath: flags.config });
373
433
  const config = loadConfig({ projectRoot, configPath: flags.config });
374
- if (!isInteractiveTerminal(process.stdin, process.stdout)) {
375
- throw new Error('Zero Mode requires an interactive terminal. Run roadmapsmith zero from a terminal session, or add a config/brief workflow before retrying in non-interactive mode.');
434
+ const roadmapFile = resolveRoadmapFile(projectRoot, config, flags['roadmap-file']);
435
+ const roadmapExistedBeforeInit = fs.existsSync(roadmapFile);
436
+ const defaults = resolveZeroModeAnswers(projectRoot, config, flags);
437
+ const interactive = isInteractiveTerminal(process.stdin, process.stdout);
438
+ let answers = defaults;
439
+
440
+ if (!interactive) {
441
+ const missingFields = getMissingZeroModeFields(defaults);
442
+ if (missingFields.length > 0) {
443
+ throw new Error(
444
+ 'Zero Mode requires a complete brief in non-interactive environments. Missing: ' +
445
+ `${formatMissingZeroModeFields(missingFields).join(', ')}. ` +
446
+ 'Provide the missing values with CLI flags or in roadmap-skill.config.json before retrying.'
447
+ );
448
+ }
449
+ console.log('RoadmapSmith Zero Mode');
450
+ console.log('Using config/flag discovery inputs in non-interactive mode.\n');
451
+ } else {
452
+ const rl = readline.createInterface({
453
+ input: process.stdin,
454
+ output: process.stdout
455
+ });
456
+
457
+ try {
458
+ console.log('RoadmapSmith Zero Mode');
459
+ console.log('Answer the discovery interview to generate the first roadmap.\n');
460
+ answers = await collectZeroModeAnswers((prompt) => rl.question(prompt), defaults);
461
+ } finally {
462
+ rl.close();
463
+ }
376
464
  }
377
465
 
378
- const defaults = buildZeroModeDefaults(projectRoot, config);
379
- const rl = readline.createInterface({
380
- input: process.stdin,
381
- output: process.stdout
466
+ const existingUserConfig = readUserConfig({ projectRoot, configPath: flags.config });
467
+ const nextUserConfig = buildZeroModeConfigPatch(projectRoot, existingUserConfig, answers);
468
+ writeText(configPath, JSON.stringify(nextUserConfig, null, 2));
469
+ console.log(`Updated ${configPath}`);
470
+ const nextConfig = loadConfig({ projectRoot, configPath: flags.config });
471
+ runInitCommand(projectRoot, nextConfig, flags);
472
+ runGenerateCommand(projectRoot, nextConfig, flags, {
473
+ commandName: 'roadmapsmith zero',
474
+ suppressPreWriteWarning: true,
475
+ forceFullRegenerate: !roadmapExistedBeforeInit
382
476
  });
383
-
384
- try {
385
- console.log('RoadmapSmith Zero Mode');
386
- console.log('Answer the discovery interview to generate the first roadmap.\n');
387
- const answers = await collectZeroModeAnswers((prompt) => rl.question(prompt), defaults);
388
- const existingUserConfig = readUserConfig({ projectRoot, configPath: flags.config });
389
- const nextUserConfig = buildZeroModeConfigPatch(projectRoot, existingUserConfig, answers);
390
- writeText(configPath, JSON.stringify(nextUserConfig, null, 2));
391
- console.log(`Updated ${configPath}`);
392
- const nextConfig = loadConfig({ projectRoot, configPath: flags.config });
393
- runInitCommand(projectRoot, nextConfig, flags);
394
- runGenerateCommand(projectRoot, nextConfig, flags);
395
- } finally {
396
- rl.close();
397
- }
398
477
  }
399
478
 
400
479
  function runMaintainCommand(projectRoot, flags) {
401
480
  const config = loadConfig({ projectRoot, configPath: flags.config });
481
+ const roadmapFile = resolveRoadmapFile(projectRoot, config, flags['roadmap-file']);
482
+ const existingContent = readTextIfExists(roadmapFile);
483
+ assertMaintainCanWrite(roadmapFile, existingContent);
402
484
  const fullRegen = isEnabled(flags['full-regen']);
485
+ const warningState = { emitted: false };
403
486
  runGenerateCommand(projectRoot, config, flags, {
487
+ commandName: 'roadmapsmith maintain',
404
488
  preserveManagedBlock: !fullRegen,
405
- forceFullRegenerate: fullRegen
489
+ forceFullRegenerate: fullRegen,
490
+ warningState
491
+ });
492
+ runSyncCommand(projectRoot, config, { ...flags, audit: true }, {
493
+ audit: true,
494
+ commandName: 'roadmapsmith maintain',
495
+ warningState
406
496
  });
407
- runSyncCommand(projectRoot, config, { ...flags, audit: true }, { audit: true });
408
497
  }
409
498
 
410
499
  async function run() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roadmapsmith",
3
- "version": "0.9.33",
3
+ "version": "0.9.34",
4
4
  "description": "Evidence-backed ROADMAP.md workflows for AI coding agents, with canonical RoadmapSmith status and maintain surfaces plus advanced sync/generate tools and legacy compatibility aliases.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -9,7 +9,8 @@ Use this command when the repository already has code, tests, docs, or an existi
9
9
 
10
10
  ## Required behavior
11
11
 
12
- 1. Run `roadmapsmith maintain --project-root .`.
12
+ 1. Run `roadmapsmith maintain --project-root .`. Prefer `--dry-run` first when previewing impact.
13
13
  2. Treat this command as CLI-backed. Do not silently replace it with manual reasoning when the CLI is unavailable.
14
- 3. Mention that maintain runs preserve-first generate, sync, and audit in one invocation.
15
- 4. After a successful maintain cycle, do not propose generate, sync, or audit separately unless the user needs manual control or inspection.
14
+ 3. Mention that maintain runs preserve-first generate, sync, and audit in one invocation, but only for an existing managed roadmap block.
15
+ 4. If the roadmap is non-empty and lacks `<!-- rs:managed:* -->`, direct the user to `roadmapsmith update` for conservative inline annotations or `roadmapsmith generate` for explicit managed-section creation.
16
+ 5. After a successful maintain cycle, do not propose generate, sync, or audit separately unless the user needs manual control or inspection.
@@ -2,6 +2,6 @@
2
2
  "interface": {
3
3
  "display_name": "Roadmap Sync (Deprecated)",
4
4
  "short_description": "DEPRECATED legacy root; use /roadmap-maintain or /roadmap-update.",
5
- "default_prompt": "Prefer /roadmap, /roadmap-status, /roadmap-maintain, and /roadmap-update."
5
+ "default_prompt": "Prefer /roadmap, /roadmap-status, /roadmap-maintain, and /roadmap-update; use /roadmap-update when markers are missing."
6
6
  }
7
7
  }
@@ -10,4 +10,5 @@ Use this command when the repository is empty or low-context and the user needs
10
10
  ## Required behavior
11
11
 
12
12
  1. Run `roadmapsmith zero --project-root .`.
13
- 2. If the CLI is missing, explain the install path instead of improvising the workflow manually.
13
+ 2. In non-interactive environments, provide or reuse a complete brief from config plus flags such as `--primary-user`, `--problem-statement`, `--target-outcome`, and repeatable `--done-criterion`.
14
+ 3. If the CLI is missing, explain the install path instead of improvising the workflow manually.
package/skills.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "npx skills add PapiScholz/roadmapsmith --skill '*' -a claude-code",
13
13
  "roadmapsmith setup",
14
14
  "roadmapsmith zero",
15
- "roadmapsmith maintain",
15
+ "roadmapsmith maintain --dry-run",
16
16
  "roadmapsmith /roadmap",
17
17
  "roadmapsmith update --task p2-customer-history --evidence \"src/app/api/customers/route.ts, test/customers.test.js\"",
18
18
  "roadmapsmith validate --json"
@@ -21,74 +21,74 @@
21
21
  "command": "npx skills add PapiScholz/roadmapsmith --skill '*' -a claude-code",
22
22
  "source": "PapiScholz/roadmapsmith",
23
23
  "skill": "*",
24
- "notes": "Recommended Claude Code install path for native GUI slash commands. Canonical surfaces: /roadmap, /roadmap-zero, /roadmap-maintain, /roadmap-status, /roadmap-validate, /roadmap-update, /roadmap-setup. The public CLI update family covers both checklist refresh and verified single-task completion; sync remains the advanced alias for the mutating refresh path. Advanced surfaces: /roadmap-init, /roadmap-generate, /roadmap-audit. DEPRECATED: /roadmap-sync remains only as a compatibility root for existing automation; use /roadmap-maintain or /roadmap-update for new workflows. Install the roadmapsmith CLI separately for actual command execution; roadmapsmith status is the public readiness command and roadmapsmith doctor remains a compatibility alias. Then run /reload-skills and, if applicable, /reload-plugins. Codex native plugin installs use the repo/package .codex-plugin surface instead of this Claude-specific skills CLI path."
24
+ "notes": "Recommended Claude Code install path for native GUI slash commands. Canonical surfaces: /roadmap, /roadmap-zero, /roadmap-maintain, /roadmap-status, /roadmap-validate, /roadmap-update, /roadmap-setup. The public CLI update family covers both checklist refresh and verified single-task completion; sync remains the advanced alias for the mutating refresh path. /roadmap-maintain now owns only existing managed roadmap blocks, while /roadmap-update is the conservative annotation path for authored roadmaps without markers. /roadmap-zero can consume a complete discovery brief from config plus CLI flags in non-interactive environments. Advanced surfaces: /roadmap-init, /roadmap-generate, /roadmap-audit. DEPRECATED: /roadmap-sync remains only as a compatibility root for existing automation; use /roadmap-maintain or /roadmap-update for new workflows. Install the roadmapsmith CLI separately for actual command execution; roadmapsmith status is the public readiness command and roadmapsmith doctor remains a compatibility alias. Then run /reload-skills and, if applicable, /reload-plugins. Codex native plugin installs use the repo/package .codex-plugin surface instead of this Claude-specific skills CLI path."
25
25
  },
26
26
  "skills": [
27
27
  {
28
28
  "name": "roadmap",
29
29
  "path": "skills/roadmap",
30
30
  "description": "Native slash palette for RoadmapSmith commands and recommended entrypoints across supported hosts.",
31
- "version": "0.9.33"
31
+ "version": "0.9.34"
32
32
  },
33
33
  {
34
34
  "name": "roadmap-zero",
35
35
  "path": "skills/roadmap-zero",
36
- "description": "Native slash entrypoint for the one-command Zero Mode CLI workflow.",
37
- "version": "0.9.33"
36
+ "description": "Native slash entrypoint for Zero Mode, including non-interactive config-plus-flag discovery.",
37
+ "version": "0.9.34"
38
38
  },
39
39
  {
40
40
  "name": "roadmap-maintain",
41
41
  "path": "skills/roadmap-maintain",
42
- "description": "Native slash entrypoint for the preserve-first generate + sync + audit flow.",
43
- "version": "0.9.33"
42
+ "description": "Native slash entrypoint for conservative managed-block maintenance plus sync and audit output.",
43
+ "version": "0.9.34"
44
44
  },
45
45
  {
46
46
  "name": "roadmap-status",
47
47
  "path": "skills/roadmap-status",
48
48
  "description": "Native slash readiness check grounded in roadmapsmith status JSON.",
49
- "version": "0.9.33"
49
+ "version": "0.9.34"
50
50
  },
51
51
  {
52
52
  "name": "roadmap-init",
53
53
  "path": "skills/roadmap-init",
54
54
  "description": "Native slash entrypoint for creating ROADMAP.md and AGENTS.md.",
55
- "version": "0.9.33"
55
+ "version": "0.9.34"
56
56
  },
57
57
  {
58
58
  "name": "roadmap-generate",
59
59
  "path": "skills/roadmap-generate",
60
60
  "description": "Native slash entrypoint for managed roadmap updates that require --full-regen before destructive replacement.",
61
- "version": "0.9.33"
61
+ "version": "0.9.34"
62
62
  },
63
63
  {
64
64
  "name": "roadmap-validate",
65
65
  "path": "skills/roadmap-validate",
66
66
  "description": "Native slash entrypoint for evidence-backed roadmap validation.",
67
- "version": "0.9.33"
67
+ "version": "0.9.34"
68
68
  },
69
69
  {
70
70
  "name": "roadmap-update",
71
71
  "path": "skills/roadmap-update",
72
- "description": "Native slash entrypoint for evidence-backed sync and verified single-task completion.",
73
- "version": "0.9.33"
72
+ "description": "Native slash entrypoint for evidence-backed inline annotation refresh and verified single-task completion.",
73
+ "version": "0.9.34"
74
74
  },
75
75
  {
76
76
  "name": "roadmap-sync",
77
77
  "path": "skills/roadmap-sync",
78
78
  "description": "DEPRECATED legacy compatibility root; use roadmap-maintain or roadmap-update.",
79
- "version": "0.9.33"
79
+ "version": "0.9.34"
80
80
  },
81
81
  {
82
82
  "name": "roadmap-audit",
83
83
  "path": "skills/roadmap-audit",
84
84
  "description": "Native slash entrypoint for the advanced sync-plus-audit mutating summary workflow.",
85
- "version": "0.9.33"
85
+ "version": "0.9.34"
86
86
  },
87
87
  {
88
88
  "name": "roadmap-setup",
89
89
  "path": "skills/roadmap-setup",
90
90
  "description": "Native slash entrypoint for generating RoadmapSmith host integration files.",
91
- "version": "0.9.33"
91
+ "version": "0.9.34"
92
92
  }
93
93
  ]
94
94
  }
package/src/zero.js CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  const path = require('path');
4
4
 
5
+ const ZERO_MODE_FLAG_SPECS = {
6
+ productName: { flag: '--product-name', configPath: 'product.name', type: 'scalar' },
7
+ primaryUser: { flag: '--primary-user', configPath: 'product.primaryUser', type: 'scalar', required: true },
8
+ problemStatement: { flag: '--problem-statement', configPath: 'zeroMode.problemStatement', type: 'scalar', required: true },
9
+ targetOutcome: { flag: '--target-outcome', configPath: 'product.targetOutcome', type: 'scalar', required: true },
10
+ antiGoals: { flag: '--anti-goal', configPath: 'product.antiGoals', type: 'list' },
11
+ preferredStack: { flag: '--preferred-stack', configPath: 'zeroMode.preferredStack', type: 'scalar' },
12
+ constraints: { flag: '--constraint', configPath: 'zeroMode.constraints', type: 'list' },
13
+ doneCriteria: { flag: '--done-criterion', configPath: 'zeroMode.doneCriteria', type: 'list', required: true }
14
+ };
15
+
5
16
  const ZERO_MODE_QUESTIONS = [
6
17
  { id: 'productName', prompt: '1. What product are we building?' },
7
18
  { id: 'primaryUser', prompt: '2. Who is the target user?' },
@@ -54,6 +65,80 @@ function buildZeroModeDefaults(projectRoot, config) {
54
65
  };
55
66
  }
56
67
 
68
+ function getSingleFlagValue(value) {
69
+ if (Array.isArray(value)) {
70
+ return value[value.length - 1];
71
+ }
72
+ return value;
73
+ }
74
+
75
+ function getListFlagValues(value) {
76
+ const rawValues = Array.isArray(value) ? value : [value];
77
+ return rawValues.flatMap((item) => splitListAnswer(item));
78
+ }
79
+
80
+ function resolveZeroModeAnswers(projectRoot, config, flags = {}) {
81
+ const defaults = buildZeroModeDefaults(projectRoot, config);
82
+ const answers = { ...defaults };
83
+
84
+ for (const [field, spec] of Object.entries(ZERO_MODE_FLAG_SPECS)) {
85
+ const flagKey = spec.flag.slice(2);
86
+ if (!Object.prototype.hasOwnProperty.call(flags, flagKey)) {
87
+ continue;
88
+ }
89
+
90
+ if (spec.type === 'list') {
91
+ answers[field] = getListFlagValues(flags[flagKey]).join('; ');
92
+ continue;
93
+ }
94
+
95
+ const raw = getSingleFlagValue(flags[flagKey]);
96
+ answers[field] = String(raw == null ? '' : raw).trim();
97
+ }
98
+
99
+ if (!String(answers.productName || '').trim()) {
100
+ answers.productName = path.basename(projectRoot);
101
+ }
102
+
103
+ return answers;
104
+ }
105
+
106
+ function getMissingZeroModeFields(answers = {}) {
107
+ const missing = [];
108
+ for (const [field, spec] of Object.entries(ZERO_MODE_FLAG_SPECS)) {
109
+ if (!spec.required) {
110
+ continue;
111
+ }
112
+
113
+ if (spec.type === 'list') {
114
+ if (splitListAnswer(answers[field]).length === 0) {
115
+ missing.push({
116
+ field,
117
+ flag: spec.flag,
118
+ configPath: spec.configPath
119
+ });
120
+ }
121
+ continue;
122
+ }
123
+
124
+ if (!String(answers[field] || '').trim()) {
125
+ missing.push({
126
+ field,
127
+ flag: spec.flag,
128
+ configPath: spec.configPath
129
+ });
130
+ }
131
+ }
132
+ return missing;
133
+ }
134
+
135
+ function formatMissingZeroModeFields(missingFields) {
136
+ return missingFields.map((item) => {
137
+ const label = item.field.replace(/([A-Z])/g, ' $1').toLowerCase();
138
+ return `${label} (${item.flag} or config ${item.configPath})`;
139
+ });
140
+ }
141
+
57
142
  async function collectZeroModeAnswers(ask, defaults = {}) {
58
143
  const answers = {};
59
144
  for (const question of ZERO_MODE_QUESTIONS) {
@@ -124,6 +209,9 @@ module.exports = {
124
209
  buildZeroModeConfigPatch,
125
210
  buildZeroModeDefaults,
126
211
  collectZeroModeAnswers,
212
+ formatMissingZeroModeFields,
213
+ getMissingZeroModeFields,
127
214
  isInteractiveTerminal,
215
+ resolveZeroModeAnswers,
128
216
  splitListAnswer
129
217
  };