nubos-pilot 0.4.0 → 0.5.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 (190) hide show
  1. package/README.md +149 -0
  2. package/agents/np-executor.md +10 -5
  3. package/agents/np-nyquist-auditor.md +17 -17
  4. package/agents/np-plan-checker.md +39 -29
  5. package/agents/np-planner.md +83 -6
  6. package/agents/np-verifier.md +19 -15
  7. package/bin/install.js +33 -52
  8. package/bin/np-tools/_commands.cjs +23 -39
  9. package/bin/np-tools/add-tests.cjs +34 -37
  10. package/bin/np-tools/add-tests.test.cjs +34 -28
  11. package/bin/np-tools/add-todo.cjs +2 -2
  12. package/bin/np-tools/checkpoint.test.cjs +17 -17
  13. package/bin/np-tools/commit-task.cjs +14 -33
  14. package/bin/np-tools/commit-task.test.cjs +19 -19
  15. package/bin/np-tools/discuss-phase.cjs +28 -41
  16. package/bin/np-tools/discuss-phase.test.cjs +37 -53
  17. package/bin/np-tools/doctor.cjs +63 -0
  18. package/bin/np-tools/execute-milestone.cjs +225 -0
  19. package/bin/np-tools/execute-milestone.test.cjs +154 -0
  20. package/bin/np-tools/help.test.cjs +4 -6
  21. package/bin/np-tools/init-dispatch.test.cjs +27 -41
  22. package/bin/np-tools/new-milestone.cjs +121 -121
  23. package/bin/np-tools/new-milestone.test.cjs +56 -49
  24. package/bin/np-tools/new-project.cjs +97 -95
  25. package/bin/np-tools/new-project.test.cjs +49 -41
  26. package/bin/np-tools/park.cjs +4 -30
  27. package/bin/np-tools/park.test.cjs +10 -9
  28. package/bin/np-tools/pause-work.test.cjs +4 -4
  29. package/bin/np-tools/plan-milestone.cjs +381 -0
  30. package/bin/np-tools/plan-milestone.test.cjs +209 -0
  31. package/bin/np-tools/research-phase.cjs +36 -53
  32. package/bin/np-tools/research-phase.test.cjs +31 -40
  33. package/bin/np-tools/reset-slice.cjs +93 -5
  34. package/bin/np-tools/reset-slice.test.cjs +89 -37
  35. package/bin/np-tools/resume-work.test.cjs +7 -7
  36. package/bin/np-tools/skip.cjs +4 -30
  37. package/bin/np-tools/skip.test.cjs +12 -12
  38. package/bin/np-tools/slug.cjs +2 -2
  39. package/bin/np-tools/undo-task.cjs +33 -6
  40. package/bin/np-tools/undo-task.test.cjs +63 -74
  41. package/bin/np-tools/undo.cjs +55 -28
  42. package/bin/np-tools/undo.test.cjs +81 -68
  43. package/bin/np-tools/unpark.cjs +4 -30
  44. package/bin/np-tools/unpark.test.cjs +10 -9
  45. package/bin/np-tools/verify-work.cjs +67 -42
  46. package/bin/np-tools/verify-work.test.cjs +46 -30
  47. package/lib/agents.test.cjs +22 -53
  48. package/lib/checkpoint.test.cjs +35 -35
  49. package/lib/fixtures/plans/cycle/tasks/{T-01.md → T0001/T0001-PLAN.md} +4 -4
  50. package/lib/fixtures/plans/cycle/tasks/{T-02.md → T0002/T0002-PLAN.md} +4 -4
  51. package/lib/fixtures/plans/cycle/tasks/{T-03.md → T0003/T0003-PLAN.md} +4 -4
  52. package/lib/fixtures/plans/{parallel/tasks/T-01.md → linear/tasks/T0001/T0001-PLAN.md} +3 -3
  53. package/lib/fixtures/plans/linear/tasks/{T-02.md → T0002/T0002-PLAN.md} +4 -4
  54. package/lib/fixtures/plans/linear/tasks/{T-03.md → T0003/T0003-PLAN.md} +4 -4
  55. package/lib/fixtures/plans/{linear/tasks/T-01.md → parallel/tasks/T0001/T0001-PLAN.md} +3 -3
  56. package/lib/fixtures/plans/parallel/tasks/{T-02.md → T0002/T0002-PLAN.md} +4 -4
  57. package/lib/fixtures/plans/parallel/tasks/{T-03.md → T0003/T0003-PLAN.md} +4 -4
  58. package/lib/fixtures/plans/wave-conflict/tasks/{T-01.md → T0001/T0001-PLAN.md} +3 -3
  59. package/lib/fixtures/plans/wave-conflict/tasks/{T-02.md → T0002/T0002-PLAN.md} +4 -4
  60. package/lib/git.test.cjs +21 -21
  61. package/lib/layout.cjs +266 -0
  62. package/lib/layout.test.cjs +140 -0
  63. package/lib/model-profiles.cjs +4 -4
  64. package/lib/model-profiles.test.cjs +9 -6
  65. package/lib/roadmap.cjs +38 -3
  66. package/lib/tasks.cjs +26 -20
  67. package/lib/tasks.test.cjs +45 -40
  68. package/lib/verify.cjs +36 -39
  69. package/lib/verify.test.cjs +47 -46
  70. package/np-tools.cjs +22 -170
  71. package/package.json +1 -1
  72. package/templates/milestone/CONTEXT.md +28 -0
  73. package/templates/milestone/META.json +11 -0
  74. package/templates/milestone/ROADMAP.md +11 -0
  75. package/templates/slice/ASSESSMENT.md +24 -0
  76. package/templates/slice/PLAN.md +43 -0
  77. package/templates/slice/RESEARCH.md +20 -0
  78. package/templates/slice/SUMMARY.md +17 -0
  79. package/templates/slice/UAT.md +21 -0
  80. package/templates/task/PLAN.md +48 -0
  81. package/templates/task/SUMMARY.md +24 -0
  82. package/workflows/add-tests.md +1 -0
  83. package/workflows/add-todo.md +6 -5
  84. package/workflows/discuss-phase.md +44 -34
  85. package/workflows/discuss-project.md +1 -0
  86. package/workflows/doctor.md +6 -0
  87. package/workflows/execute-phase.md +89 -75
  88. package/workflows/help.md +6 -0
  89. package/workflows/new-milestone.md +30 -51
  90. package/workflows/new-project.md +12 -7
  91. package/workflows/note.md +4 -3
  92. package/workflows/park.md +1 -0
  93. package/workflows/plan-phase.md +140 -200
  94. package/workflows/research-phase.md +18 -17
  95. package/workflows/reset-slice.md +75 -27
  96. package/workflows/resume-work.md +2 -2
  97. package/workflows/scan-codebase.md +1 -0
  98. package/workflows/session-report.md +1 -0
  99. package/workflows/skip.md +1 -0
  100. package/workflows/stats.md +1 -0
  101. package/workflows/thread.md +4 -3
  102. package/workflows/undo-task.md +54 -27
  103. package/workflows/undo.md +75 -38
  104. package/workflows/unpark.md +1 -0
  105. package/workflows/update-docs.md +1 -0
  106. package/workflows/validate-phase.md +52 -103
  107. package/workflows/verify-work.md +16 -20
  108. package/agents/np-ai-researcher.md +0 -140
  109. package/agents/np-code-fixer.md +0 -381
  110. package/agents/np-code-reviewer.md +0 -352
  111. package/agents/np-domain-researcher.md +0 -136
  112. package/agents/np-eval-auditor.md +0 -167
  113. package/agents/np-eval-planner.md +0 -153
  114. package/agents/np-framework-selector.md +0 -171
  115. package/agents/np-security-auditor.md +0 -206
  116. package/agents/np-ui-auditor.md +0 -369
  117. package/agents/np-ui-checker.md +0 -192
  118. package/agents/np-ui-researcher.md +0 -324
  119. package/bin/np-tools/ai-integration-phase.cjs +0 -109
  120. package/bin/np-tools/ai-integration-phase.test.cjs +0 -123
  121. package/bin/np-tools/autonomous.cjs +0 -69
  122. package/bin/np-tools/autonomous.test.cjs +0 -74
  123. package/bin/np-tools/code-review.cjs +0 -133
  124. package/bin/np-tools/code-review.test.cjs +0 -96
  125. package/bin/np-tools/discuss-phase-power.cjs +0 -265
  126. package/bin/np-tools/discuss-phase-power.test.cjs +0 -242
  127. package/bin/np-tools/dispatch.cjs +0 -116
  128. package/bin/np-tools/eval-review.cjs +0 -116
  129. package/bin/np-tools/eval-review.test.cjs +0 -123
  130. package/bin/np-tools/execute-phase.cjs +0 -182
  131. package/bin/np-tools/execute-phase.test.cjs +0 -116
  132. package/bin/np-tools/execute-plan.cjs +0 -124
  133. package/bin/np-tools/execute-plan.test.cjs +0 -82
  134. package/bin/np-tools/next.cjs +0 -7
  135. package/bin/np-tools/next.test.cjs +0 -30
  136. package/bin/np-tools/phase.cjs +0 -71
  137. package/bin/np-tools/phase.test.cjs +0 -81
  138. package/bin/np-tools/plan-diff.cjs +0 -57
  139. package/bin/np-tools/plan-diff.test.cjs +0 -134
  140. package/bin/np-tools/plan-milestone-gaps.cjs +0 -115
  141. package/bin/np-tools/plan-milestone-gaps.test.cjs +0 -122
  142. package/bin/np-tools/plan-phase.cjs +0 -350
  143. package/bin/np-tools/plan-phase.test.cjs +0 -263
  144. package/bin/np-tools/progress.cjs +0 -7
  145. package/bin/np-tools/progress.test.cjs +0 -44
  146. package/bin/np-tools/queue.cjs +0 -213
  147. package/bin/np-tools/triage.cjs +0 -128
  148. package/bin/np-tools/ui-phase.cjs +0 -108
  149. package/bin/np-tools/ui-phase.test.cjs +0 -121
  150. package/bin/np-tools/ui-review.cjs +0 -108
  151. package/bin/np-tools/ui-review.test.cjs +0 -120
  152. package/lib/gaps.cjs +0 -197
  153. package/lib/gaps.test.cjs +0 -200
  154. package/lib/next.cjs +0 -236
  155. package/lib/next.test.cjs +0 -194
  156. package/lib/phase.cjs +0 -95
  157. package/lib/phase.test.cjs +0 -189
  158. package/lib/plan-diff.cjs +0 -173
  159. package/lib/plan-diff.test.cjs +0 -217
  160. package/lib/plan.cjs +0 -85
  161. package/lib/plan.test.cjs +0 -263
  162. package/lib/progress.cjs +0 -95
  163. package/lib/progress.test.cjs +0 -116
  164. package/lib/undo.cjs +0 -179
  165. package/lib/undo.test.cjs +0 -261
  166. package/templates/AI-SPEC.md +0 -90
  167. package/templates/CONTEXT.md +0 -32
  168. package/templates/PLAN.md +0 -69
  169. package/templates/SECURITY.md +0 -61
  170. package/templates/UI-SPEC.md +0 -64
  171. package/workflows/add-backlog.md +0 -212
  172. package/workflows/ai-integration-phase.md +0 -230
  173. package/workflows/autonomous.md +0 -94
  174. package/workflows/cleanup.md +0 -325
  175. package/workflows/code-review-fix.md +0 -435
  176. package/workflows/code-review.md +0 -447
  177. package/workflows/discuss-phase-assumptions.md +0 -269
  178. package/workflows/discuss-phase-power.md +0 -139
  179. package/workflows/dispatch.md +0 -9
  180. package/workflows/eval-review.md +0 -243
  181. package/workflows/execute-plan.md +0 -82
  182. package/workflows/next.md +0 -8
  183. package/workflows/plan-milestone-gaps.md +0 -233
  184. package/workflows/progress.md +0 -8
  185. package/workflows/queue.md +0 -9
  186. package/workflows/review.md +0 -489
  187. package/workflows/secure-phase.md +0 -209
  188. package/workflows/triage.md +0 -9
  189. package/workflows/ui-phase.md +0 -246
  190. package/workflows/ui-review.md +0 -222
