@slope-dev/slope 1.59.2 → 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.
- package/README.md +6 -0
- package/dist/cli/actor.d.ts +18 -0
- package/dist/cli/actor.d.ts.map +1 -0
- package/dist/cli/actor.js +133 -0
- package/dist/cli/actor.js.map +1 -0
- package/dist/cli/commands/agent.d.ts.map +1 -1
- package/dist/cli/commands/agent.js +10 -8
- package/dist/cli/commands/agent.js.map +1 -1
- package/dist/cli/commands/briefing.d.ts.map +1 -1
- package/dist/cli/commands/briefing.js +12 -1
- package/dist/cli/commands/briefing.js.map +1 -1
- package/dist/cli/commands/claim.d.ts.map +1 -1
- package/dist/cli/commands/claim.js +10 -6
- package/dist/cli/commands/claim.js.map +1 -1
- package/dist/cli/commands/commit-ready.d.ts.map +1 -1
- package/dist/cli/commands/commit-ready.js +4 -2
- package/dist/cli/commands/commit-ready.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +6 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/interview.d.ts.map +1 -1
- package/dist/cli/commands/interview.js +5 -0
- package/dist/cli/commands/interview.js.map +1 -1
- package/dist/cli/commands/next.d.ts +1 -1
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +31 -2
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/now.d.ts +3 -0
- package/dist/cli/commands/now.d.ts.map +1 -1
- package/dist/cli/commands/now.js +14 -2
- package/dist/cli/commands/now.js.map +1 -1
- package/dist/cli/commands/release.d.ts.map +1 -1
- package/dist/cli/commands/release.js +7 -3
- package/dist/cli/commands/release.js.map +1 -1
- package/dist/cli/commands/retro.d.ts.map +1 -1
- package/dist/cli/commands/retro.js +27 -6
- package/dist/cli/commands/retro.js.map +1 -1
- package/dist/cli/commands/review-run.d.ts +2 -0
- package/dist/cli/commands/review-run.d.ts.map +1 -1
- package/dist/cli/commands/review-run.js +38 -8
- package/dist/cli/commands/review-run.js.map +1 -1
- package/dist/cli/commands/review-state.d.ts +2 -5
- package/dist/cli/commands/review-state.d.ts.map +1 -1
- package/dist/cli/commands/review-state.js +211 -47
- package/dist/cli/commands/review-state.js.map +1 -1
- package/dist/cli/commands/review.d.ts +9 -1
- package/dist/cli/commands/review.d.ts.map +1 -1
- package/dist/cli/commands/review.js +67 -39
- package/dist/cli/commands/review.js.map +1 -1
- package/dist/cli/commands/roadmap.d.ts.map +1 -1
- package/dist/cli/commands/roadmap.js +291 -9
- package/dist/cli/commands/roadmap.js.map +1 -1
- package/dist/cli/commands/sprint.d.ts.map +1 -1
- package/dist/cli/commands/sprint.js +594 -68
- package/dist/cli/commands/sprint.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +20 -4
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/ticket.d.ts.map +1 -1
- package/dist/cli/commands/ticket.js +31 -9
- package/dist/cli/commands/ticket.js.map +1 -1
- package/dist/cli/commands/version.d.ts +12 -0
- package/dist/cli/commands/version.d.ts.map +1 -1
- package/dist/cli/commands/version.js +169 -4
- package/dist/cli/commands/version.js.map +1 -1
- package/dist/cli/git-preflight.d.ts +9 -0
- package/dist/cli/git-preflight.d.ts.map +1 -0
- package/dist/cli/git-preflight.js +36 -0
- package/dist/cli/git-preflight.js.map +1 -0
- package/dist/cli/guards/claim-required.d.ts.map +1 -1
- package/dist/cli/guards/claim-required.js +19 -4
- package/dist/cli/guards/claim-required.js.map +1 -1
- package/dist/cli/guards/next-action.js +1 -1
- package/dist/cli/guards/next-action.js.map +1 -1
- package/dist/cli/guards/phase-boundary.js +60 -10
- package/dist/cli/guards/phase-boundary.js.map +1 -1
- package/dist/cli/guards/post-push.d.ts.map +1 -1
- package/dist/cli/guards/post-push.js +8 -2
- package/dist/cli/guards/post-push.js.map +1 -1
- package/dist/cli/guards/pr-review.d.ts +6 -0
- package/dist/cli/guards/pr-review.d.ts.map +1 -1
- package/dist/cli/guards/pr-review.js +22 -11
- package/dist/cli/guards/pr-review.js.map +1 -1
- package/dist/cli/guards/review-tier.d.ts.map +1 -1
- package/dist/cli/guards/review-tier.js +2 -3
- package/dist/cli/guards/review-tier.js.map +1 -1
- package/dist/cli/guards/roadmap-edit-shipped.d.ts.map +1 -1
- package/dist/cli/guards/roadmap-edit-shipped.js +143 -7
- package/dist/cli/guards/roadmap-edit-shipped.js.map +1 -1
- package/dist/cli/guards/scope-drift.d.ts.map +1 -1
- package/dist/cli/guards/scope-drift.js +8 -2
- package/dist/cli/guards/scope-drift.js.map +1 -1
- package/dist/cli/guards/session-briefing.d.ts.map +1 -1
- package/dist/cli/guards/session-briefing.js +24 -2
- package/dist/cli/guards/session-briefing.js.map +1 -1
- package/dist/cli/guards/sprint-completion.d.ts.map +1 -1
- package/dist/cli/guards/sprint-completion.js +107 -18
- package/dist/cli/guards/sprint-completion.js.map +1 -1
- package/dist/cli/guards/workflow-step-gate.d.ts.map +1 -1
- package/dist/cli/guards/workflow-step-gate.js +25 -6
- package/dist/cli/guards/workflow-step-gate.js.map +1 -1
- package/dist/cli/index.js +4 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/registry.js +45 -3
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli/reviewer-agents.d.ts +22 -0
- package/dist/cli/reviewer-agents.d.ts.map +1 -0
- package/dist/cli/reviewer-agents.js +141 -0
- package/dist/cli/reviewer-agents.js.map +1 -0
- package/dist/cli/roadmap-source-store.d.ts +37 -0
- package/dist/cli/roadmap-source-store.d.ts.map +1 -0
- package/dist/cli/roadmap-source-store.js +298 -0
- package/dist/cli/roadmap-source-store.js.map +1 -0
- package/dist/cli/sprint-inference.js +1 -1
- package/dist/cli/sprint-inference.js.map +1 -1
- package/dist/cli/sprint-rollover.d.ts +102 -0
- package/dist/cli/sprint-rollover.d.ts.map +1 -0
- package/dist/cli/sprint-rollover.js +558 -0
- package/dist/cli/sprint-rollover.js.map +1 -0
- package/dist/cli/sprint-state.d.ts +99 -2
- package/dist/cli/sprint-state.d.ts.map +1 -1
- package/dist/cli/sprint-state.js +349 -14
- package/dist/cli/sprint-state.js.map +1 -1
- package/dist/cli/template-generator.d.ts.map +1 -1
- package/dist/cli/template-generator.js +45 -32
- package/dist/cli/template-generator.js.map +1 -1
- package/dist/cli/workflow-resync.d.ts +2 -0
- package/dist/cli/workflow-resync.d.ts.map +1 -1
- package/dist/cli/workflow-resync.js +34 -12
- package/dist/cli/workflow-resync.js.map +1 -1
- package/dist/core/analyzers/git.d.ts.map +1 -1
- package/dist/core/analyzers/git.js +95 -5
- package/dist/core/analyzers/git.js.map +1 -1
- package/dist/core/briefing.d.ts.map +1 -1
- package/dist/core/briefing.js +254 -14
- package/dist/core/briefing.js.map +1 -1
- package/dist/core/builder.d.ts +16 -2
- package/dist/core/builder.d.ts.map +1 -1
- package/dist/core/builder.js +82 -5
- package/dist/core/builder.js.map +1 -1
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/constants.js +1 -0
- package/dist/core/constants.js.map +1 -1
- package/dist/core/findings.d.ts +20 -0
- package/dist/core/findings.d.ts.map +1 -0
- package/dist/core/findings.js +85 -0
- package/dist/core/findings.js.map +1 -0
- package/dist/core/generators/first-sprint.js +1 -1
- package/dist/core/generators/first-sprint.js.map +1 -1
- package/dist/core/generators/roadmap.js +3 -3
- package/dist/core/generators/roadmap.js.map +1 -1
- package/dist/core/index.d.ts +10 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +5 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/interview-engine.d.ts.map +1 -1
- package/dist/core/interview-engine.js +5 -4
- package/dist/core/interview-engine.js.map +1 -1
- package/dist/core/interview-metaphor.d.ts +5 -0
- package/dist/core/interview-metaphor.d.ts.map +1 -0
- package/dist/core/interview-metaphor.js +20 -0
- package/dist/core/interview-metaphor.js.map +1 -0
- package/dist/core/interview-steps.d.ts.map +1 -1
- package/dist/core/interview-steps.js +3 -6
- package/dist/core/interview-steps.js.map +1 -1
- package/dist/core/interview.d.ts.map +1 -1
- package/dist/core/interview.js +5 -6
- package/dist/core/interview.js.map +1 -1
- package/dist/core/roadmap-focus.d.ts +101 -0
- package/dist/core/roadmap-focus.d.ts.map +1 -0
- package/dist/core/roadmap-focus.js +293 -0
- package/dist/core/roadmap-focus.js.map +1 -0
- package/dist/core/roadmap-sources.d.ts +52 -0
- package/dist/core/roadmap-sources.d.ts.map +1 -0
- package/dist/core/roadmap-sources.js +483 -0
- package/dist/core/roadmap-sources.js.map +1 -0
- package/dist/core/roadmap.d.ts +25 -1
- package/dist/core/roadmap.d.ts.map +1 -1
- package/dist/core/roadmap.js +62 -45
- package/dist/core/roadmap.js.map +1 -1
- package/dist/core/types.d.ts +11 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/workflow-engine.d.ts.map +1 -1
- package/dist/core/workflow-engine.js +17 -2
- package/dist/core/workflow-engine.js.map +1 -1
- package/dist/mcp/registry.d.ts +1 -1
- package/dist/mcp/registry.d.ts.map +1 -1
- package/dist/mcp/registry.js +5 -4
- package/dist/mcp/registry.js.map +1 -1
- package/dist/store/index.d.ts +13 -0
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +60 -7
- package/dist/store/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/workflows/sprint-standard.yaml +7 -1
- package/templates/claude-code/CLAUDE.md +3 -1
- package/templates/claude-code/commands/post-sprint.md +2 -0
- package/templates/claude-code/commands/start-sprint.md +10 -5
- package/templates/claude-code/rules/sprint-checklist.md +13 -10
- package/templates/codex/plugins/slope/.codex-plugin/plugin.json +1 -1
- package/templates/codex/plugins/slope/skills/slope-retro/SKILL.md +1 -1
- package/templates/codex/plugins/slope/skills/slope-sprint/SKILL.md +5 -4
- package/templates/cursor/rules/slope-sprint-checklist.mdc +11 -8
- package/templates/generic/SLOPE-CHECKLIST.md +13 -9
- package/templates/pi/skills/post-sprint.md +2 -0
- package/templates/pi/skills/start-sprint.md +10 -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
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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(
|
|
63
|
+
raw = JSON.parse(readFileSync(scorecardPath, 'utf8'));
|
|
44
64
|
}
|
|
45
65
|
catch {
|
|
46
|
-
console.error(`\nFailed to parse ${
|
|
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(
|
|
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.
|
|
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":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
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":"
|
|
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));
|
|
@@ -469,7 +689,45 @@ function scorecardToSprint(card) {
|
|
|
469
689
|
tickets,
|
|
470
690
|
};
|
|
471
691
|
}
|
|
692
|
+
function ticketIdentity(ticket) {
|
|
693
|
+
return ticket.key || ticket.id || '';
|
|
694
|
+
}
|
|
695
|
+
function mergeScorecardTickets(existingTickets, scorecardTickets) {
|
|
696
|
+
const existingByKey = new Map(existingTickets.map(ticket => [ticketIdentity(ticket), ticket]));
|
|
697
|
+
return scorecardTickets.map(ticket => {
|
|
698
|
+
const existing = existingByKey.get(ticketIdentity(ticket));
|
|
699
|
+
return existing ? { ...existing, ...ticket } : ticket;
|
|
700
|
+
});
|
|
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
|
+
}
|
|
472
728
|
function syncSubcommand(flags, cwd) {
|
|
729
|
+
if (modularAuthorityBlocksProjectionMutation(flags, cwd, 'sync'))
|
|
730
|
+
return;
|
|
473
731
|
const dryRun = flags['dry-run'] === 'true';
|
|
474
732
|
const path = resolveRoadmapPath(flags, cwd);
|
|
475
733
|
const config = loadConfig(cwd);
|
|
@@ -508,12 +766,13 @@ function syncSubcommand(flags, cwd) {
|
|
|
508
766
|
existing.par = fromCard.par;
|
|
509
767
|
existing.slope = fromCard.slope;
|
|
510
768
|
existing.type = fromCard.type;
|
|
511
|
-
existing.tickets = fromCard.tickets;
|
|
512
|
-
|
|
769
|
+
existing.tickets = mergeScorecardTickets(existing.tickets, fromCard.tickets);
|
|
770
|
+
existing.status = 'complete';
|
|
771
|
+
// Preserve: sprint depends_on and ticket metadata such as depends_on/github_issue.
|
|
513
772
|
updated++;
|
|
514
773
|
}
|
|
515
774
|
else {
|
|
516
|
-
roadmap.sprints.push(fromCard);
|
|
775
|
+
roadmap.sprints.push({ ...fromCard, status: 'complete' });
|
|
517
776
|
added++;
|
|
518
777
|
}
|
|
519
778
|
}
|
|
@@ -535,6 +794,8 @@ function syncSubcommand(flags, cwd) {
|
|
|
535
794
|
console.log(`\n Written to ${path}\n`);
|
|
536
795
|
}
|
|
537
796
|
async function generateSubcommand(flags, cwd) {
|
|
797
|
+
if (modularAuthorityBlocksProjectionMutation(flags, cwd, 'generate'))
|
|
798
|
+
return;
|
|
538
799
|
const vision = loadVision(cwd);
|
|
539
800
|
if (!vision) {
|
|
540
801
|
console.error('\nNo vision found. Create one first:');
|
|
@@ -624,6 +885,18 @@ export async function roadmapCommand(args) {
|
|
|
624
885
|
case 'status':
|
|
625
886
|
statusSubcommand(flags, cwd);
|
|
626
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;
|
|
627
900
|
case 'show':
|
|
628
901
|
showSubcommand(flags, cwd);
|
|
629
902
|
break;
|
|
@@ -641,23 +914,32 @@ export async function roadmapCommand(args) {
|
|
|
641
914
|
slope roadmap — Strategic planning tools
|
|
642
915
|
|
|
643
916
|
Usage:
|
|
644
|
-
slope roadmap interview [--agent] [--force]
|
|
917
|
+
slope roadmap interview [--agent] [--force] [--allow-no-git] Run project interview for planning input
|
|
645
918
|
slope roadmap validate [--path=<file>] Schema + dependency graph checks
|
|
646
919
|
slope roadmap review [--path=<file>] Automated architect review
|
|
647
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
|
|
648
925
|
slope roadmap show [--path=<file>] Render summary (critical path, parallel tracks)
|
|
649
926
|
slope roadmap sync [--path=<file>] [--dry-run] Sync scorecards into roadmap
|
|
650
927
|
slope roadmap generate [--path=<file>] [--dry-run] Generate from vision + concrete backlog signals
|
|
651
928
|
|
|
652
929
|
Options:
|
|
653
930
|
--path=<file> Path to roadmap JSON (default: docs/backlog/roadmap.json)
|
|
654
|
-
--sprint=N
|
|
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
|
|
655
935
|
--full Show full roadmap history for status
|
|
656
936
|
--dry-run Show what would change without writing (for sync and generate)
|
|
657
937
|
|
|
658
938
|
Interview delegates to \`slope interview\`. Use \`--agent\` for JSON I/O, or
|
|
659
939
|
\`slope vision create/update\` plus \`slope roadmap generate\` when you already
|
|
660
940
|
have planning answers.
|
|
941
|
+
Use \`--allow-no-git\` only for degraded projects where commit-backed completion
|
|
942
|
+
evidence is unavailable.
|
|
661
943
|
|
|
662
944
|
Generate requires concrete backlog signals from source TODO/FIXME/HACK comments
|
|
663
945
|
or synced issue data. It fails instead of creating placeholder planning tickets
|