@slope-dev/slope 1.60.0 → 1.61.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 (115) hide show
  1. package/README.md +6 -0
  2. package/dist/cli/commands/agent.js +2 -2
  3. package/dist/cli/commands/agent.js.map +1 -1
  4. package/dist/cli/commands/commit-ready.js +1 -1
  5. package/dist/cli/commands/commit-ready.js.map +1 -1
  6. package/dist/cli/commands/next.d.ts +1 -1
  7. package/dist/cli/commands/next.d.ts.map +1 -1
  8. package/dist/cli/commands/next.js +31 -2
  9. package/dist/cli/commands/next.js.map +1 -1
  10. package/dist/cli/commands/now.d.ts +3 -0
  11. package/dist/cli/commands/now.d.ts.map +1 -1
  12. package/dist/cli/commands/now.js +14 -2
  13. package/dist/cli/commands/now.js.map +1 -1
  14. package/dist/cli/commands/review-state.d.ts.map +1 -1
  15. package/dist/cli/commands/review-state.js +45 -4
  16. package/dist/cli/commands/review-state.js.map +1 -1
  17. package/dist/cli/commands/review.d.ts +9 -1
  18. package/dist/cli/commands/review.d.ts.map +1 -1
  19. package/dist/cli/commands/review.js +67 -39
  20. package/dist/cli/commands/review.js.map +1 -1
  21. package/dist/cli/commands/roadmap.d.ts.map +1 -1
  22. package/dist/cli/commands/roadmap.js +274 -5
  23. package/dist/cli/commands/roadmap.js.map +1 -1
  24. package/dist/cli/commands/sprint.d.ts.map +1 -1
  25. package/dist/cli/commands/sprint.js +353 -54
  26. package/dist/cli/commands/sprint.js.map +1 -1
  27. package/dist/cli/guards/claim-required.d.ts.map +1 -1
  28. package/dist/cli/guards/claim-required.js +19 -4
  29. package/dist/cli/guards/claim-required.js.map +1 -1
  30. package/dist/cli/guards/next-action.js +1 -1
  31. package/dist/cli/guards/next-action.js.map +1 -1
  32. package/dist/cli/guards/phase-boundary.d.ts.map +1 -1
  33. package/dist/cli/guards/phase-boundary.js +8 -2
  34. package/dist/cli/guards/phase-boundary.js.map +1 -1
  35. package/dist/cli/guards/post-push.js +2 -2
  36. package/dist/cli/guards/post-push.js.map +1 -1
  37. package/dist/cli/guards/review-tier.d.ts.map +1 -1
  38. package/dist/cli/guards/review-tier.js +2 -3
  39. package/dist/cli/guards/review-tier.js.map +1 -1
  40. package/dist/cli/guards/roadmap-edit-shipped.d.ts.map +1 -1
  41. package/dist/cli/guards/roadmap-edit-shipped.js +143 -7
  42. package/dist/cli/guards/roadmap-edit-shipped.js.map +1 -1
  43. package/dist/cli/guards/scope-drift.d.ts.map +1 -1
  44. package/dist/cli/guards/scope-drift.js +8 -2
  45. package/dist/cli/guards/scope-drift.js.map +1 -1
  46. package/dist/cli/guards/session-briefing.js +6 -2
  47. package/dist/cli/guards/session-briefing.js.map +1 -1
  48. package/dist/cli/guards/sprint-completion.d.ts.map +1 -1
  49. package/dist/cli/guards/sprint-completion.js +94 -18
  50. package/dist/cli/guards/sprint-completion.js.map +1 -1
  51. package/dist/cli/index.js +4 -2
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/cli/registry.d.ts.map +1 -1
  54. package/dist/cli/registry.js +25 -0
  55. package/dist/cli/registry.js.map +1 -1
  56. package/dist/cli/roadmap-source-store.d.ts +37 -0
  57. package/dist/cli/roadmap-source-store.d.ts.map +1 -0
  58. package/dist/cli/roadmap-source-store.js +298 -0
  59. package/dist/cli/roadmap-source-store.js.map +1 -0
  60. package/dist/cli/sprint-inference.js +1 -1
  61. package/dist/cli/sprint-inference.js.map +1 -1
  62. package/dist/cli/sprint-rollover.d.ts +102 -0
  63. package/dist/cli/sprint-rollover.d.ts.map +1 -0
  64. package/dist/cli/sprint-rollover.js +558 -0
  65. package/dist/cli/sprint-rollover.js.map +1 -0
  66. package/dist/cli/sprint-state.d.ts +72 -1
  67. package/dist/cli/sprint-state.d.ts.map +1 -1
  68. package/dist/cli/sprint-state.js +203 -5
  69. package/dist/cli/sprint-state.js.map +1 -1
  70. package/dist/cli/template-generator.d.ts.map +1 -1
  71. package/dist/cli/template-generator.js +45 -32
  72. package/dist/cli/template-generator.js.map +1 -1
  73. package/dist/cli/workflow-resync.d.ts.map +1 -1
  74. package/dist/cli/workflow-resync.js +9 -8
  75. package/dist/cli/workflow-resync.js.map +1 -1
  76. package/dist/core/briefing.d.ts.map +1 -1
  77. package/dist/core/briefing.js +254 -14
  78. package/dist/core/briefing.js.map +1 -1
  79. package/dist/core/builder.d.ts +16 -2
  80. package/dist/core/builder.d.ts.map +1 -1
  81. package/dist/core/builder.js +82 -5
  82. package/dist/core/builder.js.map +1 -1
  83. package/dist/core/index.d.ts +7 -3
  84. package/dist/core/index.d.ts.map +1 -1
  85. package/dist/core/index.js +3 -1
  86. package/dist/core/index.js.map +1 -1
  87. package/dist/core/roadmap-focus.d.ts +101 -0
  88. package/dist/core/roadmap-focus.d.ts.map +1 -0
  89. package/dist/core/roadmap-focus.js +293 -0
  90. package/dist/core/roadmap-focus.js.map +1 -0
  91. package/dist/core/roadmap-sources.d.ts +52 -0
  92. package/dist/core/roadmap-sources.d.ts.map +1 -0
  93. package/dist/core/roadmap-sources.js +483 -0
  94. package/dist/core/roadmap-sources.js.map +1 -0
  95. package/dist/core/roadmap.d.ts +25 -1
  96. package/dist/core/roadmap.d.ts.map +1 -1
  97. package/dist/core/roadmap.js +62 -45
  98. package/dist/core/roadmap.js.map +1 -1
  99. package/dist/core/workflow-engine.d.ts.map +1 -1
  100. package/dist/core/workflow-engine.js +17 -2
  101. package/dist/core/workflow-engine.js.map +1 -1
  102. package/dist/mcp/registry.d.ts +1 -1
  103. package/dist/mcp/registry.d.ts.map +1 -1
  104. package/dist/mcp/registry.js +5 -4
  105. package/dist/mcp/registry.js.map +1 -1
  106. package/package.json +1 -1
  107. package/src/core/workflows/sprint-standard.yaml +1 -1
  108. package/templates/claude-code/CLAUDE.md +3 -1
  109. package/templates/claude-code/commands/start-sprint.md +9 -5
  110. package/templates/claude-code/rules/sprint-checklist.md +13 -10
  111. package/templates/codex/plugins/slope/.codex-plugin/plugin.json +1 -1
  112. package/templates/codex/plugins/slope/skills/slope-sprint/SKILL.md +5 -4
  113. package/templates/cursor/rules/slope-sprint-checklist.mdc +11 -8
  114. package/templates/generic/SLOPE-CHECKLIST.md +10 -7
  115. package/templates/pi/skills/start-sprint.md +9 -5
