@topogram/cli 0.3.72 → 0.3.74
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/release-rollout.js +191 -10
- package/src/cli/commands/release-shared.js +51 -2
- package/src/cli/commands/release.js +16 -3
- package/src/cli/commands/sdlc.js +213 -5
- package/src/cli/dispatcher.js +8 -0
- package/src/cli/help.js +15 -3
- 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
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
|
@@ -11,11 +11,19 @@ export function printUsage(options = {}) {
|
|
|
11
11
|
console.log(" or: topogram doctor --allow-local-npmrc");
|
|
12
12
|
console.log("Usage: topogram setup package-auth|catalog-auth");
|
|
13
13
|
console.log("Usage: topogram release status [--json] [--strict] [--markdown|--write-report <path>]");
|
|
14
|
-
console.log(" or: topogram release roll-consumers <version|--latest> [--json] [--no-push] [--watch]");
|
|
14
|
+
console.log(" or: topogram release roll-consumers <version|--latest> [--json] [--no-push] [--watch|--no-watch]");
|
|
15
15
|
console.log("Usage: topogram check [path] [--json]");
|
|
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
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
domainById,
|
|
9
9
|
getJourneyDoc,
|
|
10
10
|
pitchById,
|
|
11
|
+
planById,
|
|
11
12
|
recommendedVerificationTargets,
|
|
12
13
|
relatedEntitiesForDomain,
|
|
13
14
|
relatedProjectionsForDomain,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
summarizeDocument,
|
|
22
23
|
summarizeDomain,
|
|
23
24
|
summarizePitch,
|
|
25
|
+
summarizePlan,
|
|
24
26
|
summarizeRequirement,
|
|
25
27
|
summarizeStatementsByIds,
|
|
26
28
|
summarizeTask,
|
|
@@ -297,10 +299,12 @@ export function taskSlice(graph, taskId) {
|
|
|
297
299
|
|
|
298
300
|
const satisfies = (task.satisfies || []).map(/** @param {any} r */ (r) => (typeof r === "string" ? r : r?.id)).filter(Boolean).sort();
|
|
299
301
|
const acRefs = (task.acceptanceRefs || []).map(/** @param {any} r */ (r) => (typeof r === "string" ? r : r?.id)).filter(Boolean).sort();
|
|
302
|
+
const verificationRefs = (task.verificationRefs || []).map(/** @param {any} r */ (r) => (typeof r === "string" ? r : r?.id)).filter(Boolean).sort();
|
|
300
303
|
const blockedBy = (task.blockedBy || []).map(/** @param {any} r */ (r) => (typeof r === "string" ? r : r?.id)).filter(Boolean).sort();
|
|
301
304
|
const blocks = (task.blocks || []).map(/** @param {any} r */ (r) => (typeof r === "string" ? r : r?.id)).filter(Boolean).sort();
|
|
302
305
|
const affects = (task.affects || []).map(/** @param {any} a */ (a) => (typeof a === "string" ? a : a?.id)).filter(Boolean).sort();
|
|
303
|
-
const
|
|
306
|
+
const plans = (task.plans || []).slice().sort();
|
|
307
|
+
const verifications = [...new Set([...verificationRefs, ...verificationIdsForTarget(graph, [taskId, ...affects, ...acRefs])])].sort();
|
|
304
308
|
|
|
305
309
|
return {
|
|
306
310
|
type: "context_slice",
|
|
@@ -310,15 +314,19 @@ export function taskSlice(graph, taskId) {
|
|
|
310
314
|
depends_on: {
|
|
311
315
|
satisfies,
|
|
312
316
|
acceptance_refs: acRefs,
|
|
317
|
+
verification_refs: verificationRefs,
|
|
313
318
|
blocked_by: blockedBy,
|
|
314
319
|
blocks,
|
|
320
|
+
plans,
|
|
315
321
|
affects,
|
|
316
322
|
verifications
|
|
317
323
|
},
|
|
318
324
|
related: {
|
|
319
325
|
satisfies: summarizeStatementsByIds(graph, satisfies),
|
|
320
326
|
acceptance_refs: summarizeStatementsByIds(graph, acRefs),
|
|
327
|
+
verification_refs: summarizeStatementsByIds(graph, verificationRefs),
|
|
321
328
|
blocked_by: summarizeStatementsByIds(graph, blockedBy),
|
|
329
|
+
plans: summarizeStatementsByIds(graph, plans),
|
|
322
330
|
affects: summarizeStatementsByIds(graph, affects)
|
|
323
331
|
},
|
|
324
332
|
verification: summarizeStatementsByIds(graph, verifications),
|
|
@@ -331,6 +339,54 @@ export function taskSlice(graph, taskId) {
|
|
|
331
339
|
};
|
|
332
340
|
}
|
|
333
341
|
|
|
342
|
+
/**
|
|
343
|
+
* @param {import("../shared/types.d.ts").ContextGraph} graph
|
|
344
|
+
* @param {string} planId
|
|
345
|
+
* @returns {any}
|
|
346
|
+
*/
|
|
347
|
+
export function planSlice(graph, planId) {
|
|
348
|
+
const plan = planById(graph, planId);
|
|
349
|
+
if (!plan) throw new Error(`No plan found with id '${planId}'`);
|
|
350
|
+
|
|
351
|
+
const taskId = plan.task?.id || null;
|
|
352
|
+
const task = taskId ? taskById(graph, taskId) : null;
|
|
353
|
+
const satisfies = task ? (task.satisfies || []).map(/** @param {any} r */ (r) => (typeof r === "string" ? r : r?.id)).filter(Boolean).sort() : [];
|
|
354
|
+
const acRefs = task ? (task.acceptanceRefs || []).map(/** @param {any} r */ (r) => (typeof r === "string" ? r : r?.id)).filter(Boolean).sort() : [];
|
|
355
|
+
const verificationRefs = task ? (task.verificationRefs || []).map(/** @param {any} r */ (r) => (typeof r === "string" ? r : r?.id)).filter(Boolean).sort() : [];
|
|
356
|
+
const affects = task ? (task.affects || []).map(/** @param {any} a */ (a) => (typeof a === "string" ? a : a?.id)).filter(Boolean).sort() : [];
|
|
357
|
+
const verifications = [...new Set([...verificationRefs, ...verificationIdsForTarget(graph, [planId, ...(taskId ? [taskId] : []), ...affects, ...acRefs])])].sort();
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
type: "context_slice",
|
|
361
|
+
version: 1,
|
|
362
|
+
focus: { kind: "plan", id: planId },
|
|
363
|
+
summary: summarizePlan(plan),
|
|
364
|
+
depends_on: {
|
|
365
|
+
task: taskId,
|
|
366
|
+
satisfies,
|
|
367
|
+
acceptance_refs: acRefs,
|
|
368
|
+
verification_refs: verificationRefs,
|
|
369
|
+
affects,
|
|
370
|
+
verifications
|
|
371
|
+
},
|
|
372
|
+
related: {
|
|
373
|
+
task: task ? [summarizeTask(task)] : [],
|
|
374
|
+
satisfies: summarizeStatementsByIds(graph, satisfies),
|
|
375
|
+
acceptance_refs: summarizeStatementsByIds(graph, acRefs),
|
|
376
|
+
verification_refs: summarizeStatementsByIds(graph, verificationRefs),
|
|
377
|
+
affects: summarizeStatementsByIds(graph, affects)
|
|
378
|
+
},
|
|
379
|
+
steps: plan.steps || [],
|
|
380
|
+
verification: summarizeStatementsByIds(graph, verifications),
|
|
381
|
+
verification_targets: recommendedVerificationTargets(graph, [planId, ...(taskId ? [taskId] : []), ...affects, ...acRefs], {
|
|
382
|
+
rationale: "Plan slice points at verification for the owning task and affected surfaces."
|
|
383
|
+
}),
|
|
384
|
+
write_scope: buildDefaultWriteScope(),
|
|
385
|
+
review_boundary: reviewBoundaryForTask(),
|
|
386
|
+
ownership_boundary: defaultOwnershipBoundary()
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
334
390
|
/**
|
|
335
391
|
* @param {import("../shared/types.d.ts").ContextGraph} graph
|
|
336
392
|
* @param {string} bugId
|
|
@@ -414,4 +470,3 @@ export function documentSlice(graph, documentId) {
|
|
|
414
470
|
ownership_boundary: defaultOwnershipBoundary()
|
|
415
471
|
};
|
|
416
472
|
}
|
|
417
|
-
|
|
@@ -234,6 +234,13 @@ function selectedSurface(options = {}) {
|
|
|
234
234
|
if (options.entityId) return { kind: "entity", id: options.entityId };
|
|
235
235
|
if (options.journeyId) return { kind: "journey", id: options.journeyId };
|
|
236
236
|
if (options.surfaceId) return { kind: "surface", id: options.surfaceId };
|
|
237
|
+
if (options.pitchId) return { kind: "pitch", id: options.pitchId };
|
|
238
|
+
if (options.requirementId) return { kind: "requirement", id: options.requirementId };
|
|
239
|
+
if (options.acceptanceId) return { kind: "acceptance_criterion", id: options.acceptanceId };
|
|
240
|
+
if (options.taskId) return { kind: "task", id: options.taskId };
|
|
241
|
+
if (options.planId) return { kind: "plan", id: options.planId };
|
|
242
|
+
if (options.bugId) return { kind: "bug", id: options.bugId };
|
|
243
|
+
if (options.documentId) return { kind: "document", id: options.documentId };
|
|
237
244
|
return null;
|
|
238
245
|
}
|
|
239
246
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
summarizeBug,
|
|
10
|
+
summarizePlan,
|
|
10
11
|
summarizePitch,
|
|
11
12
|
summarizeRequirement,
|
|
12
13
|
summarizeTask
|
|
@@ -20,6 +21,7 @@ const SUMMARIZERS = {
|
|
|
20
21
|
pitch: summarizePitch,
|
|
21
22
|
requirement: summarizeRequirement,
|
|
22
23
|
task: summarizeTask,
|
|
24
|
+
plan: summarizePlan,
|
|
23
25
|
bug: summarizeBug
|
|
24
26
|
};
|
|
25
27
|
|
|
@@ -19,7 +19,11 @@ export function generateSdlcTraceabilityMatrix(graph, options = {}) {
|
|
|
19
19
|
const pitch = pitchId ? pitchesById.get(pitchId) : null;
|
|
20
20
|
|
|
21
21
|
const taskIds = (ac.tasks || []).slice().sort();
|
|
22
|
-
const
|
|
22
|
+
const taskVerificationIds = taskIds.flatMap((id) => {
|
|
23
|
+
const task = tasksById.get(id);
|
|
24
|
+
return (task?.verificationRefs || []).map((ref) => typeof ref === "string" ? ref : ref?.id).filter(Boolean);
|
|
25
|
+
});
|
|
26
|
+
const verificationIds = [...new Set([...(ac.verifications || []), ...taskVerificationIds])].sort();
|
|
23
27
|
|
|
24
28
|
const linkedBugIds = [];
|
|
25
29
|
for (const bug of bugsById.values()) {
|
|
@@ -20,7 +20,7 @@ export function findNearestGitRoot(startDir) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function normalizeWorkspacePaths(inputPath) {
|
|
23
|
-
const context = resolveWorkspaceContext(inputPath);
|
|
23
|
+
const context = resolveWorkspaceContext(inputPath, { ignoreAncestorConfig: true });
|
|
24
24
|
const absolute = path.resolve(inputPath);
|
|
25
25
|
const topogramRoot = context.topoRoot;
|
|
26
26
|
const workspaceRoot = context.projectRoot;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
export const IMPORT_TRACKS = new Set(["db", "api", "ui", "workflows", "verification"]);
|
|
1
|
+
export const IMPORT_TRACKS = new Set(["db", "api", "ui", "cli", "workflows", "verification"]);
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @typedef {{score:number, reasons:string[]}} DetectionResult
|
|
5
5
|
* @typedef {{findings:any[], candidates:any}} ExtractResult
|
|
6
6
|
* @typedef {{
|
|
7
7
|
* id:string,
|
|
8
|
-
* track:"db"|"api"|"ui"|"workflows"|"verification",
|
|
8
|
+
* track:"db"|"api"|"ui"|"cli"|"workflows"|"verification",
|
|
9
9
|
* detect:(context:any)=>DetectionResult,
|
|
10
10
|
* extract:(context:any)=>ExtractResult
|
|
11
11
|
* }} ImportExtractor
|
|
12
12
|
* @typedef {{
|
|
13
13
|
* id:string,
|
|
14
|
-
* track:"db"|"api"|"ui"|"workflows"|"verification"|"docs",
|
|
14
|
+
* track:"db"|"api"|"ui"|"cli"|"workflows"|"verification"|"docs",
|
|
15
15
|
* applies:(context:any, candidates:any)=>boolean|number,
|
|
16
16
|
* enrich:(context:any, candidates:any)=>any
|
|
17
17
|
* }} ImportEnricher
|