package/bin/install.js CHANGED
@@ -222,19 +222,18 @@ async function _runInitQuestions(detectedRuntime, askUser, flags) {
222
222
  const scope = f.scope || (await askUser({ type: 'select', question: 'Installation scope?',
223
223
  options: VALID_SCOPES, default: 'local' })).value;
224
224
  const model_profile = (await askUser({ type: 'select', question: 'Model-Profile?',
225
- options: ['inherit', 'quality', 'balanced', 'budget'], default: 'inherit' })).value;
226
- const commit_docs = (await askUser({ type: 'confirm', question: 'Commit documentation artefacts?', default: true })).value;
227
- const branching_strategy = (await askUser({ type: 'select', question: 'Branching strategy?',
228
- options: ['single-branch', 'phase-branches', 'milestone-branches'], default: 'single-branch' })).value;
229
- const phase_branch_template = (await askUser({ type: 'input', question: 'Phase branch template?', default: 'phase/{number}-{slug}' })).value;
230
- const milestone_branch_template = (await askUser({ type: 'input', question: 'Milestone branch template?', default: 'milestone/{name}' })).value;
231
- const parallelization = (await askUser({ type: 'confirm', question: 'Enable parallelization?', default: true })).value;
232
- const research = (await askUser({ type: 'confirm', question: 'Enable research step?', default: false })).value;
233
- const plan_checker = (await askUser({ type: 'confirm', question: 'Enable plan_checker?', default: true })).value;
234
- const verifier = (await askUser({ type: 'confirm', question: 'Enable verifier?', default: true })).value;
225
+ options: ['frontier', 'quality', 'balanced', 'budget', 'inherit'], default: 'frontier' })).value;
235
226
  const response_language = (await askUser({ type: 'input', question: 'Response language (ISO-639 code)?', default: 'en' })).value;