@@ -1,49 +1,69 @@
1
- import { mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
2
- import { dirname, isAbsolute, join } from 'node:path';
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
2
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
3
3
  import { formatSprintReview, compareSprintIds, normalizeScorecard, parseSprintNumber } from '../../core/index.js';
4
4
  import { loadConfig } from '../config.js';
5
5
  import { resolveMetaphor } from '../metaphor.js';
6
- import { updateGate } from '../sprint-state.js';
7
- export function reviewCommand(path, mode, metaphorFlag, outputPath) {
8
- const config = loadConfig();
9
- const cwd = process.cwd();
10
- const metaphor = resolveMetaphor(metaphorFlag ? [`--metaphor=${metaphorFlag}`] : [], config.metaphor);
11
- if (!path) {
12
- // Default to latest scorecard
13
- const dir = join(cwd, config.scorecardDir);
14
- const patternParts = config.scorecardPattern.split('*');
15
- const prefix = patternParts[0] ?? '';
16
- const suffix = patternParts[1] ?? '';
17
- const regex = new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(\\d+(?:\\.\\d+)?)${suffix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`);
18
- try {
19
- const files = readdirSync(dir)
20
- .filter((f) => {
21
- const m = f.match(regex);
22
- const sprint = m ? parseSprintNumber(m[1]) : null;
23
- return sprint != null && sprint >= config.minSprint;
24
- })
25
- .sort((a, b) => {
26
- const na = parseSprintNumber(a.match(regex)?.[1] ?? '0') ?? 0;
27
- const nb = parseSprintNumber(b.match(regex)?.[1] ?? '0') ?? 0;
28
- return compareSprintIds(na, nb);
29
- });
30
- if (files.length === 0) {
31
- console.log('\nNo scorecards found.\n');
32
- process.exit(1);
33
- }
34
- path = join(dir, files[files.length - 1]);
6
+ import { loadSprintState, updateGate } from '../sprint-state.js';
7
+ function discoverScorecardPaths(cwd, config) {
8
+ const dir = join(cwd, config.scorecardDir);
9
+ if (!existsSync(dir))
10
+ return [];
11
+ const patternParts = config.scorecardPattern.split('*');
12
+ const prefix = patternParts[0] ?? '';
13
+ const suffix = patternParts[1] ?? '';
14
+ const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
15
+ const regex = new RegExp(`^${escapeRegex(prefix)}(\\d+(?:\\.\\d+)?)${escapeRegex(suffix)}$`);
16
+ return readdirSync(dir)
17
+ .map(file => ({ file, sprint: parseSprintNumber(file.match(regex)?.[1] ?? '') }))
18
+ .filter((entry) => entry.sprint != null && entry.sprint >= config.minSprint)
19
+ .sort((a, b) => compareSprintIds(a.sprint, b.sprint))
20
+ .map(entry => join(dir, entry.file));
21
+ }
22
+ function resolveScorecardPath(path, sprintSelector, cwd, config) {
23
+ if (path && sprintSelector) {
24
+ console.error('\nUse either an explicit scorecard path or --sprint, not both.\n');
25
+ process.exit(1);
26
+ }
27
+ if (path)
28
+ return isAbsolute(path) ? path : resolve(cwd, path);
29
+ if (sprintSelector) {
30
+ const sprint = parseSprintNumber(sprintSelector);
31
+ if (sprint == null) {
32
+ console.error(`\nInvalid sprint selector: ${sprintSelector}\n`);
33
+ process.exit(1);
35
34
  }
36
- catch {
37
- console.log('\nScorecard directory not found.\n');
35
+ const selected = join(cwd, config.scorecardDir, config.scorecardPattern.replace('*', String(sprint)));
36
+ if (!existsSync(selected)) {
37
+ console.error(`\nScorecard not found for Sprint ${sprint}: ${relative(cwd, selected)}\n`);
38
38
  process.exit(1);
39
39
  }
40
+ return selected;
41
+ }
42
+ const scorecards = discoverScorecardPaths(cwd, config);
43
+ if (scorecards.length === 0) {
44
+ console.error('\nNo scorecards found. Pass an explicit scorecard path.\n');
45
+ process.exit(1);
46
+ }
47
+ if (scorecards.length > 1) {
48
+ console.error('\nMultiple scorecards found; refusing to guess which sprint to review.');
49
+ console.error('Pass an explicit path, for example:');
50
+ console.error(` slope review ${relative(cwd, scorecards[scorecards.length - 1])}`);
51
+ console.error('');
52
+ process.exit(1);
40
53
  }
54
+ return scorecards[0];
55
+ }
56
+ export function reviewCommand(path, mode, metaphorFlag, outputPath, sprintSelector) {
57
+ const config = loadConfig();
58
+ const cwd = process.cwd();
59
+ const metaphor = resolveMetaphor(metaphorFlag ? [`--metaphor=${metaphorFlag}`] : [], config.metaphor);
60
+ const scorecardPath = resolveScorecardPath(path, sprintSelector, cwd, config);
41
61
  let raw;
42
62
  try {
43
- raw = JSON.parse(readFileSync(path, 'utf8'));
63
+ raw = JSON.parse(readFileSync(scorecardPath, 'utf8'));
44
64
  }
45
65
  catch {
46
- console.error(`\nFailed to parse ${path}\n`);
66
+ console.error(`\nFailed to parse ${scorecardPath}\n`);
47
67
  process.exit(1);
48
68
  }
49
69
  const card = normalizeScorecard(raw);
@@ -54,12 +74,20 @@ export function reviewCommand(path, mode, metaphorFlag, outputPath) {
54
74
  if (outputPath !== null) {
55
75
  const reviewPath = outputPath
56
76
  ? isAbsolute(outputPath) ? outputPath : join(cwd, outputPath)
57
- : join(cwd, config.scorecardDir, `sprint-${card.sprint_number}-review.md`);
77
+ : join(dirname(scorecardPath), `sprint-${card.sprint_number}-review.md`);
58
78
  mkdirSync(dirname(reviewPath), { recursive: true });
59
79
  writeFileSync(reviewPath, review + '\n');
60
- console.log(`\nReview written: ${reviewPath.replace(cwd + '/', '')}`);
80
+ console.error(`\nReview written: ${relative(cwd, reviewPath)}`);
81
+ }
82
+ // Generating a historical sprint's review must not complete a different
83
+ // active sprint's gate.
84
+ const sprintState = loadSprintState(cwd);
85
+ if (sprintState?.sprint === card.sprint_number) {
86
+ updateGate(cwd, 'review_md', true);
61
87
  }
62
- // Mark review_md gate complete
63
- updateGate(cwd, 'review_md', true);
64
88
  }
89
+ export const reviewCommandInternals = {
90
+ discoverScorecardPaths,
91
+ resolveScorecardPath,
92
+ };
65
93
  //# sourceMappingURL=review.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"review.js","sourceRoot":"","sources":["../../../src/cli/commands/review.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../../src/cli/commands/review.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"roadmap.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/roadmap.ts"],"names":[],"mappings":"AAktBA,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DlE"}
1
+ {"version":3,"file":"roadmap.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/roadmap.ts"],"names":[],"mappings":"AA8+BA,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6ElE"}
@@ -1,14 +1,15 @@
1
1
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { parseRoadmap, validateRoadmap, castRoadmapStructure, findShippedSprintsOnMain, compareSprintIds, computeCriticalPath, findParallelOpportunities, formatSprintLabel, formatRoadmapSummary, formatStrategicContext, isRoadmapSprintPending, loadScorecards, loadVision, parseSprintNumber, analyzeBacklog, mergeBacklogs, runAnalyzers, estimateComplexity, generateRoadmapFromVision, RoadmapGenerationError, } from '../../core/index.js';
2
+ import { dirname, join, relative, resolve } from 'node:path';
3
+ import { parseRoadmap, validateRoadmap, castRoadmapStructure, findShippedSprintsOnMain, compareSprintIds, computeCriticalPath, findParallelOpportunities, formatSprintLabel, formatRoadmapSummary, formatStrategicContext, buildRoadmapFocus, formatRoadmapFocus, formatRoadmapSprintLabel, roadmapSprintOrderValue, isRoadmapSprintPending, loadScorecards, discoverScorecardFiles, sprintNumberFromScorecardFile, loadVision, parseSprintNumber, analyzeBacklog, mergeBacklogs, runAnalyzers, estimateComplexity, generateRoadmapFromVision, RoadmapGenerationError, } from '../../core/index.js';
4
4
  import { loadConfig } from '../config.js';
5
- import { buildRoadmapReality, formatRoadmapRealitySection } from '../pre-sprint-reality.js';
5
+ import { buildRoadmapReality, formatRoadmapRealitySection, roadmapRealityIssues } from '../pre-sprint-reality.js';
6
6
  import { interviewCommand } from './interview.js';
7
+ import { loadRoadmapSourceStore, hasModularRoadmapSources, planRoadmapSourceArchive, applyRoadmapSourceArchive, validateRoadmapSourceStore, writeRoadmapSourceProjection, } from '../roadmap-source-store.js';
7
8
  // --- Helpers ---
8
9
  function parseArgs(args) {
9
10
  const result = {};
10
11
  for (const arg of args) {
11
- const match = arg.match(/^--(\w[\w-]*)(?:=(.+))?$/);
12
+ const match = arg.match(/^--(\w[\w-]*)(?:=(.*))?$/);
12
13
  if (match)
13
14
  result[match[1]] = match[2] ?? 'true';
14
15
  }
@@ -301,6 +302,225 @@ function statusSubcommand(flags, cwd) {
301
302
  printCompactRoadmapStatus(roadmap, currentSprint, completedSprints, cwd);
302
303
  }
303
304
  }
305
+ function displayPath(cwd, path) {
306
+ return relative(cwd, resolve(cwd, path)).replace(/\\/g, '/');
307
+ }
308
+ function focusEvidence(roadmap, sprintId, flags, cwd) {
309
+ const config = loadConfig(cwd);
310
+ const evidence = [{
311
+ kind: 'roadmap',
312
+ label: 'Roadmap source',
313
+ ref: displayPath(cwd, resolveRoadmapPath(flags, cwd)),
314
+ sprint: sprintId,
315
+ }];
316
+ const selected = roadmap.sprints.find(sprint => sprint.id === sprintId);
317
+ const phase = roadmap.phases.find(candidate => candidate.sprints.includes(sprintId));
318
+ const phaseIndex = phase?.sprints.indexOf(sprintId) ?? -1;
319
+ const contextIds = new Set([
320
+ sprintId,
321
+ ...(selected?.depends_on ?? []),
322
+ ...(phaseIndex < 0 ? [] : phase.sprints.slice(Math.max(0, phaseIndex - 2), phaseIndex)),
323
+ ]);
324
+ const contextByValue = new Map([...contextIds].map(id => [roadmapSprintOrderValue(roadmap, id), id]));
325
+ for (const path of discoverScorecardFiles(config, cwd)) {
326
+ const scorecardSprint = sprintNumberFromScorecardFile(path, config);
327
+ if (scorecardSprint == null)
328
+ continue;
329
+ const roadmapSprintId = contextByValue.get(roadmapSprintOrderValue(roadmap, scorecardSprint));
330
+ if (roadmapSprintId == null)
331
+ continue;
332
+ evidence.push({
333
+ kind: 'scorecard',
334
+ label: `${formatRoadmapSprintLabel(roadmap, roadmapSprintId)} scorecard`,
335
+ ref: displayPath(cwd, path),
336
+ sprint: roadmapSprintId,
337
+ });
338
+ const reviewPath = [scorecardSprint, roadmapSprintId]
339
+ .map(id => join(dirname(path), `sprint-${id}-review.md`))
340
+ .find(candidate => existsSync(candidate));
341
+ if (reviewPath) {
342
+ evidence.push({
343
+ kind: 'review',
344
+ label: `${formatRoadmapSprintLabel(roadmap, roadmapSprintId)} review`,
345
+ ref: displayPath(cwd, reviewPath),
346
+ sprint: roadmapSprintId,
347
+ });
348
+ }
349
+ }
350
+ return evidence;
351
+ }
352
+ function focusSubcommand(flags, cwd) {
353
+ if (!Object.prototype.hasOwnProperty.call(flags, 'sprint') || flags.sprint === 'true') {
354
+ console.error('\nMissing required --sprint=N for roadmap focus.');
355
+ console.error('Usage: slope roadmap focus --sprint=N [--path=<file>] [--json]\n');
356
+ process.exit(1);
357
+ return;
358
+ }
359
+ const sprintId = parseSprintNumber(flags.sprint);
360
+ if (sprintId == null) {
361
+ console.error(`\nInvalid sprint number: ${flags.sprint || '(empty)'}`);
362
+ console.error('Usage: slope roadmap focus --sprint=N [--path=<file>] [--json]\n');
363
+ process.exit(1);
364
+ return;
365
+ }
366
+ const roadmap = loadRoadmapFile(flags, cwd);
367
+ if (!roadmap) {
368
+ process.exit(1);
369
+ return;
370
+ }
371
+ const config = loadConfig(cwd);
372
+ const scorecards = loadScorecards(config, cwd);
373
+ const completedSprintIds = scorecards.map(card => card.sprint_number);
374
+ const hazards = roadmapRealityIssues(buildRoadmapReality(cwd, roadmap), sprintId)
375
+ .map(issue => ({
376
+ sprint: sprintId,
377
+ sprint_label: formatRoadmapSprintLabel(roadmap, sprintId),
378
+ type: 'roadmap_reality',
379
+ severity: issue.type === 'error' ? 'major' : 'minor',
380
+ description: issue.message,
381
+ }));
382
+ const focus = buildRoadmapFocus(roadmap, sprintId, {
383
+ completedSprintIds,
384
+ hazards,
385
+ scorecards,
386
+ evidence: focusEvidence(roadmap, sprintId, flags, cwd),
387
+ });
388
+ if (!focus) {
389
+ console.error(`\nSprint ${formatRoadmapSprintLabel(roadmap, sprintId)} was not found in the roadmap.\n`);
390
+ process.exit(1);
391
+ return;
392
+ }
393
+ if (flags.json === 'true') {
394
+ console.log(JSON.stringify(focus, null, 2));
395
+ }
396
+ else {
397
+ console.log(formatRoadmapFocus(focus).trimEnd());
398
+ }
399
+ }
400
+ function compileSourcesSubcommand(flags, cwd) {
401
+ try {
402
+ const store = loadRoadmapSourceStore(cwd, flags.source);
403
+ const validation = validateRoadmapSourceStore(store, { checkProjection: false });
404
+ if (!validation.valid) {
405
+ throw new Error([
406
+ 'Modular roadmap sources are invalid:',
407
+ ...validation.errors.map(issue => ` - ${issue.source ? `${issue.source}: ` : ''}${issue.message}`),
408
+ 'Run `slope roadmap validate-sources` for the full report.',
409
+ ].join('\n'));
410
+ }
411
+ const existing = existsSync(store.outputPath) ? readFileSync(store.outputPath, 'utf8') : null;
412
+ const changed = existing !== store.projection;
413
+ const output = displayPath(cwd, store.outputPath);
414
+ if (flags.check === 'true') {
415
+ if (changed) {
416
+ throw new Error(`Roadmap projection drift: ${output}. Run \`slope roadmap compile\` to regenerate it from modular sources.`);
417
+ }
418
+ console.log(`\nRoadmap projection is current: ${output}\n`);
419
+ return;
420
+ }
421
+ if (flags['dry-run'] === 'true') {
422
+ console.log(`\nRoadmap compile dry run: ${changed ? 'would write' : 'already current'} ${output}`);
423
+ console.log(` Sources: ${store.sources.length}; phases: ${store.roadmap.phases.length}; sprints: ${store.roadmap.sprints.length}\n`);
424
+ return;
425
+ }
426
+ const result = writeRoadmapSourceProjection(store);
427
+ console.log(`\nRoadmap projection ${result}: ${output}`);
428
+ console.log(` Sources: ${store.sources.length}; phases: ${store.roadmap.phases.length}; sprints: ${store.roadmap.sprints.length}\n`);
429
+ }
430
+ catch (error) {
431
+ console.error(`\n${error.message}\n`);
432
+ process.exit(1);
433
+ }
434
+ }
435
+ function validateSourcesSubcommand(flags, cwd) {
436
+ const explicitSource = Object.prototype.hasOwnProperty.call(flags, 'source');
437
+ let sourceExists;
438
+ try {
439
+ sourceExists = hasModularRoadmapSources(cwd, flags.source);
440
+ }
441
+ catch (error) {
442
+ console.error(`\n${error.message}\n`);
443
+ process.exit(1);
444
+ return;
445
+ }
446
+ if (!sourceExists) {
447
+ if (explicitSource) {
448
+ console.error(`\nModular roadmap manifest not found: ${flags.source || '(empty)'}\n`);
449
+ process.exit(1);
450
+ return;
451
+ }
452
+ console.log('\nSingle-file roadmap mode; run `slope roadmap validate` for docs/backlog/roadmap.json.\n');
453
+ return;
454
+ }
455
+ let store;
456
+ try {
457
+ store = loadRoadmapSourceStore(cwd, flags.source);
458
+ }
459
+ catch (error) {
460
+ console.error(`\n${error.message}\n`);
461
+ process.exit(1);
462
+ return;
463
+ }
464
+ const validation = validateRoadmapSourceStore(store);
465
+ console.log(`\nModular roadmap sources: ${displayPath(cwd, store.manifestPath)}`);
466
+ console.log('═'.repeat(40));
467
+ if (validation.valid) {
468
+ console.log('\n✓ Modular sources and compiled projection are valid');
469
+ }
470
+ else {
471
+ console.log(`\n✗ ${validation.errors.length} source error${validation.errors.length === 1 ? '' : 's'} found`);
472
+ }
473
+ for (const issue of validation.errors) {
474
+ console.log(` ✗ ${issue.source ? `[${issue.source}] ` : ''}${issue.message}`);
475
+ }
476
+ for (const issue of validation.warnings) {
477
+ console.log(` ⚠ ${issue.source ? `[${issue.source}] ` : ''}${issue.message}`);
478
+ }
479
+ console.log(`\n Sources: ${store.sources.length}; phases: ${store.roadmap.phases.length}; sprints: ${store.roadmap.sprints.length}\n`);
480
+ if (!validation.valid)
481
+ process.exit(1);
482
+ }
483
+ function archiveSourcesSubcommand(flags, cwd) {
484
+ if (!Object.prototype.hasOwnProperty.call(flags, 'through') || flags.through === 'true') {
485
+ console.error('\nMissing required --through=N for roadmap archive.');
486
+ console.error('Usage: slope roadmap archive --through=N [--source=<file>] [--dry-run]\n');
487
+ process.exit(1);
488
+ return;
489
+ }
490
+ const through = parseSprintNumber(flags.through);
491
+ if (through == null) {
492
+ console.error(`\nInvalid archive boundary: ${flags.through || '(empty)'}\n`);
493
+ process.exit(1);
494
+ return;
495
+ }
496
+ try {
497
+ const store = loadRoadmapSourceStore(cwd, flags.source);
498
+ const projectionBefore = existsSync(store.outputPath) ? readFileSync(store.outputPath, 'utf8') : null;
499
+ const plan = planRoadmapSourceArchive(store, through);
500
+ console.log(`\nRoadmap archive through Sprint ${through}:`);
501
+ if (plan.moves.length === 0) {
502
+ console.log(' No complete live phases are eligible.\n');
503
+ return;
504
+ }
505
+ for (const move of plan.moves)
506
+ console.log(` ${move.from} -> ${move.to}`);
507
+ if (flags['dry-run'] === 'true') {
508
+ console.log('\n --dry-run: source files, manifest, and projection are unchanged.\n');
509
+ return;
510
+ }
511
+ applyRoadmapSourceArchive(store, plan);
512
+ const reloaded = loadRoadmapSourceStore(cwd, flags.source);
513
+ const validation = validateRoadmapSourceStore(reloaded);
514
+ if (!validation.valid || readFileSync(reloaded.outputPath, 'utf8') !== projectionBefore) {
515
+ throw new Error('Archive verification failed: compiled projection changed or archived evidence is invalid.');
516
+ }
517
+ console.log(`\n Archived ${plan.moves.length} phase source${plan.moves.length === 1 ? '' : 's'}; compatibility projection unchanged.\n`);
518
+ }
519
+ catch (error) {
520
+ console.error(`\n${error.message}\n`);
521
+ process.exit(1);
522
+ }
523
+ }
304
524
  function printFullRoadmapStatus(roadmap, currentSprint, completedSprints) {
305
525
  console.log(`\n# Roadmap Status — ${roadmap.name}`);
306
526
  console.log('\u2550'.repeat(40));
@@ -479,7 +699,35 @@ function mergeScorecardTickets(existingTickets, scorecardTickets) {
479
699
  return existing ? { ...existing, ...ticket } : ticket;
480
700
  });
