@topogram/cli 0.3.71 → 0.3.73
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 +24 -195
- package/package.json +1 -1
- package/src/adoption/plan/index.js +2 -1
- package/src/agent-brief.js +46 -2
- package/src/archive/archive.js +1 -1
- package/src/archive/jsonl.js +18 -8
- package/src/archive/resolver-bridge.js +34 -1
- package/src/archive/schema.js +1 -1
- package/src/archive/unarchive.js +26 -0
- package/src/cli/command-parsers/project.js +0 -3
- package/src/cli/command-parsers/sdlc.js +66 -0
- package/src/cli/commands/import/help.js +1 -0
- package/src/cli/commands/import/plan.js +9 -0
- package/src/cli/commands/import/workspace.js +3 -0
- package/src/cli/commands/query/definitions.js +11 -10
- package/src/cli/commands/query/workspace.js +23 -2
- package/src/cli/commands/sdlc.js +213 -5
- package/src/cli/dispatcher.js +8 -5
- package/src/cli/help.js +14 -3
- package/src/cli/migration-guidance.js +3 -0
- package/src/cli/options.js +1 -0
- package/src/generator/context/shared/domain-sdlc.js +27 -0
- package/src/generator/context/shared/relationships.js +2 -1
- package/src/generator/context/shared/types.d.ts +1 -0
- package/src/generator/context/shared.d.ts +2 -0
- package/src/generator/context/shared.js +2 -0
- package/src/generator/context/slice/core.js +3 -0
- package/src/generator/context/slice/sdlc.js +57 -2
- package/src/generator/context/task-mode.js +7 -0
- package/src/generator/sdlc/board.js +2 -0
- package/src/generator/sdlc/traceability-matrix.js +5 -1
- package/src/generator/surfaces/databases/lifecycle-shared.js +2 -2
- package/src/import/core/context.js +1 -1
- package/src/import/core/contracts.js +3 -3
- package/src/import/core/registry.js +3 -0
- package/src/import/core/runner/candidates.js +7 -0
- package/src/import/core/runner/reports.js +9 -1
- package/src/import/core/runner/tracks.js +3 -0
- package/src/import/extractors/cli/generic.js +340 -0
- package/src/new-project/project-files.js +10 -3
- package/src/resolver/enrich/task.js +3 -1
- package/src/resolver/index.js +6 -0
- package/src/resolver/normalize.js +31 -0
- package/src/resolver/projections-cli.js +158 -0
- package/src/sdlc/adopt.js +4 -1
- package/src/sdlc/check.js +24 -2
- package/src/sdlc/complete.js +47 -0
- package/src/sdlc/dod/index.js +2 -0
- package/src/sdlc/dod/plan.js +15 -0
- package/src/sdlc/dod/task.js +7 -3
- package/src/sdlc/explain.js +53 -1
- package/src/sdlc/gate.js +352 -0
- package/src/sdlc/history.d.ts +7 -0
- package/src/sdlc/history.js +50 -5
- package/src/sdlc/link.js +172 -0
- package/src/sdlc/paths.d.ts +4 -0
- package/src/sdlc/paths.js +8 -0
- package/src/sdlc/plan-steps.js +71 -0
- package/src/sdlc/plan.js +245 -0
- package/src/sdlc/policy.js +249 -0
- package/src/sdlc/prep.js +186 -0
- package/src/sdlc/scaffold.js +4 -2
- package/src/sdlc/status-filter.js +2 -0
- package/src/sdlc/transitions/index.js +3 -0
- package/src/sdlc/transitions/plan.js +32 -0
- package/src/validator/common.js +25 -4
- package/src/validator/index.js +10 -0
- package/src/validator/kinds.d.ts +7 -0
- package/src/validator/kinds.js +32 -0
- package/src/validator/per-kind/plan.js +128 -0
- package/src/validator/per-kind/task.js +19 -0
- package/src/validator/projections/cli.js +267 -0
- package/src/validator.d.ts +1 -0
- package/src/workflows/import-app/shared.js +1 -1
- package/src/workflows/reconcile/adoption-plan/build.js +3 -1
- package/src/workflows/reconcile/adoption-plan/reasons.js +5 -0
- package/src/workflows/reconcile/bundle-core/index.js +3 -0
- package/src/workflows/reconcile/candidate-model.js +15 -0
- package/src/workflows/reconcile/gap-report.js +4 -2
- package/src/workflows/reconcile/impacts/adoption-plan.js +13 -0
- package/src/workflows/reconcile/renderers.js +82 -0
- package/src/workflows/reconcile/summary.js +4 -0
- package/src/workflows/reconcile/workflow.js +2 -1
- package/src/workspace-paths.js +34 -3
- package/src/cli/commands/migrate.js +0 -153
|
@@ -31,6 +31,7 @@ export function printImportHelp() {
|
|
|
31
31
|
console.log("Examples:");
|
|
32
32
|
console.log(" topogram import ./existing-app --out ./imported-topogram");
|
|
33
33
|
console.log(" topogram import ./existing-app --out ./imported-topogram --from db,api,ui");
|
|
34
|
+
console.log(" topogram import ./existing-cli --out ./imported-topogram --from cli");
|
|
34
35
|
console.log(" topogram import diff ./imported-topogram");
|
|
35
36
|
console.log(" topogram import refresh ./imported-topogram --from ./existing-app --dry-run");
|
|
36
37
|
console.log(" topogram import refresh ./imported-topogram --from ./existing-app");
|
|
@@ -40,6 +40,15 @@ export const BROWNFIELD_BROAD_ADOPT_SELECTORS = [
|
|
|
40
40
|
},
|
|
41
41
|
{ selector: "workflows", kind: "track", label: "workflows", matches: (/** @type {AnyRecord} */ item) => item.track === "workflows" || item.kind === "decision" },
|
|
42
42
|
{ selector: "verification", kind: "kind", label: "verification", matches: (/** @type {AnyRecord} */ item) => item.kind === "verification" },
|
|
43
|
+
{
|
|
44
|
+
selector: "cli",
|
|
45
|
+
kind: "track",
|
|
46
|
+
label: "CLI surfaces",
|
|
47
|
+
matches: (/** @type {AnyRecord} */ item) =>
|
|
48
|
+
item.bundle === "cli" ||
|
|
49
|
+
item.track === "cli" ||
|
|
50
|
+
item.suggested_action === "promote_cli_surface"
|
|
51
|
+
},
|
|
43
52
|
{
|
|
44
53
|
selector: "ui",
|
|
45
54
|
kind: "track",
|
|
@@ -116,6 +116,9 @@ export function importCandidateCounts(summary) {
|
|
|
116
116
|
uiRoutes: candidates.ui?.routes?.length || 0,
|
|
117
117
|
uiWidgets: candidates.ui?.widgets?.length || candidates.ui?.components?.length || 0,
|
|
118
118
|
uiShapes: candidates.ui?.shapes?.length || 0,
|
|
119
|
+
cliCommands: candidates.cli?.commands?.length || 0,
|
|
120
|
+
cliCapabilities: candidates.cli?.capabilities?.length || 0,
|
|
121
|
+
cliSurfaces: candidates.cli?.surfaces?.length || 0,
|
|
119
122
|
workflows: candidates.workflows?.workflows?.length || 0,
|
|
120
123
|
verifications: candidates.verification?.verifications?.length || 0
|
|
121
124
|
};
|
|
@@ -32,21 +32,22 @@
|
|
|
32
32
|
* @returns {QueryDefinition[]}
|
|
33
33
|
*/
|
|
34
34
|
export function queryDefinitions() {
|
|
35
|
+
const contextSelectors = ["mode", "capability", "workflow", "projection", "widget", "entity", "journey", "surface", "domain", "pitch", "requirement", "acceptance", "task", "plan", "bug", "document", "from-topogram"];
|
|
35
36
|
return [
|
|
36
37
|
{
|
|
37
38
|
name: "slice",
|
|
38
39
|
purpose: "Give an agent the smallest graph slice needed to reason about one selected semantic surface.",
|
|
39
40
|
description: "Return a focused semantic context slice for one selected surface.",
|
|
40
|
-
selectors: ["capability", "workflow", "projection", "widget", "entity", "journey", "domain"],
|
|
41
|
+
selectors: ["capability", "workflow", "projection", "widget", "entity", "journey", "domain", "pitch", "requirement", "acceptance", "task", "plan", "bug", "document"],
|
|
41
42
|
args: ["[path]", "[selectors]", "[--json]"],
|
|
42
43
|
output: "context_slice",
|
|
43
|
-
example: "topogram query slice ./topo --
|
|
44
|
+
example: "topogram query slice ./topo --task task_implement_audit_writer"
|
|
44
45
|
},
|
|
45
46
|
{
|
|
46
47
|
name: "verification-targets",
|
|
47
48
|
purpose: "Map a selected change or mode to the smallest verification set worth running.",
|
|
48
49
|
description: "Return the smallest verification target set for a mode, selector, or diff.",
|
|
49
|
-
selectors:
|
|
50
|
+
selectors: contextSelectors.filter((selector) => selector !== "surface" && selector !== "domain" && selector !== "pitch" && selector !== "requirement" && selector !== "acceptance" && selector !== "plan" && selector !== "bug" && selector !== "document"),
|
|
50
51
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
51
52
|
output: "verification_targets",
|
|
52
53
|
example: "topogram query verification-targets ./topo --widget widget_data_grid"
|
|
@@ -64,7 +65,7 @@ export function queryDefinitions() {
|
|
|
64
65
|
name: "change-plan",
|
|
65
66
|
purpose: "Summarize what a selected change affects before code or Topogram edits start.",
|
|
66
67
|
description: "Return the semantic change plan, generator targets, risk, and alignment recommendations.",
|
|
67
|
-
selectors:
|
|
68
|
+
selectors: contextSelectors,
|
|
68
69
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
69
70
|
output: "change_plan_query",
|
|
70
71
|
example: "topogram query change-plan ./topo --widget widget_data_grid"
|
|
@@ -73,7 +74,7 @@ export function queryDefinitions() {
|
|
|
73
74
|
name: "review-packet",
|
|
74
75
|
purpose: "Bundle the context a human or agent needs to review a selected semantic change.",
|
|
75
76
|
description: "Return the review packet for a selected change or diff.",
|
|
76
|
-
selectors:
|
|
77
|
+
selectors: contextSelectors,
|
|
77
78
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
78
79
|
output: "review_packet_query",
|
|
79
80
|
example: "topogram query review-packet ./topo --widget widget_data_grid"
|
|
@@ -82,7 +83,7 @@ export function queryDefinitions() {
|
|
|
82
83
|
name: "resolved-workflow-context",
|
|
83
84
|
purpose: "Resolve workflow guidance and artifact load order for a selected mode or change.",
|
|
84
85
|
description: "Return resolved workflow guidance, artifact load order, preset policy, and recommended artifact queries.",
|
|
85
|
-
selectors: [
|
|
86
|
+
selectors: [...contextSelectors.filter((selector) => selector !== "document"), "provider", "preset"],
|
|
86
87
|
args: ["[path]", "[--mode <id>]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
87
88
|
output: "resolved_workflow_context_query",
|
|
88
89
|
example: "topogram query resolved-workflow-context ./topo --mode modeling --widget widget_data_grid --json"
|
|
@@ -91,7 +92,7 @@ export function queryDefinitions() {
|
|
|
91
92
|
name: "single-agent-plan",
|
|
92
93
|
purpose: "Give one coding agent a bounded plan, artifact set, and write guidance.",
|
|
93
94
|
description: "Return a single-agent operating plan for a mode and optional selector.",
|
|
94
|
-
selectors:
|
|
95
|
+
selectors: contextSelectors.filter((selector) => selector !== "document"),
|
|
95
96
|
args: ["[path]", "[--mode <id>]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
96
97
|
output: "single_agent_plan_query",
|
|
97
98
|
example: "topogram query single-agent-plan ./topo --mode modeling --widget widget_data_grid --json"
|
|
@@ -100,7 +101,7 @@ export function queryDefinitions() {
|
|
|
100
101
|
name: "risk-summary",
|
|
101
102
|
purpose: "Surface behavioral, ownership, and verification risks for a selected change.",
|
|
102
103
|
description: "Return the risk summary for a selected change, mode, or diff.",
|
|
103
|
-
selectors:
|
|
104
|
+
selectors: contextSelectors,
|
|
104
105
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
105
106
|
output: "risk_summary_query",
|
|
106
107
|
example: "topogram query risk-summary ./topo --widget widget_data_grid"
|
|
@@ -109,7 +110,7 @@ export function queryDefinitions() {
|
|
|
109
110
|
name: "proceed-decision",
|
|
110
111
|
purpose: "Tell a human or agent whether enough context and proof exist to proceed.",
|
|
111
112
|
description: "Return a proceed/no-go decision for the current selected work.",
|
|
112
|
-
selectors:
|
|
113
|
+
selectors: contextSelectors,
|
|
113
114
|
args: ["[path]", "[--mode <id>]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
114
115
|
output: "proceed_decision_query",
|
|
115
116
|
example: "topogram query proceed-decision ./topo --mode verification"
|
|
@@ -118,7 +119,7 @@ export function queryDefinitions() {
|
|
|
118
119
|
name: "write-scope",
|
|
119
120
|
purpose: "Define where an agent may edit for a selected semantic surface.",
|
|
120
121
|
description: "Return safe edit boundaries for a selected mode or semantic surface.",
|
|
121
|
-
selectors:
|
|
122
|
+
selectors: contextSelectors.filter((selector) => selector !== "surface" && selector !== "document"),
|
|
122
123
|
args: ["[path]", "[selectors]", "[--from-topogram <path>]", "[--json]"],
|
|
123
124
|
output: "write_scope_query",
|
|
124
125
|
example: "topogram query write-scope ./topo --widget widget_data_grid"
|
|
@@ -74,6 +74,13 @@ export function importAdoptOnlyRequested(options = {}) {
|
|
|
74
74
|
options.journeyId ||
|
|
75
75
|
options.surfaceId ||
|
|
76
76
|
options.domainId ||
|
|
77
|
+
options.pitchId ||
|
|
78
|
+
options.requirementId ||
|
|
79
|
+
options.acceptanceId ||
|
|
80
|
+
options.taskId ||
|
|
81
|
+
options.planId ||
|
|
82
|
+
options.bugId ||
|
|
83
|
+
options.documentId ||
|
|
77
84
|
options.fromTopogramPath
|
|
78
85
|
);
|
|
79
86
|
}
|
|
@@ -202,7 +209,14 @@ export function hasSelectors(options) {
|
|
|
202
209
|
options.entityId ||
|
|
203
210
|
options.journeyId ||
|
|
204
211
|
options.surfaceId ||
|
|
205
|
-
options.domainId
|
|
212
|
+
options.domainId ||
|
|
213
|
+
options.pitchId ||
|
|
214
|
+
options.requirementId ||
|
|
215
|
+
options.acceptanceId ||
|
|
216
|
+
options.taskId ||
|
|
217
|
+
options.planId ||
|
|
218
|
+
options.bugId ||
|
|
219
|
+
options.documentId
|
|
206
220
|
);
|
|
207
221
|
}
|
|
208
222
|
|
|
@@ -228,7 +242,14 @@ export function selectorOptions(options) {
|
|
|
228
242
|
entityId: options.entityId,
|
|
229
243
|
journeyId: options.journeyId,
|
|
230
244
|
surfaceId: options.surfaceId,
|
|
231
|
-
domainId: options.domainId
|
|
245
|
+
domainId: options.domainId,
|
|
246
|
+
pitchId: options.pitchId,
|
|
247
|
+
requirementId: options.requirementId,
|
|
248
|
+
acceptanceId: options.acceptanceId,
|
|
249
|
+
taskId: options.taskId,
|
|
250
|
+
planId: options.planId,
|
|
251
|
+
bugId: options.bugId,
|
|
252
|
+
documentId: options.documentId
|
|
232
253
|
};
|
|
233
254
|
}
|
|
234
255
|
|
package/src/cli/commands/sdlc.js
CHANGED
|
@@ -6,6 +6,7 @@ import { stableStringify } from "../../format.js";
|
|
|
6
6
|
import { parsePath } from "../../parser.js";
|
|
7
7
|
import { resolveWorkspace } from "../../resolver.js";
|
|
8
8
|
import { formatValidationErrors } from "../../validator.js";
|
|
9
|
+
import { resolveTopoRoot } from "../../workspace-paths.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @typedef {Record<string, any>} AnyRecord
|
|
@@ -21,6 +22,23 @@ function flagValue(args, flag) {
|
|
|
21
22
|
return index >= 0 ? args[index + 1] || null : null;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @param {string[]} args
|
|
27
|
+
* @param {string} flag
|
|
28
|
+
* @returns {string[]}
|
|
29
|
+
*/
|
|
30
|
+
function flagValues(args, flag) {
|
|
31
|
+
/** @type {string[]} */
|
|
32
|
+
const values = [];
|
|
33
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
34
|
+
if (args[index] === flag && args[index + 1] && !args[index + 1].startsWith("-")) {
|
|
35
|
+
values.push(args[index + 1]);
|
|
36
|
+
index += 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return values;
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
/**
|
|
25
43
|
* @param {string[]} args
|
|
26
44
|
* @returns {boolean}
|
|
@@ -29,6 +47,65 @@ function includeHistory(args) {
|
|
|
29
47
|
return args.includes("--history") || args.includes("--include-history");
|
|
30
48
|
}
|
|
31
49
|
|
|
50
|
+
/**
|
|
51
|
+
* @param {Record<string, any>} payload
|
|
52
|
+
* @returns {void}
|
|
53
|
+
*/
|
|
54
|
+
function printPolicyExplain(payload) {
|
|
55
|
+
console.log("Topogram SDLC policy");
|
|
56
|
+
console.log(`Status: ${payload.policy?.status || "not_adopted"}`);
|
|
57
|
+
console.log(`Mode: ${payload.policy?.mode || "none"}`);
|
|
58
|
+
console.log(`Policy: ${payload.policy?.path || "missing"}`);
|
|
59
|
+
console.log(payload.enforcement || "Project has not adopted enforced SDLC.");
|
|
60
|
+
if ((payload.policy?.protectedPaths || []).length > 0) {
|
|
61
|
+
console.log("Protected paths:");
|
|
62
|
+
for (const item of payload.policy.protectedPaths) {
|
|
63
|
+
console.log(` - ${item}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if ((payload.nextCommands || []).length > 0) {
|
|
67
|
+
console.log("Next commands:");
|
|
68
|
+
for (const command of payload.nextCommands) {
|
|
69
|
+
console.log(` - ${command}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {Record<string, any>} payload
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*/
|
|
78
|
+
function printCommitPrep(payload) {
|
|
79
|
+
console.log("Topogram SDLC commit prep");
|
|
80
|
+
console.log(`Status: ${payload.ok ? "ready" : "needs attention"}`);
|
|
81
|
+
console.log(`Changed task files: ${(payload.taskFiles || []).length}`);
|
|
82
|
+
if ((payload.openTasks || []).length > 0) {
|
|
83
|
+
console.log("Open changed tasks:");
|
|
84
|
+
for (const task of payload.openTasks) {
|
|
85
|
+
console.log(` - ${task.id} (${task.status}, ${task.disposition || "unclassified"})`);
|
|
86
|
+
console.log(` file: ${task.file}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if ((payload.errors || []).length > 0) {
|
|
90
|
+
console.log("Errors:");
|
|
91
|
+
for (const error of payload.errors) {
|
|
92
|
+
console.log(` - ${error}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if ((payload.warnings || []).length > 0) {
|
|
96
|
+
console.log("Warnings:");
|
|
97
|
+
for (const warning of payload.warnings) {
|
|
98
|
+
console.log(` - ${warning}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if ((payload.nextCommands || []).length > 0) {
|
|
102
|
+
console.log("Next commands:");
|
|
103
|
+
for (const command of payload.nextCommands) {
|
|
104
|
+
console.log(` - ${command}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
32
109
|
/**
|
|
33
110
|
* @param {string} sdlcRoot
|
|
34
111
|
* @returns {AnyRecord|null}
|
|
@@ -56,15 +133,144 @@ function resolveSdlcWorkspace(sdlcRoot) {
|
|
|
56
133
|
*/
|
|
57
134
|
export async function runSdlcCommand(context) {
|
|
58
135
|
const { commandArgs, args } = context;
|
|
59
|
-
const sdlcRoot =
|
|
136
|
+
const sdlcRoot = resolveTopoRoot(context.inputPath || ".");
|
|
60
137
|
const actor = flagValue(args, "--actor");
|
|
61
138
|
const note = flagValue(args, "--note");
|
|
62
139
|
const status = flagValue(args, "--status");
|
|
140
|
+
const id = flagValue(args, "--id");
|
|
63
141
|
const before = flagValue(args, "--before");
|
|
64
142
|
const appVersion = flagValue(args, "--app-version");
|
|
65
143
|
const sinceTag = flagValue(args, "--since-tag");
|
|
144
|
+
const base = flagValue(args, "--base");
|
|
145
|
+
const head = flagValue(args, "--head");
|
|
146
|
+
const exemption = flagValue(args, "--exemption");
|
|
147
|
+
const verification = flagValue(args, "--verification");
|
|
66
148
|
const dryRun = args.includes("--dry-run");
|
|
67
149
|
const strict = args.includes("--strict");
|
|
150
|
+
const json = args.includes("--json");
|
|
151
|
+
|
|
152
|
+
if (String(commandArgs.sdlcCommand || "").startsWith("policy:")) {
|
|
153
|
+
const {
|
|
154
|
+
explainSdlcPolicy,
|
|
155
|
+
loadSdlcPolicy,
|
|
156
|
+
policyProjectRoot,
|
|
157
|
+
writeDefaultSdlcPolicy
|
|
158
|
+
} = await import("../../sdlc/policy.js");
|
|
159
|
+
const projectRoot = policyProjectRoot(context.inputPath || ".");
|
|
160
|
+
if (commandArgs.sdlcCommand === "policy:init") {
|
|
161
|
+
const result = writeDefaultSdlcPolicy(projectRoot);
|
|
162
|
+
console.log(stableStringify(result));
|
|
163
|
+
return result.ok ? 0 : 1;
|
|
164
|
+
}
|
|
165
|
+
if (commandArgs.sdlcCommand === "policy:check") {
|
|
166
|
+
const info = loadSdlcPolicy(projectRoot);
|
|
167
|
+
const result = {
|
|
168
|
+
type: "sdlc_policy_check",
|
|
169
|
+
ok: info.exists && info.diagnostics.every((diagnostic) => diagnostic.severity !== "error"),
|
|
170
|
+
exists: info.exists,
|
|
171
|
+
path: info.path,
|
|
172
|
+
status: info.status,
|
|
173
|
+
mode: info.mode,
|
|
174
|
+
diagnostics: info.diagnostics
|
|
175
|
+
};
|
|
176
|
+
if (json) {
|
|
177
|
+
console.log(stableStringify(result));
|
|
178
|
+
} else if (result.ok) {
|
|
179
|
+
console.log(`SDLC policy is valid: ${result.path}`);
|
|
180
|
+
} else {
|
|
181
|
+
console.error(stableStringify(result));
|
|
182
|
+
}
|
|
183
|
+
return result.ok ? 0 : 1;
|
|
184
|
+
}
|
|
185
|
+
if (commandArgs.sdlcCommand === "policy:explain") {
|
|
186
|
+
const result = explainSdlcPolicy(projectRoot);
|
|
187
|
+
if (json) {
|
|
188
|
+
console.log(stableStringify(result));
|
|
189
|
+
} else {
|
|
190
|
+
printPolicyExplain(result);
|
|
191
|
+
}
|
|
192
|
+
return result.ok ? 0 : 1;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (commandArgs.sdlcCommand === "gate") {
|
|
197
|
+
const { runSdlcGate } = await import("../../sdlc/gate.js");
|
|
198
|
+
const result = await runSdlcGate(context.inputPath || ".", {
|
|
199
|
+
base,
|
|
200
|
+
head,
|
|
201
|
+
sdlcIds: flagValues(args, "--sdlc-id"),
|
|
202
|
+
exemption,
|
|
203
|
+
requireAdopted: args.includes("--require-adopted")
|
|
204
|
+
});
|
|
205
|
+
console.log(stableStringify(result));
|
|
206
|
+
return result.ok ? 0 : 1;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (commandArgs.sdlcCommand === "prep:commit") {
|
|
210
|
+
const { runSdlcCommitPrep } = await import("../../sdlc/prep.js");
|
|
211
|
+
const result = runSdlcCommitPrep(context.inputPath || ".", {
|
|
212
|
+
base,
|
|
213
|
+
head
|
|
214
|
+
});
|
|
215
|
+
if (json) {
|
|
216
|
+
console.log(stableStringify(result));
|
|
217
|
+
} else {
|
|
218
|
+
printCommitPrep(result);
|
|
219
|
+
}
|
|
220
|
+
return result.ok ? 0 : 1;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (commandArgs.sdlcCommand === "link") {
|
|
224
|
+
const { linkSdlcRecord } = await import("../../sdlc/link.js");
|
|
225
|
+
const result = linkSdlcRecord(sdlcRoot, commandArgs.sdlcFromId, commandArgs.sdlcToId, {
|
|
226
|
+
write: args.includes("--write")
|
|
227
|
+
});
|
|
228
|
+
console.log(stableStringify(result));
|
|
229
|
+
return result.ok ? 0 : 1;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (commandArgs.sdlcCommand === "complete") {
|
|
233
|
+
const { completeTask } = await import("../../sdlc/complete.js");
|
|
234
|
+
const result = completeTask(sdlcRoot, commandArgs.sdlcId, verification || "", {
|
|
235
|
+
write: args.includes("--write") && !dryRun,
|
|
236
|
+
actor,
|
|
237
|
+
note
|
|
238
|
+
});
|
|
239
|
+
console.log(stableStringify(result));
|
|
240
|
+
return result.ok ? 0 : 1;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (commandArgs.sdlcCommand === "plan:create") {
|
|
244
|
+
const { createPlan } = await import("../../sdlc/plan.js");
|
|
245
|
+
const result = createPlan(sdlcRoot, commandArgs.sdlcId, commandArgs.sdlcSlug, {
|
|
246
|
+
write: args.includes("--write") && !dryRun
|
|
247
|
+
});
|
|
248
|
+
console.log(stableStringify(result));
|
|
249
|
+
return result.ok ? 0 : 1;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (commandArgs.sdlcCommand === "plan:explain") {
|
|
253
|
+
const { explainPlan } = await import("../../sdlc/plan.js");
|
|
254
|
+
const result = explainPlan(sdlcRoot, commandArgs.sdlcId);
|
|
255
|
+
console.log(stableStringify(result));
|
|
256
|
+
return result.ok ? 0 : 1;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (String(commandArgs.sdlcCommand || "").startsWith("plan:step:")) {
|
|
260
|
+
const { transitionPlanStep } = await import("../../sdlc/plan.js");
|
|
261
|
+
if (commandArgs.sdlcCommand === "plan:step:skip" && !note) {
|
|
262
|
+
console.log(stableStringify({ ok: false, error: "sdlc plan step skip requires --note <reason>" }));
|
|
263
|
+
return 1;
|
|
264
|
+
}
|
|
265
|
+
const result = transitionPlanStep(sdlcRoot, commandArgs.sdlcId, commandArgs.sdlcStepId, commandArgs.sdlcTargetStatus, {
|
|
266
|
+
write: args.includes("--write") && !dryRun,
|
|
267
|
+
dryRun,
|
|
268
|
+
actor,
|
|
269
|
+
note
|
|
270
|
+
});
|
|
271
|
+
console.log(stableStringify(result));
|
|
272
|
+
return result.ok ? 0 : 1;
|
|
273
|
+
}
|
|
68
274
|
|
|
69
275
|
if (commandArgs.sdlcCommand === "transition") {
|
|
70
276
|
const { transitionStatement } = await import("../../sdlc/transition.js");
|
|
@@ -115,10 +321,12 @@ export async function runSdlcCommand(context) {
|
|
|
115
321
|
if (!resolved) {
|
|
116
322
|
return 1;
|
|
117
323
|
}
|
|
118
|
-
const ids =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
324
|
+
const ids = id
|
|
325
|
+
? [id]
|
|
326
|
+
: archiveEligibleStatements(resolved, {
|
|
327
|
+
before,
|
|
328
|
+
statuses: status ? status.split(",") : null
|
|
329
|
+
});
|
|
122
330
|
const result = archiveBatch(sdlcRoot, ids, { dryRun, by: actor, reason: note });
|
|
123
331
|
console.log(stableStringify({ candidates: ids, ...result }));
|
|
124
332
|
return result.ok ? 0 : 1;
|
package/src/cli/dispatcher.js
CHANGED
|
@@ -10,7 +10,6 @@ import { runGenerateAppCommand } from "./commands/generate.js";
|
|
|
10
10
|
import { runGeneratorCommand } from "./commands/generator.js";
|
|
11
11
|
import { runGeneratorPolicyCommand } from "./commands/generator-policy.js";
|
|
12
12
|
import { runImportCommand } from "./commands/import-runner.js";
|
|
13
|
-
import { runMigrateCommand } from "./commands/migrate.js";
|
|
14
13
|
import { runNewProjectCommand } from "./commands/new.js";
|
|
15
14
|
import { runPackageCommand } from "./commands/package.js";
|
|
16
15
|
import { runParseCommand, runResolveCommand } from "./commands/inspect.js";
|
|
@@ -110,6 +109,7 @@ export async function runCliDispatch(context) {
|
|
|
110
109
|
domainId,
|
|
111
110
|
seamId,
|
|
112
111
|
taskId,
|
|
112
|
+
planId,
|
|
113
113
|
pitchId,
|
|
114
114
|
requirementId,
|
|
115
115
|
acceptanceId,
|
|
@@ -242,10 +242,6 @@ export async function runCliDispatch(context) {
|
|
|
242
242
|
return runPackageCommand({ commandArgs, inputPath, json: emitJson });
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
if (commandArgs?.migrateCommand) {
|
|
246
|
-
return runMigrateCommand(inputPath, { write: shouldWrite, json: emitJson });
|
|
247
|
-
}
|
|
248
|
-
|
|
249
245
|
if (commandArgs?.importCommand) {
|
|
250
246
|
return runImportCommand({
|
|
251
247
|
commandArgs,
|
|
@@ -301,6 +297,13 @@ export async function runCliDispatch(context) {
|
|
|
301
297
|
journeyId,
|
|
302
298
|
surfaceId,
|
|
303
299
|
domainId,
|
|
300
|
+
pitchId,
|
|
301
|
+
requirementId,
|
|
302
|
+
acceptanceId,
|
|
303
|
+
taskId,
|
|
304
|
+
planId,
|
|
305
|
+
bugId,
|
|
306
|
+
documentId,
|
|
304
307
|
seamId,
|
|
305
308
|
modeId,
|
|
306
309
|
providerId,
|
package/src/cli/help.js
CHANGED
|
@@ -16,6 +16,14 @@ export function printUsage(options = {}) {
|
|
|
16
16
|
console.log(" or: topogram widget check [path] [--projection <id>] [--widget <id>] [--json]");
|
|
17
17
|
console.log(" or: topogram widget behavior [path] [--projection <id>] [--widget <id>] [--json]");
|
|
18
18
|
console.log(" or: topogram agent brief [path] [--json]");
|
|
19
|
+
console.log(" or: topogram sdlc policy init|check|explain [path] [--json]");
|
|
20
|
+
console.log(" or: topogram sdlc gate [path] --base <ref> --head <ref> [--sdlc-id <id>] [--exemption <text>] [--require-adopted] [--json]");
|
|
21
|
+
console.log(" or: topogram sdlc prep commit [path] [--base <ref> --head <ref>] [--json]");
|
|
22
|
+
console.log(" or: topogram sdlc link <from-id> <to-id> [path] [--write]");
|
|
23
|
+
console.log(" or: topogram sdlc complete <task-id> [path] --verification <verification-id> [--dry-run|--write]");
|
|
24
|
+
console.log(" or: topogram sdlc plan create <task-id> <slug> [path] [--write]");
|
|
25
|
+
console.log(" or: topogram sdlc plan explain <plan-id> [path] [--json]");
|
|
26
|
+
console.log(" or: topogram sdlc plan step complete <plan-id> <step-id> [path] --actor <actor> [--write]");
|
|
19
27
|
console.log(" or: topogram generate [path] [--out <path>]");
|
|
20
28
|
console.log(" or: topogram emit <target> [path] [--json|--write --out-dir <path>]");
|
|
21
29
|
console.log(" or: topogram query list [--json]");
|
|
@@ -28,7 +36,6 @@ export function printUsage(options = {}) {
|
|
|
28
36
|
console.log(" or: topogram catalog doctor [--json] [--catalog <path-or-source>]");
|
|
29
37
|
console.log(" or: topogram catalog check <path-or-url> [--json]");
|
|
30
38
|
console.log(" or: topogram catalog copy <id> <target> [--version <version>] [--json] [--catalog <path-or-source>]");
|
|
31
|
-
console.log(" or: topogram migrate workspace-folder [path] [--dry-run|--write] [--json]");
|
|
32
39
|
console.log(" or: topogram package update-cli <version|--latest> [--json]");
|
|
33
40
|
console.log(" or: topogram import <app-path> --out <target> [--from <track[,track]>] [--json]");
|
|
34
41
|
console.log(" or: topogram import refresh [path] [--from <app-path>] [--dry-run] [--json]");
|
|
@@ -78,6 +85,10 @@ export function printUsage(options = {}) {
|
|
|
78
85
|
console.log(" topogram widget behavior --projection proj_web_surface");
|
|
79
86
|
console.log(" topogram agent brief");
|
|
80
87
|
console.log(" topogram agent brief --json");
|
|
88
|
+
console.log(" topogram sdlc policy explain");
|
|
89
|
+
console.log(" topogram sdlc prep commit . --base origin/main --head HEAD");
|
|
90
|
+
console.log(" topogram sdlc gate . --require-adopted");
|
|
91
|
+
console.log(" topogram sdlc plan explain plan_example --json");
|
|
81
92
|
console.log(" topogram query list");
|
|
82
93
|
console.log(" topogram query show widget-behavior");
|
|
83
94
|
console.log(" topogram query widget-behavior ./topo --projection proj_web_surface --json");
|
|
@@ -165,7 +176,7 @@ export function printUsage(options = {}) {
|
|
|
165
176
|
console.log(" or: node ./src/cli.js query maintained-drift <path> --from-topogram <path>");
|
|
166
177
|
console.log(" or: node ./src/cli.js query seam-check <path> [--seam <id>] [--from-topogram <path>]");
|
|
167
178
|
console.log(" or: node ./src/cli.js query diff <path> --from-topogram <path>");
|
|
168
|
-
console.log(" or: node ./src/cli.js query slice <path> [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>] [--domain <id>]");
|
|
179
|
+
console.log(" or: node ./src/cli.js query slice <path> [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>] [--domain <id>] [--task <id>] [--plan <id>] [--bug <id>]");
|
|
169
180
|
console.log(" or: node ./src/cli.js query domain-list <path>");
|
|
170
181
|
console.log(" or: node ./src/cli.js query domain-coverage <path> --domain <id>");
|
|
171
182
|
console.log(" or: node ./src/cli.js query review-boundary <path> [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>]");
|
|
@@ -179,7 +190,7 @@ export function printUsage(options = {}) {
|
|
|
179
190
|
console.log(" or: node ./src/cli.js query proceed-decision <path> [--mode <id>] [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>] [--surface <id>] [--from-topogram <path>]");
|
|
180
191
|
console.log(" or: node ./src/cli.js query review-packet <path> [--mode <id>] [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>] [--surface <id>] [--from-topogram <path>]");
|
|
181
192
|
console.log(" or: node ./src/cli.js query next-action <path> [--mode <id>] [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>] [--from-topogram <path>]");
|
|
182
|
-
console.log(" or: node ./src/cli.js query single-agent-plan <path> --mode <id> [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>] [--surface <id>] [--from-topogram <path>]");
|
|
193
|
+
console.log(" or: node ./src/cli.js query single-agent-plan <path> --mode <id> [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>] [--surface <id>] [--task <id>] [--plan <id>] [--bug <id>] [--from-topogram <path>]");
|
|
183
194
|
console.log(" or: node ./src/cli.js query multi-agent-plan <path> --mode import-adopt");
|
|
184
195
|
console.log(" or: node ./src/cli.js query resolved-workflow-context <path> --mode <id> [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>] [--surface <id>] [--provider <id>] [--preset <id>] [--from-topogram <path>]");
|
|
185
196
|
console.log(" or: node ./src/cli.js query workflow-preset-activation <path> --mode <id> [--provider <id>] [--preset <id>] [--from-topogram <path>]");
|
|
@@ -30,6 +30,9 @@ export function cliMigrationError(args) {
|
|
|
30
30
|
if (args[0] === "component") {
|
|
31
31
|
return "Command 'topogram component' was renamed to 'topogram widget'.";
|
|
32
32
|
}
|
|
33
|
+
if (args[0] === "migrate") {
|
|
34
|
+
return "Command 'topogram migrate workspace-folder' was removed. Use topo/ workspaces or configure topogram.project.json workspace to a non-legacy relative path.";
|
|
35
|
+
}
|
|
33
36
|
for (const [oldArg, newArg] of RENAMED_CLI_ARGS) {
|
|
34
37
|
if (args.includes(oldArg)) {
|
|
35
38
|
return `CLI flag '${oldArg}' was renamed to '${newArg}'.`;
|
package/src/cli/options.js
CHANGED
|
@@ -81,6 +81,7 @@ export function parseCliOptions(args, commandArgs) {
|
|
|
81
81
|
domainId: optionValue(args, "--domain"),
|
|
82
82
|
seamId: optionValue(args, "--seam"),
|
|
83
83
|
taskId: optionValue(args, "--task"),
|
|
84
|
+
planId: optionValue(args, "--plan"),
|
|
84
85
|
pitchId: optionValue(args, "--pitch"),
|
|
85
86
|
requirementId: optionValue(args, "--requirement"),
|
|
86
87
|
acceptanceId: optionValue(args, "--acceptance"),
|
|
@@ -135,6 +135,14 @@ export function acceptanceCriterionById(graph, id) {
|
|
|
135
135
|
export function taskById(graph, id) {
|
|
136
136
|
return (graph?.byKind?.task || []).find(/** @param {any} s */ (s) => s.id === id) || null;
|
|
137
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* @param {import("./types.d.ts").ContextGraph} graph
|
|
140
|
+
* @param {string} id
|
|
141
|
+
* @returns {any}
|
|
142
|
+
*/
|
|
143
|
+
export function planById(graph, id) {
|
|
144
|
+
return (graph?.byKind?.plan || []).find(/** @param {any} s */ (s) => s.id === id) || null;
|
|
145
|
+
}
|
|
138
146
|
/**
|
|
139
147
|
* @param {import("./types.d.ts").ContextGraph} graph
|
|
140
148
|
* @param {string} id
|
|
@@ -207,10 +215,29 @@ export function summarizeTask(task) {
|
|
|
207
215
|
status: task.status,
|
|
208
216
|
priority: task.priority,
|
|
209
217
|
work_type: task.workType,
|
|
218
|
+
disposition: task.disposition || null,
|
|
210
219
|
claimed_by: (task.claimedBy || []).map(/** @param {any} r */ (r) => (typeof r === "string" ? r : r?.id)).filter(Boolean),
|
|
211
220
|
domain: task.resolvedDomain ? task.resolvedDomain.id : null
|
|
212
221
|
};
|
|
213
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* @param {any} plan
|
|
225
|
+
* @returns {any}
|
|
226
|
+
*/
|
|
227
|
+
export function summarizePlan(plan) {
|
|
228
|
+
if (!plan) return null;
|
|
229
|
+
const steps = plan.steps || [];
|
|
230
|
+
return {
|
|
231
|
+
id: plan.id,
|
|
232
|
+
name: plan.name,
|
|
233
|
+
status: plan.status,
|
|
234
|
+
priority: plan.priority,
|
|
235
|
+
task: plan.task?.id || null,
|
|
236
|
+
step_count: steps.length,
|
|
237
|
+
incomplete_steps: steps.filter(/** @param {any} step */ (step) => step.status !== "done" && step.status !== "skipped").map(/** @param {any} step */ (step) => step.id),
|
|
238
|
+
domain: plan.resolvedDomain ? plan.resolvedDomain.id : null
|
|
239
|
+
};
|
|
240
|
+
}
|
|
214
241
|
/**
|
|
215
242
|
* @param {any} bug
|
|
216
243
|
* @returns {any}
|
|
@@ -436,13 +436,14 @@ export function ensureContextSelection(options = {}) {
|
|
|
436
436
|
options.requirementId ? ["requirement", options.requirementId] : null,
|
|
437
437
|
options.acceptanceId ? ["acceptance_criterion", options.acceptanceId] : null,
|
|
438
438
|
options.taskId ? ["task", options.taskId] : null,
|
|
439
|
+
options.planId ? ["plan", options.planId] : null,
|
|
439
440
|
options.bugId ? ["bug", options.bugId] : null,
|
|
440
441
|
options.documentId ? ["document", options.documentId] : null
|
|
441
442
|
].filter(Boolean));
|
|
442
443
|
|
|
443
444
|
if (selectors.length !== 1) {
|
|
444
445
|
throw new Error(
|
|
445
|
-
"Context selection requires exactly one of --capability, --workflow, --projection, --widget, --entity, --journey, --surface, --domain, --pitch, --requirement, --acceptance, --task, --bug, or --document"
|
|
446
|
+
"Context selection requires exactly one of --capability, --workflow, --projection, --widget, --entity, --journey, --surface, --domain, --pitch, --requirement, --acceptance, --task, --plan, --bug, or --document"
|
|
446
447
|
);
|
|
447
448
|
}
|
|
448
449
|
|
|
@@ -9,6 +9,7 @@ export function getJourneyDoc(...args: any[]): any;
|
|
|
9
9
|
export function getStatement(...args: any[]): any;
|
|
10
10
|
export function getWorkflowDoc(...args: any[]): any;
|
|
11
11
|
export function pitchById(...args: any[]): any;
|
|
12
|
+
export function planById(...args: any[]): any;
|
|
12
13
|
export function relatedCapabilitiesForEntity(...args: any[]): any;
|
|
13
14
|
export function relatedCapabilitiesForProjection(...args: any[]): any;
|
|
14
15
|
export function relatedEntitiesForDomain(...args: any[]): any;
|
|
@@ -34,6 +35,7 @@ export function summarizeDocsByIds(...args: any[]): any;
|
|
|
34
35
|
export function summarizeDocument(...args: any[]): any;
|
|
35
36
|
export function summarizeDomain(...args: any[]): any;
|
|
36
37
|
export function summarizePitch(...args: any[]): any;
|
|
38
|
+
export function summarizePlan(...args: any[]): any;
|
|
37
39
|
export function summarizeProjection(...args: any[]): any;
|
|
38
40
|
export function summarizeRequirement(...args: any[]): any;
|
|
39
41
|
export function summarizeStatementsByIds(...args: any[]): any;
|
|
@@ -55,12 +55,14 @@ export {
|
|
|
55
55
|
requirementById,
|
|
56
56
|
acceptanceCriterionById,
|
|
57
57
|
taskById,
|
|
58
|
+
planById,
|
|
58
59
|
bugById,
|
|
59
60
|
documentById,
|
|
60
61
|
summarizePitch,
|
|
61
62
|
summarizeRequirement,
|
|
62
63
|
summarizeAcceptanceCriterion,
|
|
63
64
|
summarizeTask,
|
|
65
|
+
summarizePlan,
|
|
64
66
|
summarizeBug,
|
|
65
67
|
summarizeDocument,
|
|
66
68
|
getWorkflowDoc,
|