@topogram/cli 0.3.72 → 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/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 -0
- package/src/cli/help.js +14 -2
- 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/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 +26 -2
|
@@ -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
|
@@ -109,6 +109,7 @@ export async function runCliDispatch(context) {
|
|
|
109
109
|
domainId,
|
|
110
110
|
seamId,
|
|
111
111
|
taskId,
|
|
112
|
+
planId,
|
|
112
113
|
pitchId,
|
|
113
114
|
requirementId,
|
|
114
115
|
acceptanceId,
|
|
@@ -296,6 +297,13 @@ export async function runCliDispatch(context) {
|
|
|
296
297
|
journeyId,
|
|
297
298
|
surfaceId,
|
|
298
299
|
domainId,
|
|
300
|
+
pitchId,
|
|
301
|
+
requirementId,
|
|
302
|
+
acceptanceId,
|
|
303
|
+
taskId,
|
|
304
|
+
planId,
|
|
305
|
+
bugId,
|
|
306
|
+
documentId,
|
|
299
307
|
seamId,
|
|
300
308
|
modeId,
|
|
301
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]");
|
|
@@ -77,6 +85,10 @@ export function printUsage(options = {}) {
|
|
|
77
85
|
console.log(" topogram widget behavior --projection proj_web_surface");
|
|
78
86
|
console.log(" topogram agent brief");
|
|
79
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");
|
|
80
92
|
console.log(" topogram query list");
|
|
81
93
|
console.log(" topogram query show widget-behavior");
|
|
82
94
|
console.log(" topogram query widget-behavior ./topo --projection proj_web_surface --json");
|
|
@@ -164,7 +176,7 @@ export function printUsage(options = {}) {
|
|
|
164
176
|
console.log(" or: node ./src/cli.js query maintained-drift <path> --from-topogram <path>");
|
|
165
177
|
console.log(" or: node ./src/cli.js query seam-check <path> [--seam <id>] [--from-topogram <path>]");
|
|
166
178
|
console.log(" or: node ./src/cli.js query diff <path> --from-topogram <path>");
|
|
167
|
-
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>]");
|
|
168
180
|
console.log(" or: node ./src/cli.js query domain-list <path>");
|
|
169
181
|
console.log(" or: node ./src/cli.js query domain-coverage <path> --domain <id>");
|
|
170
182
|
console.log(" or: node ./src/cli.js query review-boundary <path> [--capability <id>] [--workflow <id>] [--projection <id>] [--widget <id>] [--entity <id>] [--journey <id>]");
|
|
@@ -178,7 +190,7 @@ export function printUsage(options = {}) {
|
|
|
178
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>]");
|
|
179
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>]");
|
|
180
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>]");
|
|
181
|
-
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>]");
|
|
182
194
|
console.log(" or: node ./src/cli.js query multi-agent-plan <path> --mode import-adopt");
|
|
183
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>]");
|
|
184
196
|
console.log(" or: node ./src/cli.js query workflow-preset-activation <path> --mode <id> [--provider <id>] [--preset <id>] [--from-topogram <path>]");
|
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,
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
domainSlice,
|
|
41
41
|
journeySlice,
|
|
42
42
|
pitchSlice,
|
|
43
|
+
planSlice,
|
|
43
44
|
requirementSlice,
|
|
44
45
|
taskSlice
|
|
45
46
|
} from "./sdlc.js";
|
|
@@ -375,6 +376,7 @@ export function generateContextSlice(graph, options = {}) {
|
|
|
375
376
|
requirementId: options.requirementId,
|
|
376
377
|
acceptanceId: options.acceptanceId,
|
|
377
378
|
taskId: options.taskId,
|
|
379
|
+
planId: options.planId,
|
|
378
380
|
bugId: options.bugId,
|
|
379
381
|
documentId: options.documentId
|
|
380
382
|
});
|
|
@@ -390,6 +392,7 @@ export function generateContextSlice(graph, options = {}) {
|
|
|
390
392
|
if (selection.kind === "requirement") return requirementSlice(graph, selection.id);
|
|
391
393
|
if (selection.kind === "acceptance_criterion") return acceptanceCriterionSlice(graph, selection.id);
|
|
392
394
|
if (selection.kind === "task") return taskSlice(graph, selection.id);
|
|
395
|
+
if (selection.kind === "plan") return planSlice(graph, selection.id);
|
|
393
396
|
if (selection.kind === "bug") return bugSlice(graph, selection.id);
|
|
394
397
|
if (selection.kind === "document") return documentSlice(graph, selection.id);
|
|
395
398
|
|