236
- return { runtime, runtimes, scope, mcp: !!f.mcp, model_profile, commit_docs, branching_strategy, phase_branch_template,
237
- milestone_branch_template, parallelization, research, plan_checker, verifier, response_language };
227
+ return {
228
+ runtime, runtimes, scope, mcp: !!f.mcp,
229
+ model_profile,
230
+ response_language,
231
+ commit_docs: true,
232
+ parallelization: true,
233
+ research: true,
234
+ plan_checker: true,
235
+ verifier: true,
236
+ };
238
237
  }
239
238
 
240
239
  function _repairCodexConfig() {
@@ -251,56 +250,38 @@ function _repairCodexConfig() {
251
250
 
252
251
  const LEGACY_AGENTS = new Set(['claude', 'codex', 'gemini', 'opencode']);
253
252
 
253
+ const DEFAULT_CLAUDE_MD = '# CLAUDE.md\n\n'
254
+ + 'Project guidance for Claude Code. Add project-specific instructions above the'
255
+ + ' managed block — `npx nubos-pilot` only rewrites the block between the markers.\n';
256
+
254
257
  function _rewriteManagedMarkdown(projectRoot, runtimes) {
255
258
  const claudePath = path.join(projectRoot, 'CLAUDE.md');
256
259
  const agentsPath = path.join(projectRoot, 'AGENTS.md');
257
260
  const geminiPath = path.join(projectRoot, 'GEMINI.md');
258
- const claudeExists = fs.existsSync(claudePath);
259
- if (claudeExists) {
260
- const content = fs.readFileSync(claudePath, 'utf-8');
261
- const next = managedBlockMod.rewriteBlock(content, MANAGED_BLOCK_INNER);
262
- atomicWriteFileSync(claudePath, next);
263
- }
264
-
265
- let agentsBase;
266
- if (fs.existsSync(agentsPath)) {
267
- agentsBase = fs.readFileSync(agentsPath, 'utf-8');
268
- } else if (claudeExists) {
269
- agentsBase = agentsMdMod.generateAgentsMd(fs.readFileSync(claudePath, 'utf-8'), 'codex');
270
- } else {
271
- agentsBase = null;
272
- }
273
- if (agentsBase !== null) {
274
- const agentsNext = managedBlockMod.rewriteBlock(agentsBase, MANAGED_BLOCK_INNER);
275
- atomicWriteFileSync(agentsPath, agentsNext);
276
- }
277
-
278
- let geminiBase;
279
- if (fs.existsSync(geminiPath)) {
280
- geminiBase = fs.readFileSync(geminiPath, 'utf-8');
281
- } else if (claudeExists) {
282
- geminiBase = agentsMdMod.generateAgentsMd(fs.readFileSync(claudePath, 'utf-8'), 'gemini');
283
- } else {
284
- geminiBase = null;
285
- }
286
- if (geminiBase !== null) {
287
- const geminiNext = managedBlockMod.rewriteBlock(geminiBase, MANAGED_BLOCK_INNER);
288
- atomicWriteFileSync(geminiPath, geminiNext);
289
- }
261
+ const claudeBase = fs.existsSync(claudePath)
262
+ ? fs.readFileSync(claudePath, 'utf-8')
263
+ : DEFAULT_CLAUDE_MD;
264
+ const claudeNext = managedBlockMod.rewriteBlock(claudeBase, MANAGED_BLOCK_INNER);
265
+ atomicWriteFileSync(claudePath, claudeNext);
266
+
267
+ const agentsBase = fs.existsSync(agentsPath)
268
+ ? fs.readFileSync(agentsPath, 'utf-8')
269
+ : agentsMdMod.generateAgentsMd(claudeNext, 'codex');
270
+ atomicWriteFileSync(agentsPath, managedBlockMod.rewriteBlock(agentsBase, MANAGED_BLOCK_INNER));
271
+
272
+ const geminiBase = fs.existsSync(geminiPath)
273
+ ? fs.readFileSync(geminiPath, 'utf-8')
274
+ : agentsMdMod.generateAgentsMd(claudeNext, 'gemini');
275
+ atomicWriteFileSync(geminiPath, managedBlockMod.rewriteBlock(geminiBase, MANAGED_BLOCK_INNER));
290
276
 
291
277
  const extras = (runtimes || []).filter((id) => !LEGACY_AGENTS.has(id));
292
278
  for (const id of extras) {
293
279
  const meta = registryMod.getRuntimeMeta(id);
294
280
  if (!meta) continue;
295
281
  const targetPath = registryMod.runtimeAgentsPath(meta, 'local', projectRoot);
296
- let base;
297
- if (fs.existsSync(targetPath)) {
298
- base = fs.readFileSync(targetPath, 'utf-8');
299
- } else if (claudeExists) {
300
- base = agentsMdMod.generateAgentsMd(fs.readFileSync(claudePath, 'utf-8'), 'codex');
301
- } else {
302
- base = '';
303
- }
282
+ const base = fs.existsSync(targetPath)
283
+ ? fs.readFileSync(targetPath, 'utf-8')
284
+ : agentsMdMod.generateAgentsMd(claudeNext, 'codex');
304
285
  const next = managedBlockMod.rewriteBlock(base, MANAGED_BLOCK_INNER);
305
286
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
306
287
  atomicWriteFileSync(targetPath, next);
@@ -1,70 +1,54 @@
1
1
  const COMMANDS = [
2
- { name: 'next', category: 'Utility', description: 'Print the next actionable step' },
3
- { name: 'progress', category: 'Utility', description: 'Report % complete across phases and plans' },
4
2
  { name: 'state', category: 'Utility', description: 'Print the current project state snapshot' },
5
3
  { name: 'help', category: 'Utility', description: 'List available commands' },
6
4
  { name: 'init', category: 'Utility', description: 'Dispatcher init payload for workflows' },
7
5
 
8
- { name: 'discuss-phase', category: 'Planning', description: 'Adaptive phase-context interview (writes CONTEXT.md)' },
9
- { name: 'discuss-phase-power', category: 'Planning', description: 'Bulk gray-area question file-UI (power mode)' },
10
- { name: 'research-phase', category: 'Planning', description: 'Optional phase-level research (WebFetch + MCP; offline fallback)' },
11
- { name: 'plan-phase', category: 'Planning', description: 'Creates PLAN.md with plan-checker verification loop' },
12
- { name: 'new-project', category: 'Planning', description: 'Greenfield project init (PROJECT.md + REQUIREMENTS.md + roadmap)' },
13
- { name: 'new-milestone', category: 'Planning', description: 'Append milestone + first phase to an existing project' },
14
- { name: 'plan-milestone-gaps', category: 'Planning', description: 'Create corrective phases from audit gaps' },
6
+ { name: 'discuss-project', category: 'Planning', description: 'Adaptive project-context interview (writes PROJECT.md decisions)' },
7
+ { name: 'discuss-phase', category: 'Planning', description: 'Adaptive milestone-context interview (writes M<NNN>-CONTEXT.md)' },
8
+ { name: 'research-phase', category: 'Planning', description: 'Milestone-level research (WebFetch + MCP; offline fallback)' },
9
+ { name: 'plan-milestone', category: 'Planning', description: 'Plan a milestone: scaffolds slices + tasks' },
10
+ { name: 'new-project', category: 'Planning', description: 'Greenfield project init (PROJECT.md + REQUIREMENTS.md + M001 milestone)' },
11
+ { name: 'new-milestone', category: 'Planning', description: 'Append a new milestone (M<NNN>) to an existing project' },
15
12
  { name: 'agent-skills', category: 'Planning', description: 'Print agent_skills config for a given subagent' },
16
13
 
17
- { name: 'execute-phase', category: 'Execution', description: 'Wave-based phase execution (emits per-task executor-spawn payloads)' },
18
- { name: 'execute-plan', category: 'Execution', description: 'Single-plan execution (sub-case of execute-phase)' },
19
- { name: 'commit-task', category: 'Execution', description: 'Atomic per-task git commit via lib/git.cjs (D-03/D-25 enforced)' },
14
+ { name: 'execute-milestone', category: 'Execution', description: 'Wave-based milestone execution slice by slice, tasks parallel within a slice' },
15
+ { name: 'commit-task', category: 'Execution', description: 'Atomic per-task git commit via lib/git.cjs' },
20
16
  { name: 'checkpoint', category: 'Execution', description: 'Per-task crash-safety checkpoint CRUD (start/transition/touch/show)' },
21
- { name: 'autonomous', category: 'Execution', description: 'In-session gate snapshot for auto-advance loop (ADR-0001, no daemon)' },
22
- { name: 'verify-work', category: 'Execution', description: 'Two-pass goal-backward verification (D-21/D-22 VERIFICATION.md render/record)' },
17
+ { name: 'verify-work', category: 'Execution', description: 'Two-pass goal-backward verification (milestone-level VERIFICATION.md)' },
23
18
  { name: 'add-tests', category: 'Execution', description: 'Persist VERIFICATION Pass-cases as node:test UAT (Sentinel-preserving)' },
24
19
  { name: 'pause-work', category: 'Execution', description: 'Stamp STATE.session.stopped_at + resume_file for explicit handoff' },
25
20
  { name: 'resume-work', category: 'Execution', description: 'Classify session state (resume | orphan | clean) from STATE + checkpoints' },
26
21
 
27
- { name: 'undo', category: 'Execution', description: 'Revert all task commits of a phase or plan via git revert (no history rewrite)' },
28
- { name: 'undo-task', category: 'Execution', description: 'Revert a single task commit and reset task status to pending' },
29
- { name: 'reset-slice', category: 'Execution', description: 'Restore working-tree files of the in-flight task and clear current_task (no commit)' },
30
22
  { name: 'skip', category: 'Execution', description: 'Mark task status skipped (lifecycle CRUD)' },
31
23
  { name: 'park', category: 'Execution', description: 'Mark task status parked (lifecycle CRUD)' },
32
24
  { name: 'unpark', category: 'Execution', description: 'Return a parked task to pending (lifecycle CRUD)' },
33
25
 
34
- { name: 'doctor', category: 'Install', description: '5-check install-integrity scan (--fix for auto-safe fixes)' },
35
- { name: 'dispatch', category: 'Install', description: 'State-router: computes next action and delegates via Skill()' },
36
- { name: 'queue', category: 'Install', description: 'Unified queue across todos/backlog/UAT/unplanned phases' },
37
- { name: 'triage', category: 'Install', description: 'Interactive per-item triage loop (promote/keep/drop)' },
26
+ { name: 'undo', category: 'Execution', description: 'Revert every task commit of a milestone or slice via git revert (no history rewrite)' },
27
+ { name: 'undo-task', category: 'Execution', description: 'Revert a single task commit and reset task status to pending' },
28
+ { name: 'reset-slice', category: 'Execution', description: 'Discard in-flight task: restore working tree from HEAD, drop checkpoint, clear STATE.current_task' },
38
29
 
39
- { name: 'resolve-model', category: 'Utility', description: 'Resolve agent/tier to model alias or id (Tier×Profile matrix; consulted at Task-spawn sites by workflows)' },
30
+ { name: 'doctor', category: 'Install', description: '5-check install-integrity scan (--fix for auto-safe fixes)' },
31
+ { name: 'scan-codebase', category: 'Install', description: 'Initial deep codebase inventory → .nubos-pilot/codebase/ skill docs' },
32
+ { name: 'update-docs', category: 'Install', description: 'Refresh stale module docs after code changes' },
40
33
 
34
+ { name: 'resolve-model', category: 'Utility', description: 'Resolve agent/tier to model alias or id (Tier×Profile matrix)' },
41
35
  { name: 'metrics', category: 'Utility', description: 'Record JSONL metrics entry (record | now | start-timestamp | end-timestamp)' },
42
36
 
43
- { name: 'plan-diff', category: 'Planning', description: 'Render two-part PLAN.md diff (semantic + git) or archive-rejected with reason' },
37
+ { name: 'validate-phase', category: 'Review', description: 'Nyquist validation gap-fill via np-nyquist-auditor' },
44
38
 
45
- { name: 'ai-integration-phase', category: 'Planning', description: 'AI-SPEC generator with framework-selector + eval-planner (spawns 4 np-agents)' },
46
- { name: 'ui-phase', category: 'Planning', description: 'UI-SPEC generator with researcher + checker revision loop' },
47
- { name: 'ui-review', category: 'Review', description: '6-pillar retroactive UI audit on a completed phase' },
48
- { name: 'eval-review', category: 'Review', description: 'Retroactive eval-coverage audit on a completed phase' },
39
+ { name: 'add-todo', category: 'Capture', description: 'Capture a pending todo to .nubos-pilot/todos/pending/ + increment STATE count' },
40
+ { name: 'note', category: 'Capture', description: 'Capture a free-form note (project default, --global writes to ~/.nubos-pilot/notes/)' },
41
+ { name: 'add-backlog', category: 'Capture', description: 'Append backlog item to ROADMAP.md' },
49
42
 
50
43
  { name: 'askuser', category: 'Utility', description: 'Capability-layer prompt wrapper (reads spec JSON, returns chosen label)' },
51
- { name: 'commit', category: 'Utility', description: 'Atomic git commit wrapper with gitignore-guard (D-21)' },
44
+ { name: 'commit', category: 'Utility', description: 'Atomic git commit wrapper with gitignore-guard' },
52
45
  { name: 'config-get', category: 'Utility', description: 'Read value from .nubos-pilot/config.json by dotted key path' },
53
- { name: 'generate-slug', category: 'Utility', description: 'Slugify text via lib/phase.cjs.phaseSlug (used by add-todo, add-backlog, thread)' },
54
- { name: 'phase', category: 'Utility', description: 'Phase utilities (next-decimal <base> — used by add-backlog 999.x)' },
46
+ { name: 'generate-slug', category: 'Utility', description: 'Slugify text via lib/layout.cjs.slugify' },
55
47
  { name: 'stats', category: 'Utility', description: 'Aggregated project stats (roadmap + STATE + git + metrics JSON shape)' },
56
48
 
57
- { name: 'code-review', category: 'Review', description: 'Source-file review via np-code-reviewer (depth quick/standard/deep) — writes REVIEW.md sidecar' },
58
- { name: 'code-review-fix', category: 'Review', description: 'Auto-fix REVIEW.md findings via np-code-fixer (atomic commit per finding)' },
59
- { name: 'review', category: 'Review', description: 'Cross-AI peer review via 7-CLI fan-out (gemini/claude/codex/coderabbit/opencode/qwen/cursor)' },
60
- { name: 'secure-phase', category: 'Review', description: 'Threat-mitigation audit via np-security-auditor against PLAN.md threat_model' },
61
- { name: 'validate-phase', category: 'Review', description: 'Nyquist validation gap-fill via np-nyquist-auditor' },
62
- { name: 'add-todo', category: 'Capture', description: 'Capture a pending todo to .nubos-pilot/todos/pending/ + increment STATE count' },
63
- { name: 'note', category: 'Capture', description: 'Capture a free-form note (project default, --global writes to ~/.nubos-pilot/notes/)' },
64
- { name: 'add-backlog', category: 'Capture', description: 'Append backlog item 999.x to ROADMAP.md + scaffold phase dir' },
65
49
  { name: 'thread', category: 'Utility', description: 'Cross-session thread CRUD (create/resume under .nubos-pilot/threads/)' },
66
50
  { name: 'session-report', category: 'Utility', description: 'Generate session report from metrics since .last-session pointer' },
67
- { name: 'cleanup', category: 'Utility', description: 'Archive completed milestones to .nubos-pilot/archive/v<X.Y>/ + collapse ROADMAP <details> block' },
51
+ { name: 'cleanup', category: 'Utility', description: 'Archive completed milestones to .nubos-pilot/archive/v<X.Y>/' },
68
52
  ];
69
53
 
70
54
  module.exports = { COMMANDS };
@@ -3,65 +3,61 @@ const path = require('node:path');
3
3
 
4
4
  const { NubosPilotError, atomicWriteFileSync, withFileLock } = require('../../lib/core.cjs');
5
5
  const { getPhase } = require('../../lib/roadmap.cjs');
6
- const { paddedPhase, phaseSlug, findPhaseDir } = require('../../lib/phase.cjs');
7
- const { parseVerificationMd } = require('../../lib/verify.cjs');
6
+ const layout = require('../../lib/layout.cjs');
7
+ const { parseVerificationMd, milestoneVerificationPath } = require('../../lib/verify.cjs');
8
8
 
9
9
  const BEGIN_MARKER = '// >>> np:add-tests begin';
10
10
  const END_MARKER = '// <<< np:add-tests end';
11
11
 
12
- function _validatePhaseArg(raw) {
13
- if (raw == null || raw === '' || !/^\d+(\.\d+)?$/.test(String(raw))) {
12
+ function _validateMilestoneArg(raw) {
13
+ if (raw == null || raw === '' || !/^\d+$/.test(String(raw))) {
14
14
  throw new NubosPilotError(
15
15
  'add-tests-invalid-phase',
16
- 'add-tests requires a numeric phase argument',
16
+ 'add-tests requires a positive integer milestone argument',
17
17
  { value: raw == null ? '' : String(raw) },
18
18
  );
19
19
  }
20
- return String(raw);
20
+ return Number(raw);
21
21
  }
22
22
 
23
- function _resolveTestTarget(phaseArg, cwd) {
24
- const phaseN = Number(phaseArg);
25
- const phase = getPhase(phaseN, cwd);
26
- const slug = phase.slug || phaseSlug(phase.name);
27
- const padded = paddedPhase(phaseN);
23
+ function _resolveTestTarget(mNum, cwd) {
24
+ const def = getPhase(mNum, cwd);
25
+ const slug = layout.slugify(def.name || 'milestone');
26
+ const mIdStr = layout.mId(mNum);
28
27
  let dir = path.resolve(cwd);
29
28
  for (let i = 0; i < 10; i++) {
30
29
  if (fs.existsSync(path.join(dir, 'package.json'))) {
31
30
  return {
32
31
  pkg_root: dir,
33
- target_path: path.join(dir, 'test', 'uat', 'phase-' + padded + '-' + slug + '.test.cjs'),
34
- padded, slug,
32
+ target_path: path.join(dir, 'test', 'uat', mIdStr.toLowerCase() + '-' + slug + '.test.cjs'),
33
+ milestone_id: mIdStr, slug,
35
34
  };
36
35
  }
37
36
  const parent = path.dirname(dir);
38
37
  if (parent === dir) break;
39
38
  dir = parent;
40
39
  }
41
-
42
40
  return {
43
41
  pkg_root: path.resolve(cwd),
44
- target_path: path.join(path.resolve(cwd), 'test', 'uat', 'phase-' + padded + '-' + slug + '.test.cjs'),
45
- padded, slug,
42
+ target_path: path.join(path.resolve(cwd), 'test', 'uat', mIdStr.toLowerCase() + '-' + slug + '.test.cjs'),
43
+ milestone_id: mIdStr, slug,
46
44
  };
47
45
  }
48
46
 
49
- function _loadCases(phaseArg, cwd) {
50
- const phaseN = Number(phaseArg);
51
- const padded = paddedPhase(phaseN);
52
- const phase_dir = findPhaseDir(phaseN, cwd);
53
- if (!phase_dir) {
47
+ function _loadCases(mNum, cwd) {
48
+ const mDir = layout.findMilestoneDir(mNum, cwd);
49
+ if (!mDir) {
54
50
  throw new NubosPilotError(
55
51
  'add-tests-phase-dir-missing',
56
- 'Phase directory not found for phase ' + phaseN,
57
- { phase: phaseN },
52
+ 'Milestone directory not found for milestone ' + mNum,
53
+ { milestone: mNum },
58
54
  );
59
55
  }
60
- const vp = path.join(phase_dir, padded + '-VERIFICATION.md');
56
+ const vp = milestoneVerificationPath(mNum, cwd);
61
57
  if (!fs.existsSync(vp)) {
62
58
  throw new NubosPilotError(
63
59
  'add-tests-verification-missing',
64
- 'VERIFICATION.md not found — run `/np:verify-work ' + phaseArg + '` first',
60
+ 'VERIFICATION.md not found — run `/np:verify-work ' + mNum + '` first',
65
61
  { path: vp },
66
62
  );
67
63
  }
@@ -75,10 +71,10 @@ function _jsString(s) {
75
71
  return "'" + String(s).replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n') + "'";
76
72
  }
77
73
 
78
- function _renderBlock(phasePadded, passes, skips) {
74
+ function _renderBlock(milestoneId, passes, skips) {
79
75
  const date = new Date().toISOString().slice(0, 10);
80
76
  const lines = [];
81
- lines.push(BEGIN_MARKER + ' (phase ' + phasePadded + ', generated ' + date + ')');
77
+ lines.push(BEGIN_MARKER + ' (' + milestoneId + ', generated ' + date + ')');
82
78
  lines.push("const { test } = require('node:test');");
83
79
  lines.push("const assert = require('node:assert');");
84
80
  lines.push('');
@@ -111,11 +107,11 @@ function _mergeBlock(existing, block) {
111
107
  return before + block + after;
112
108
  }
113
109
 
114
- function _emitTests(phaseArg, cwd) {
115
- const { passes, skips } = _loadCases(phaseArg, cwd);
116
- const target = _resolveTestTarget(phaseArg, cwd);
110
+ function _emitTests(mNum, cwd) {
111
+ const { passes, skips } = _loadCases(mNum, cwd);
112
+ const target = _resolveTestTarget(mNum, cwd);
117
113
  fs.mkdirSync(path.dirname(target.target_path), { recursive: true });
118
- const block = _renderBlock(target.padded, passes, skips);
114
+ const block = _renderBlock(target.milestone_id, passes, skips);
119
115
  return withFileLock(target.target_path, () => {
120
116
  let existing = null;
121
117
  try { existing = fs.readFileSync(target.target_path, 'utf-8'); } catch { existing = null; }
@@ -139,12 +135,13 @@ function run(args, ctx) {
139
135
 
140
136
  switch (verb) {
141
137
  case 'init': {
142
- const phaseArg = _validatePhaseArg(list[1]);
143
- const target = _resolveTestTarget(phaseArg, cwd);
144
- const { passes, skips, verification_path } = _loadCases(phaseArg, cwd);
138
+ const mNum = _validateMilestoneArg(list[1]);
139
+ const target = _resolveTestTarget(mNum, cwd);
140
+ const { passes, skips, verification_path } = _loadCases(mNum, cwd);
145
141
  const payload = {
146
142
  _workflow: 'add-tests',
147
- phase: phaseArg,
143
+ milestone: mNum,
144
+ milestone_id: target.milestone_id,
148
145
  target_path: target.target_path,
149
146
  verification_path,
150
147
  pass_cases: passes,
@@ -154,8 +151,8 @@ function run(args, ctx) {
154
151
  return payload;
155
152
  }
156
153
  case 'emit': {
157
- const phaseArg = _validatePhaseArg(list[1]);
158
- const result = _emitTests(phaseArg, cwd);
154
+ const mNum = _validateMilestoneArg(list[1]);
155
+ const result = _emitTests(mNum, cwd);
159
156
  stdout.write(JSON.stringify(result));
160
157
  return result;
161
158
  }
@@ -3,30 +3,40 @@ const assert = require('node:assert/strict');
3
3
  const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
 
6
- const { makeSandbox, seedRoadmapYaml, seedPhaseDir, cleanupAll } =
6
+ const { makeSandbox, seedRoadmapYaml, seedMilestoneDir, cleanupAll } =
7
7
  require('../../tests/helpers/fixture.cjs');
8
8
  const subcmd = require('./add-tests.cjs');
9
9
 
10
10
  function _roadmap() {
11
11
  return {
12
- schema_version: 1,
13
- milestones: [{ id: 'v1.0', name: 'm1', phases: [
14
- { number: 6, name: 'Execution', slug: 'execution', goal: '', depends_on: [],
15
- requirements: [], success_criteria: ['a', 'b', 'c'],
16
- status: 'planned', plans: [] },
17
- ]}],
12
+ schema_version: 2,
13
+ milestones: [
14
+ {
15
+ id: 'M006',
16
+ number: 6,
17
+ name: 'Execution',
18
+ goal: '',
19
+ requirements: [],
20
+ success_criteria: ['a', 'b', 'c'],
21
+ status: 'pending',
22
+ slices: [],
23
+ },
24
+ ],
18
25
  };
19
26
  }
20
27
 
21
- function _capture() { let b = ''; return { stub: { write: (s) => { b += s; return true; } }, get: () => b }; }
28
+ function _capture() {
29
+ let b = '';
30
+ return { stub: { write: (s) => { b += s; return true; } }, get: () => b };
31
+ }
22
32
 
23
33
  function _seedVerification(sandbox) {
24
- const phaseDir = seedPhaseDir(sandbox, 6, 'execution', {});
34
+ const mDir = seedMilestoneDir(sandbox, 6, {});
25
35
  const v = [
26
- '# Phase 6: Execution - Verification',
36
+ '# M006 Execution Verification',
27
37
  '',
28
38
  '**Verified:** 2026-04-15',
29
- '**Phase Status:** deferred',
39
+ '**Milestone Status:** deferred',
30
40
  '',
31
41
  '## Success Criteria',
32
42
  '',
@@ -46,10 +56,9 @@ function _seedVerification(sandbox) {
46
56
  '- **Evidence:** —',
47
57
  '',
48
58
  ].join('\n');
49
- fs.writeFileSync(path.join(phaseDir, '06-VERIFICATION.md'), v, 'utf-8');
50
-
59
+ fs.writeFileSync(path.join(mDir, 'M006-VERIFICATION.md'), v, 'utf-8');
51
60
  fs.writeFileSync(path.join(sandbox, 'package.json'), '{"name":"sandbox"}', 'utf-8');
52
- return phaseDir;
61
+ return mDir;
53
62
  }
54
63
 
55
64
  afterEach(cleanupAll);
@@ -60,40 +69,39 @@ test('AT-1: init emits pass_cases (1) and skip_cases (2) categorized', () => {
60
69
  _seedVerification(sandbox);
61
70
  const cap = _capture();
62
71
  const p = subcmd.run(['init', '6'], { cwd: sandbox, stdout: cap.stub });
72
+ assert.equal(p.milestone, 6);
73
+ assert.equal(p.milestone_id, 'M006');
63
74
  assert.equal(p.pass_cases.length, 1);
64
75
  assert.equal(p.skip_cases.length, 2);
65
- assert.ok(p.target_path.endsWith('test/uat/phase-06-execution.test.cjs'));
76
+ assert.ok(p.target_path.endsWith(path.join('test', 'uat', 'm006-execution.test.cjs')));
66
77
  });
67
78
 
68
79
  test('AT-2: emit writes node:test file with begin/end sentinels', () => {
69
80
  const sandbox = makeSandbox();
70
81
  seedRoadmapYaml(sandbox, _roadmap());
71
82
  _seedVerification(sandbox);
72
- const cap = _capture();
73
- const r = subcmd.run(['emit', '6'], { cwd: sandbox, stdout: cap.stub });
83
+ const r = subcmd.run(['emit', '6'], { cwd: sandbox, stdout: _capture().stub });
74
84
  const body = fs.readFileSync(r.target_path, 'utf-8');
75
85
  assert.ok(body.includes('// >>> np:add-tests begin'));
76
86
  assert.ok(body.includes('// <<< np:add-tests end'));
77
87
  assert.ok(body.includes("test('SC-1: First passes'"));
78
88
  assert.ok(body.includes("test.skip('SC-2: Second fails'"));
79
89
  assert.ok(body.includes("test.skip('SC-3: Deferred'"));
90
+ assert.ok(body.includes('M006'));
80
91
  });
81
92
 
82
93
  test('AT-3: sentinel preservation — user edits OUTSIDE block survive regeneration', () => {
83
94
  const sandbox = makeSandbox();
84
95
  seedRoadmapYaml(sandbox, _roadmap());
85
96
  _seedVerification(sandbox);
86
- const cap1 = _capture();
87
- subcmd.run(['emit', '6'], { cwd: sandbox, stdout: cap1.stub });
88
- const target = path.join(sandbox, 'test', 'uat', 'phase-06-execution.test.cjs');
89
-
97
+ subcmd.run(['emit', '6'], { cwd: sandbox, stdout: _capture().stub });
98
+ const target = path.join(sandbox, 'test', 'uat', 'm006-execution.test.cjs');
90
99
  let body = fs.readFileSync(target, 'utf-8');
91
100
  const userTest = "// USER AUTHORED: do not delete\ntest('user: custom case', () => { assert.ok(1); });\n\n";
92
101
  body = userTest + body;
93
102
  fs.writeFileSync(target, body, 'utf-8');
94
103
 
95
- const cap2 = _capture();
96
- subcmd.run(['emit', '6'], { cwd: sandbox, stdout: cap2.stub });
104
+ subcmd.run(['emit', '6'], { cwd: sandbox, stdout: _capture().stub });
97
105
  const after = fs.readFileSync(target, 'utf-8');
98
106
  assert.ok(after.includes('// USER AUTHORED: do not delete'), 'user content lost');
99
107
  assert.ok(after.includes("test('user: custom case'"), 'user test lost');
@@ -103,20 +111,18 @@ test('AT-3: sentinel preservation — user edits OUTSIDE block survive regenerat
103
111
  test('AT-4: missing VERIFICATION.md → loud error', () => {
104
112
  const sandbox = makeSandbox();
105
113
  seedRoadmapYaml(sandbox, _roadmap());
106
- seedPhaseDir(sandbox, 6, 'execution', {});
114
+ seedMilestoneDir(sandbox, 6, {});
107
115
  fs.writeFileSync(path.join(sandbox, 'package.json'), '{}', 'utf-8');
108
- const cap = _capture();
109
116
  assert.throws(
110
- () => subcmd.run(['init', '6'], { cwd: sandbox, stdout: cap.stub }),
117
+ () => subcmd.run(['init', '6'], { cwd: sandbox, stdout: _capture().stub }),
111
118
  (err) => err && err.code === 'add-tests-verification-missing',
112
119
  );
113
120
  });
114
121
 
115
122
  test('AT-5: unknown verb throws', () => {
116
123
  const sandbox = makeSandbox();
117
- const cap = _capture();
118
124
  assert.throws(
119
- () => subcmd.run(['bogus'], { cwd: sandbox, stdout: cap.stub }),
125
+ () => subcmd.run(['bogus'], { cwd: sandbox, stdout: _capture().stub }),
120
126
  (err) => err && err.code === 'add-tests-unknown-verb',
121
127
  );
122
128
  });
@@ -2,7 +2,7 @@ const fs = require('node:fs');
2
2
  const path = require('node:path');
3
3
 
4
4
  const { projectStateDir, NubosPilotError } = require('../../lib/core.cjs');
5
- const { phaseSlug } = require('../../lib/phase.cjs');
5
+ const { slugify } = require('../../lib/layout.cjs');
6
6
 
7
7
  const MAX_DESCRIPTION_LENGTH = 500;
8
8
 
@@ -34,7 +34,7 @@ function _buildPayload(description, cwd) {
34
34
  const now = new Date();
35
35
  const iso = now.toISOString();
36
36
  const date = iso.slice(0, 10);
37
- const slug = phaseSlug(description);
37
+ const slug = slugify(description);
38
38
  if (!slug) {
39
39
  throw new NubosPilotError(
40
40
  'add-todo-empty-slug',
@@ -53,29 +53,29 @@ after(() => {
53
53
  test('CPT-1: checkpoint start writes file and updates STATE.current_task', () => {
54
54
  const root = makeRoot();
55
55
  const cap = _capture();
56
- subcmd.run(['start', '06-01-T01', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap.stub });
57
- const cp = JSON.parse(fs.readFileSync(path.join(root, '.nubos-pilot', 'checkpoints', '06-01-T01.json'), 'utf-8'));
58
- assert.equal(cp.task_id, '06-01-T01');
56
+ subcmd.run(['start', 'M006-S001-T0001', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap.stub });
57
+ const cp = JSON.parse(fs.readFileSync(path.join(root, '.nubos-pilot', 'checkpoints', 'M006-S001-T0001.json'), 'utf-8'));
58
+ assert.equal(cp.task_id, 'M006-S001-T0001');
59
59
  assert.equal(cp.status, 'in-progress');
60
60
  const state = fs.readFileSync(path.join(root, '.nubos-pilot', 'STATE.md'), 'utf-8');
61
- assert.ok(state.includes('current_task: 06-01-T01'), state);
61
+ assert.ok(state.includes('current_task: M006-S001-T0001'), state);
62
62
  });
63
63
 
64
64
  test('CPT-2: checkpoint transition updates status', () => {
65
65
  const root = makeRoot();
66
66
  const cap = _capture();
67
- subcmd.run(['start', '06-01-T02', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap.stub });
68
- subcmd.run(['transition', '06-01-T02', 'verifying'], { cwd: root, stdout: cap.stub });
69
- const cp = JSON.parse(fs.readFileSync(path.join(root, '.nubos-pilot', 'checkpoints', '06-01-T02.json'), 'utf-8'));
67
+ subcmd.run(['start', 'M006-S001-T0002', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap.stub });
68
+ subcmd.run(['transition', 'M006-S001-T0002', 'verifying'], { cwd: root, stdout: cap.stub });
69
+ const cp = JSON.parse(fs.readFileSync(path.join(root, '.nubos-pilot', 'checkpoints', 'M006-S001-T0002.json'), 'utf-8'));
70
70
  assert.equal(cp.status, 'verifying');
71
71
  });
72
72
 
73
73
  test('CPT-3: checkpoint transition rejects unknown status', () => {
74
74
  const root = makeRoot();
75
75
  const cap = _capture();
76
- subcmd.run(['start', '06-01-T03', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap.stub });
76
+ subcmd.run(['start', 'M006-S001-T0003', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap.stub });
77
77
  assert.throws(
78
- () => subcmd.run(['transition', '06-01-T03', 'weird'], { cwd: root, stdout: cap.stub }),
78
+ () => subcmd.run(['transition', 'M006-S001-T0003', 'weird'], { cwd: root, stdout: cap.stub }),
79
79
  (err) => err && err.code === 'checkpoint-invalid-status',
80
80
  );
81
81
  });
@@ -83,28 +83,28 @@ test('CPT-3: checkpoint transition rejects unknown status', () => {
83
83
  test('CPT-4: checkpoint touch appends to files_touched', () => {
84
84
  const root = makeRoot();
85
85
  const cap = _capture();
86
- subcmd.run(['start', '06-01-T04', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap.stub });
87
- subcmd.run(['touch', '06-01-T04', 'src/a.ts'], { cwd: root, stdout: cap.stub });
88
- subcmd.run(['touch', '06-01-T04', 'src/b.ts'], { cwd: root, stdout: cap.stub });
89
- const cp = JSON.parse(fs.readFileSync(path.join(root, '.nubos-pilot', 'checkpoints', '06-01-T04.json'), 'utf-8'));
86
+ subcmd.run(['start', 'M006-S001-T0004', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap.stub });
87
+ subcmd.run(['touch', 'M006-S001-T0004', 'src/a.ts'], { cwd: root, stdout: cap.stub });
88
+ subcmd.run(['touch', 'M006-S001-T0004', 'src/b.ts'], { cwd: root, stdout: cap.stub });
89
+ const cp = JSON.parse(fs.readFileSync(path.join(root, '.nubos-pilot', 'checkpoints', 'M006-S001-T0004.json'), 'utf-8'));
90
90
  assert.deepEqual(cp.files_touched, ['src/a.ts', 'src/b.ts']);
91
91
  });
92
92
 
93
93
  test('CPT-5: checkpoint show emits JSON', () => {
94
94
  const root = makeRoot();
95
95
  const cap1 = _capture();
96
- subcmd.run(['start', '06-01-T05', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap1.stub });
96
+ subcmd.run(['start', 'M006-S001-T0005', '--phase', '6', '--plan', '06-01', '--wave', '1'], { cwd: root, stdout: cap1.stub });
97
97
  const cap2 = _capture();
98
- subcmd.run(['show', '06-01-T05'], { cwd: root, stdout: cap2.stub });
98
+ subcmd.run(['show', 'M006-S001-T0005'], { cwd: root, stdout: cap2.stub });
99
99
  const json = JSON.parse(cap2.get());
100
- assert.equal(json.task_id, '06-01-T05');
100
+ assert.equal(json.task_id, 'M006-S001-T0005');
101
101
  });
102
102
 
103
103
  test('CPT-6: unknown verb throws', () => {
104
104
  const root = makeRoot();
105
105
  const cap = _capture();
106
106
  assert.throws(
107
- () => subcmd.run(['bogus', '06-01-T01'], { cwd: root, stdout: cap.stub }),
107
+ () => subcmd.run(['bogus', 'M006-S001-T0001'], { cwd: root, stdout: cap.stub }),
108
108
  (err) => err && err.code === 'checkpoint-unknown-verb',
109
109
  );
110
110
  });