481
701
  }
702
+ function modularAuthorityBlocksProjectionMutation(flags, cwd, action) {
703
+ let defaultExists = false;
704
+ let explicitExists = false;
705
+ try {
706
+ defaultExists = hasModularRoadmapSources(cwd);
707
+ if (!defaultExists && Object.prototype.hasOwnProperty.call(flags, 'source')) {
708
+ explicitExists = hasModularRoadmapSources(cwd, flags.source);
709
+ }
710
+ }
711
+ catch (error) {
712
+ console.error(`\n${error.message}\n`);
713
+ process.exit(1);
714
+ return true;
715
+ }
716
+ if (!defaultExists && Object.prototype.hasOwnProperty.call(flags, 'source') && !explicitExists) {
717
+ console.error(`\nModular roadmap manifest not found: ${flags.source || '(empty)'}\n`);
718
+ process.exit(1);
719
+ return true;
720
+ }
721
+ if (!defaultExists && !explicitExists)
722
+ return false;
723
+ console.error(`\nModular roadmap sources are authoritative; \`roadmap ${action}\` cannot edit the generated projection.`);
724
+ console.error('Update the source YAML and run `slope roadmap compile`.\n');
725
+ process.exit(1);
726
+ return true;
727
+ }
482
728
  function syncSubcommand(flags, cwd) {
729
+ if (modularAuthorityBlocksProjectionMutation(flags, cwd, 'sync'))
730
+ return;
483
731
  const dryRun = flags['dry-run'] === 'true';
484
732
  const path = resolveRoadmapPath(flags, cwd);
485
733
  const config = loadConfig(cwd);
@@ -546,6 +794,8 @@ function syncSubcommand(flags, cwd) {
546
794
  console.log(`\n Written to ${path}\n`);
547
795
  }
548
796
  async function generateSubcommand(flags, cwd) {
797
+ if (modularAuthorityBlocksProjectionMutation(flags, cwd, 'generate'))
798
+ return;
549
799
  const vision = loadVision(cwd);
550
800
  if (!vision) {
551
801
  console.error('\nNo vision found. Create one first:');
@@ -635,6 +885,18 @@ export async function roadmapCommand(args) {
635
885
  case 'status':
636
886
  statusSubcommand(flags, cwd);
637
887
  break;
888
+ case 'focus':
889
+ focusSubcommand(flags, cwd);
890
+ break;
891
+ case 'compile':
892
+ compileSourcesSubcommand(flags, cwd);
893
+ break;
894
+ case 'validate-sources':
895
+ validateSourcesSubcommand(flags, cwd);
896
+ break;
897
+ case 'archive':
898
+ archiveSourcesSubcommand(flags, cwd);
899
+ break;
638
900
  case 'show':
639
901
  showSubcommand(flags, cwd);
640
902
  break;
@@ -656,13 +918,20 @@ Usage:
656
918
  slope roadmap validate [--path=<file>] Schema + dependency graph checks
657
919
  slope roadmap review [--path=<file>] Automated architect review
658
920
  slope roadmap status [--path=<file>] [--sprint=N] [--full] Compact current progress
921
+ slope roadmap focus --sprint=N [--path=<file>] [--json] Bounded sprint context
922
+ slope roadmap compile [--source=<file>] [--dry-run|--check] Compile modular YAML sources
923
+ slope roadmap validate-sources [--source=<file>] Validate sources and projection drift
924
+ slope roadmap archive --through=N [--source=<file>] [--dry-run] Archive whole terminal phases
659
925
  slope roadmap show [--path=<file>] Render summary (critical path, parallel tracks)
660
926
  slope roadmap sync [--path=<file>] [--dry-run] Sync scorecards into roadmap
661
927
  slope roadmap generate [--path=<file>] [--dry-run] Generate from vision + concrete backlog signals
662
928
 
663
929
  Options:
664
930
  --path=<file> Path to roadmap JSON (default: docs/backlog/roadmap.json)
665
- --sprint=N Override current sprint number (for status)
931
+ --sprint=N Select a sprint (required for focus; override for status)
932
+ --json Emit machine-readable focus JSON
933
+ --source=<file> Modular roadmap manifest (default: docs/roadmap/project.yaml)
934
+ --check Fail when the compiled roadmap projection has drifted
666
935
  --full Show full roadmap history for status
667
936
  --dry-run Show what would change without writing (for sync and generate)
668
937