@xenonbyte/da-vinci-workflow 0.1.26 → 0.2.2
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/CHANGELOG.md +31 -0
- package/README.md +28 -65
- package/README.zh-CN.md +28 -65
- package/bin/da-vinci-tui.js +8 -0
- package/commands/claude/dv/continue.md +5 -0
- package/commands/codex/prompts/dv-continue.md +6 -1
- package/commands/gemini/dv/continue.toml +5 -0
- package/commands/templates/dv-continue.shared.md +33 -0
- package/docs/dv-command-reference.md +35 -0
- package/docs/execution-chain-migration.md +46 -0
- package/docs/execution-chain-plan.md +125 -0
- package/docs/prompt-entrypoints.md +8 -0
- package/docs/skill-usage.md +217 -0
- package/docs/workflow-examples.md +10 -0
- package/docs/workflow-overview.md +26 -0
- package/docs/zh-CN/dv-command-reference.md +35 -0
- package/docs/zh-CN/execution-chain-migration.md +46 -0
- package/docs/zh-CN/prompt-entrypoints.md +8 -0
- package/docs/zh-CN/skill-usage.md +217 -0
- package/docs/zh-CN/workflow-examples.md +10 -0
- package/docs/zh-CN/workflow-overview.md +26 -0
- package/lib/artifact-parsers.js +120 -0
- package/lib/audit.js +61 -0
- package/lib/cli.js +351 -13
- package/lib/diff-spec.js +242 -0
- package/lib/execution-signals.js +136 -0
- package/lib/lint-bindings.js +143 -0
- package/lib/lint-spec.js +408 -0
- package/lib/lint-tasks.js +176 -0
- package/lib/planning-parsers.js +567 -0
- package/lib/scaffold.js +193 -0
- package/lib/scope-check.js +603 -0
- package/lib/sidecars.js +369 -0
- package/lib/supervisor-review.js +28 -3
- package/lib/utils.js +10 -2
- package/lib/verify.js +652 -0
- package/lib/workflow-contract.js +107 -0
- package/lib/workflow-persisted-state.js +297 -0
- package/lib/workflow-state.js +785 -0
- package/package.json +13 -3
- package/references/artifact-templates.md +26 -0
- package/references/checkpoints.md +14 -0
- package/references/modes.md +10 -0
- package/tui/catalog.js +1190 -0
- package/tui/index.js +727 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const { getMarkdownSection, hasMarkdownHeading } = require("./audit-parsers");
|
|
2
|
+
|
|
3
|
+
const RUNTIME_SPEC_SECTION_DEFS = [
|
|
4
|
+
{
|
|
5
|
+
id: "capability",
|
|
6
|
+
heading: "Capability",
|
|
7
|
+
required: false
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: "behavior",
|
|
11
|
+
heading: "Behavior",
|
|
12
|
+
required: true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "states",
|
|
16
|
+
heading: "States",
|
|
17
|
+
required: true
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "inputs",
|
|
21
|
+
heading: "Inputs",
|
|
22
|
+
required: true
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "outputs",
|
|
26
|
+
heading: "Outputs",
|
|
27
|
+
required: true
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "acceptance",
|
|
31
|
+
heading: "Acceptance",
|
|
32
|
+
required: true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "edgeCases",
|
|
36
|
+
heading: "Edge Cases",
|
|
37
|
+
required: true
|
|
38
|
+
}
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const LIST_ITEM_PATTERN = /^\s*(?:[-*+]|\d+[.)])\s+(.+?)\s*$/;
|
|
42
|
+
|
|
43
|
+
function extractSectionItems(sectionText) {
|
|
44
|
+
const normalized = String(sectionText || "").replace(/\r\n?/g, "\n").trim();
|
|
45
|
+
if (!normalized) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const lines = normalized.split("\n");
|
|
50
|
+
const listItems = [];
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
const match = line.match(LIST_ITEM_PATTERN);
|
|
53
|
+
if (!match) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const value = String(match[1] || "").trim();
|
|
57
|
+
if (value) {
|
|
58
|
+
listItems.push(value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (listItems.length > 0) {
|
|
63
|
+
return listItems;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return normalized
|
|
67
|
+
.split(/\n\s*\n/g)
|
|
68
|
+
.map((item) => item.trim())
|
|
69
|
+
.filter(Boolean);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseRuntimeSpecMarkdown(markdownText) {
|
|
73
|
+
const source = String(markdownText || "");
|
|
74
|
+
const sections = {};
|
|
75
|
+
const missingSections = [];
|
|
76
|
+
const emptySections = [];
|
|
77
|
+
|
|
78
|
+
for (const sectionDef of RUNTIME_SPEC_SECTION_DEFS) {
|
|
79
|
+
const present = hasMarkdownHeading(source, sectionDef.heading, {
|
|
80
|
+
allowParentheticalSuffix: true
|
|
81
|
+
});
|
|
82
|
+
const raw = getMarkdownSection(source, sectionDef.heading, {
|
|
83
|
+
allowParentheticalSuffix: true
|
|
84
|
+
});
|
|
85
|
+
const empty = present && String(raw || "").trim() === "";
|
|
86
|
+
const items = extractSectionItems(raw);
|
|
87
|
+
|
|
88
|
+
sections[sectionDef.id] = {
|
|
89
|
+
heading: sectionDef.heading,
|
|
90
|
+
required: sectionDef.required,
|
|
91
|
+
present,
|
|
92
|
+
empty,
|
|
93
|
+
raw,
|
|
94
|
+
items
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if (!sectionDef.required) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!present) {
|
|
102
|
+
missingSections.push(sectionDef.heading);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (empty) {
|
|
106
|
+
emptySections.push(sectionDef.heading);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
sections,
|
|
112
|
+
missingSections,
|
|
113
|
+
emptySections
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
RUNTIME_SPEC_SECTION_DEFS,
|
|
119
|
+
parseRuntimeSpecMarkdown
|
|
120
|
+
};
|
package/lib/audit.js
CHANGED
|
@@ -11,6 +11,7 @@ const { getSessionStatePath, readSessionState } = require("./pencil-session");
|
|
|
11
11
|
const { isPathInside, listFilesRecursiveSafe } = require("./fs-safety");
|
|
12
12
|
const { pathExists, readTextIfExists } = require("./utils");
|
|
13
13
|
const { runModuleExportInWorker } = require("./async-offload");
|
|
14
|
+
const { readExecutionSignals, summarizeSignalsBySurface } = require("./execution-signals");
|
|
14
15
|
const {
|
|
15
16
|
normalizeCheckpointLabel,
|
|
16
17
|
parseCheckpointStatusMap,
|
|
@@ -38,6 +39,13 @@ const EXPORT_SCAN_LIMITS = Object.freeze({
|
|
|
38
39
|
maxDepth: 4,
|
|
39
40
|
maxEntries: 1200
|
|
40
41
|
});
|
|
42
|
+
const PLANNING_SIGNAL_SURFACES = new Set(["lint-spec", "scope-check", "lint-tasks", "lint-bindings"]);
|
|
43
|
+
const VERIFICATION_SIGNAL_SURFACES = new Set([
|
|
44
|
+
"verify-bindings",
|
|
45
|
+
"verify-implementation",
|
|
46
|
+
"verify-structure",
|
|
47
|
+
"verify-coverage"
|
|
48
|
+
]);
|
|
41
49
|
|
|
42
50
|
function listFilesRecursive(rootDir, limits = {}) {
|
|
43
51
|
return listFilesRecursiveSafe(rootDir, {
|
|
@@ -253,6 +261,17 @@ function auditProject(projectPathInput, options = {}) {
|
|
|
253
261
|
|
|
254
262
|
if (!pathExists(daVinciDir)) {
|
|
255
263
|
failures.push("Missing `.da-vinci/` directory.");
|
|
264
|
+
addMissingArtifacts(projectRoot, [path.join(projectRoot, "DA-VINCI.md"), designsDir], failures);
|
|
265
|
+
const missingWorkflowArtifacts = [
|
|
266
|
+
path.join(daVinciDir, "project-inventory.md"),
|
|
267
|
+
path.join(daVinciDir, "page-map.md"),
|
|
268
|
+
designRegistryPath
|
|
269
|
+
];
|
|
270
|
+
if (mode === "completion") {
|
|
271
|
+
addMissingArtifacts(projectRoot, missingWorkflowArtifacts, failures);
|
|
272
|
+
} else {
|
|
273
|
+
addMissingArtifacts(projectRoot, missingWorkflowArtifacts, warnings);
|
|
274
|
+
}
|
|
256
275
|
notes.push(
|
|
257
276
|
`Hint: scaffold the required workflow files with \`${buildBootstrapCommand(projectRoot, mode, options.changeId || null)}\`.`
|
|
258
277
|
);
|
|
@@ -814,6 +833,48 @@ function auditProject(projectPathInput, options = {}) {
|
|
|
814
833
|
}
|
|
815
834
|
}
|
|
816
835
|
|
|
836
|
+
const signalSummary =
|
|
837
|
+
options && options.preloadedSignalSummary && typeof options.preloadedSignalSummary === "object"
|
|
838
|
+
? options.preloadedSignalSummary
|
|
839
|
+
: summarizeSignalsBySurface(
|
|
840
|
+
readExecutionSignals(projectRoot, {
|
|
841
|
+
changeId: options.changeId || ""
|
|
842
|
+
})
|
|
843
|
+
);
|
|
844
|
+
for (const [surface, signal] of Object.entries(signalSummary)) {
|
|
845
|
+
if (surface === "signal-file-parse") {
|
|
846
|
+
const warningMessage =
|
|
847
|
+
Array.isArray(signal.warnings) && signal.warnings[0]
|
|
848
|
+
? String(signal.warnings[0])
|
|
849
|
+
: "Malformed execution signal files were detected.";
|
|
850
|
+
pushUnique(warnings, warningMessage);
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
if (mode === "integrity" && PLANNING_SIGNAL_SURFACES.has(surface)) {
|
|
855
|
+
if (signal.status === "BLOCK") {
|
|
856
|
+
pushUnique(warnings, `Planning signal ${surface} is BLOCK (advisory in integrity mode).`);
|
|
857
|
+
} else if (signal.status === "WARN") {
|
|
858
|
+
pushUnique(notes, `Planning signal ${surface} is WARN.`);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (mode === "completion" && VERIFICATION_SIGNAL_SURFACES.has(surface)) {
|
|
863
|
+
if (signal.status === "BLOCK") {
|
|
864
|
+
failures.push(`Verification signal ${surface} is BLOCK.`);
|
|
865
|
+
} else if (signal.status === "WARN") {
|
|
866
|
+
pushUnique(warnings, `Verification signal ${surface} is WARN.`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (surface === "diff-spec" && signal.status !== "PASS") {
|
|
871
|
+
pushUnique(warnings, `Planning diff signal ${surface} reports ${signal.status}.`);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
notes.push(
|
|
875
|
+
"Execution-chain signal integration uses advisory planning semantics in integrity mode and blocking verification semantics in completion mode."
|
|
876
|
+
);
|
|
877
|
+
|
|
817
878
|
const status = failures.length > 0 ? "FAIL" : warnings.length > 0 ? "WARN" : "PASS";
|
|
818
879
|
|
|
819
880
|
return {
|
package/lib/cli.js
CHANGED
|
@@ -54,11 +54,35 @@ const {
|
|
|
54
54
|
bootstrapProjectArtifacts,
|
|
55
55
|
formatBootstrapProjectReport
|
|
56
56
|
} = require("./workflow-bootstrap");
|
|
57
|
+
const {
|
|
58
|
+
deriveWorkflowStatus,
|
|
59
|
+
formatWorkflowStatusReport,
|
|
60
|
+
formatNextStepReport
|
|
61
|
+
} = require("./workflow-state");
|
|
62
|
+
const { lintRuntimeSpecs, formatLintSpecReport } = require("./lint-spec");
|
|
63
|
+
const { runScopeCheck, formatScopeCheckReport } = require("./scope-check");
|
|
64
|
+
const { lintTasks, formatLintTasksReport } = require("./lint-tasks");
|
|
65
|
+
const { lintBindings, formatLintBindingsReport } = require("./lint-bindings");
|
|
66
|
+
const { generatePlanningSidecars, formatGenerateSidecarsReport } = require("./sidecars");
|
|
67
|
+
const {
|
|
68
|
+
verifyBindings,
|
|
69
|
+
verifyImplementation,
|
|
70
|
+
verifyStructure,
|
|
71
|
+
verifyCoverage,
|
|
72
|
+
formatVerifyReport
|
|
73
|
+
} = require("./verify");
|
|
74
|
+
const { diffSpec, formatDiffSpecReport } = require("./diff-spec");
|
|
75
|
+
const { scaffoldFromBindings, formatScaffoldReport } = require("./scaffold");
|
|
76
|
+
const { writeExecutionSignal } = require("./execution-signals");
|
|
77
|
+
const { formatTuiHelp, launchTui } = require("../tui");
|
|
57
78
|
|
|
58
79
|
const DEFAULT_MAX_PREFLIGHT_STDIN_BYTES = 1024 * 1024;
|
|
80
|
+
const DEFAULT_MAX_STDIN_TRANSIENT_RETRIES = 2000;
|
|
81
|
+
const DEFAULT_MAX_STDIN_TRANSIENT_BACKOFF_MS = 25;
|
|
59
82
|
const OPTION_FLAGS_WITH_VALUES = new Set([
|
|
60
83
|
"--home",
|
|
61
84
|
"--platform",
|
|
85
|
+
"--lang",
|
|
62
86
|
"--project",
|
|
63
87
|
"--mode",
|
|
64
88
|
"--change",
|
|
@@ -98,6 +122,7 @@ const OPTION_FLAGS_WITH_VALUES = new Set([
|
|
|
98
122
|
|
|
99
123
|
const HELP_OPTION_SPECS = [
|
|
100
124
|
{ flag: "--platform <value>", description: "codex, claude, gemini, or all" },
|
|
125
|
+
{ flag: "--lang <value>", description: "ui language for `da-vinci tui`: en or zh" },
|
|
101
126
|
{ flag: "--home <path>", description: "override HOME for installation targets" },
|
|
102
127
|
{ flag: "--project <path>", description: "override project path for audit" },
|
|
103
128
|
{
|
|
@@ -159,10 +184,20 @@ const HELP_OPTION_SPECS = [
|
|
|
159
184
|
{ flag: "--revision-outcome <text>", description: "supervisor-review revision result summary" },
|
|
160
185
|
{ flag: "--top <value>", description: "icon-search result count (1-50, default 8)" },
|
|
161
186
|
{ flag: "--timeout-ms <value>", description: "network timeout for icon-sync requests" },
|
|
162
|
-
{
|
|
163
|
-
|
|
187
|
+
{
|
|
188
|
+
flag: "--strict",
|
|
189
|
+
description: "enable strict failure mode for commands that support advisory defaults (for example icon-sync, lint-spec)"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
flag: "--continue-on-error",
|
|
193
|
+
description: "print BLOCK/FAIL command results without throwing process errors"
|
|
194
|
+
},
|
|
195
|
+
{ flag: "--json", description: "print structured JSON output when supported by the command" },
|
|
164
196
|
{ flag: "--pen <path>", description: "registered .pen path for sync checks" },
|
|
165
|
-
{
|
|
197
|
+
{
|
|
198
|
+
flag: "--from <path>",
|
|
199
|
+
description: "source path for sync-pen-source, or baseline sidecars directory for diff-spec"
|
|
200
|
+
},
|
|
166
201
|
{ flag: "--to <path>", description: "destination .pen path for sync-pen-source" },
|
|
167
202
|
{ flag: "--ops-file <path>", description: "Pencil batch operations file for preflight" },
|
|
168
203
|
{ flag: "--input <path>", description: "input .pen file for snapshot-pen" },
|
|
@@ -178,11 +213,19 @@ const HELP_OPTION_SPECS = [
|
|
|
178
213
|
{ flag: "--force", description: "overwrite bootstrap placeholders or force commands that explicitly support it" }
|
|
179
214
|
];
|
|
180
215
|
|
|
181
|
-
function readLimitedStdin(maxBytes = DEFAULT_MAX_PREFLIGHT_STDIN_BYTES) {
|
|
216
|
+
function readLimitedStdin(maxBytes = DEFAULT_MAX_PREFLIGHT_STDIN_BYTES, options = {}) {
|
|
182
217
|
const limit =
|
|
183
218
|
Number.isFinite(Number(maxBytes)) && Number(maxBytes) > 0
|
|
184
219
|
? Number(maxBytes)
|
|
185
220
|
: DEFAULT_MAX_PREFLIGHT_STDIN_BYTES;
|
|
221
|
+
const maxTransientRetries =
|
|
222
|
+
Number.isFinite(Number(options.maxTransientRetries)) && Number(options.maxTransientRetries) >= 0
|
|
223
|
+
? Number(options.maxTransientRetries)
|
|
224
|
+
: DEFAULT_MAX_STDIN_TRANSIENT_RETRIES;
|
|
225
|
+
const maxBackoffMs =
|
|
226
|
+
Number.isFinite(Number(options.maxBackoffMs)) && Number(options.maxBackoffMs) > 0
|
|
227
|
+
? Number(options.maxBackoffMs)
|
|
228
|
+
: DEFAULT_MAX_STDIN_TRANSIENT_BACKOFF_MS;
|
|
186
229
|
const chunks = [];
|
|
187
230
|
let totalBytes = 0;
|
|
188
231
|
let transientReadRetries = 0;
|
|
@@ -195,7 +238,12 @@ function readLimitedStdin(maxBytes = DEFAULT_MAX_PREFLIGHT_STDIN_BYTES) {
|
|
|
195
238
|
} catch (error) {
|
|
196
239
|
if (error && (error.code === "EAGAIN" || error.code === "EINTR")) {
|
|
197
240
|
transientReadRetries += 1;
|
|
198
|
-
|
|
241
|
+
if (transientReadRetries > maxTransientRetries) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`Unable to read stdin after ${transientReadRetries} transient retry attempts (${error.code}).`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
sleepSyncMs(Math.min(5 * transientReadRetries, maxBackoffMs));
|
|
199
247
|
continue;
|
|
200
248
|
}
|
|
201
249
|
throw error;
|
|
@@ -285,6 +333,21 @@ function getOptionValues(args, name) {
|
|
|
285
333
|
.map((entry) => entry.value);
|
|
286
334
|
}
|
|
287
335
|
|
|
336
|
+
function shouldContinueOnError(args) {
|
|
337
|
+
return Array.isArray(args) && args.includes("--continue-on-error");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function emitOrThrowOnStatus(status, blockedStatuses, output, continueOnError) {
|
|
341
|
+
if (!Array.isArray(blockedStatuses) || !blockedStatuses.includes(status)) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
if (continueOnError) {
|
|
345
|
+
console.log(output);
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
throw new Error(output);
|
|
349
|
+
}
|
|
350
|
+
|
|
288
351
|
function getIntegerOption(args, name, options = {}) {
|
|
289
352
|
const raw = getOption(args, name);
|
|
290
353
|
if (raw === undefined) {
|
|
@@ -372,6 +435,32 @@ function appendStatusIssues(lines, label, missing = [], mismatched = [], unreada
|
|
|
372
435
|
}
|
|
373
436
|
}
|
|
374
437
|
|
|
438
|
+
function persistExecutionSignal(projectPath, changeId, surface, result, strict = false) {
|
|
439
|
+
try {
|
|
440
|
+
writeExecutionSignal(projectPath, {
|
|
441
|
+
changeId: changeId || "global",
|
|
442
|
+
surface,
|
|
443
|
+
status: result.status,
|
|
444
|
+
advisory: strict ? false : true,
|
|
445
|
+
strict,
|
|
446
|
+
failures: result.failures || [],
|
|
447
|
+
warnings: result.warnings || [],
|
|
448
|
+
notes: result.notes || []
|
|
449
|
+
});
|
|
450
|
+
} catch (error) {
|
|
451
|
+
// Signals are advisory metadata and should not break command execution.
|
|
452
|
+
const code = error && error.code ? String(error.code).toUpperCase() : "";
|
|
453
|
+
if (code === "EACCES" || code === "ENOSPC") {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const message = error && error.message ? error.message : String(error);
|
|
458
|
+
console.error(
|
|
459
|
+
`Warning: failed to persist execution signal (${surface}) for change ${changeId || "global"}: ${message}`
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
375
464
|
function printHelp() {
|
|
376
465
|
const optionLines = HELP_OPTION_SPECS.map((optionSpec) => {
|
|
377
466
|
const paddedFlag = optionSpec.flag.padEnd(30, " ");
|
|
@@ -386,6 +475,20 @@ function printHelp() {
|
|
|
386
475
|
" da-vinci install --platform codex,claude,gemini",
|
|
387
476
|
" da-vinci uninstall --platform codex,claude,gemini",
|
|
388
477
|
" da-vinci status",
|
|
478
|
+
" da-vinci tui [--project <path>] [--change <id>] [--lang en|zh] [--strict] [--json] [--continue-on-error]",
|
|
479
|
+
" da-vinci workflow-status [--project <path>] [--change <id>] [--json]",
|
|
480
|
+
" da-vinci next-step [--project <path>] [--change <id>] [--json]",
|
|
481
|
+
" da-vinci lint-spec [--project <path>] [--change <id>] [--strict] [--json]",
|
|
482
|
+
" da-vinci scope-check [--project <path>] [--change <id>] [--strict] [--json]",
|
|
483
|
+
" da-vinci lint-tasks [--project <path>] [--change <id>] [--strict] [--json]",
|
|
484
|
+
" da-vinci lint-bindings [--project <path>] [--change <id>] [--strict] [--json]",
|
|
485
|
+
" da-vinci generate-sidecars [--project <path>] [--change <id>] [--json]",
|
|
486
|
+
" da-vinci verify-bindings [--project <path>] [--change <id>] [--strict] [--json]",
|
|
487
|
+
" da-vinci verify-implementation [--project <path>] [--change <id>] [--strict] [--json]",
|
|
488
|
+
" da-vinci verify-structure [--project <path>] [--change <id>] [--strict] [--json]",
|
|
489
|
+
" da-vinci verify-coverage [--project <path>] [--change <id>] [--strict] [--json]",
|
|
490
|
+
" da-vinci diff-spec [--project <path>] [--change <id>] [--from <sidecars-dir>] [--json]",
|
|
491
|
+
" da-vinci scaffold [--project <path>] [--change <id>] [--output <path>] [--json]",
|
|
389
492
|
" da-vinci validate-assets",
|
|
390
493
|
" da-vinci bootstrap-project --project <path> [--change <id>] [--force]",
|
|
391
494
|
" da-vinci audit [project-path]",
|
|
@@ -803,6 +906,7 @@ async function runCli(argv) {
|
|
|
803
906
|
const [command] = argv;
|
|
804
907
|
const homeDir = getOption(argv, "--home");
|
|
805
908
|
const positionalArgs = getPositionalArgs(argv.slice(1), OPTION_FLAGS_WITH_VALUES);
|
|
909
|
+
const continueOnError = shouldContinueOnError(argv);
|
|
806
910
|
|
|
807
911
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
808
912
|
printHelp();
|
|
@@ -837,6 +941,235 @@ async function runCli(argv) {
|
|
|
837
941
|
return;
|
|
838
942
|
}
|
|
839
943
|
|
|
944
|
+
if (command === "tui") {
|
|
945
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
946
|
+
const changeId = getOption(argv, "--change");
|
|
947
|
+
const lang = getOption(argv, "--lang");
|
|
948
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
949
|
+
console.log(formatTuiHelp(lang));
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
await launchTui({
|
|
953
|
+
projectPath,
|
|
954
|
+
changeId,
|
|
955
|
+
lang,
|
|
956
|
+
strict: argv.includes("--strict"),
|
|
957
|
+
jsonOutput: argv.includes("--json"),
|
|
958
|
+
continueOnError
|
|
959
|
+
});
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
if (command === "workflow-status") {
|
|
964
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
965
|
+
const changeId = getOption(argv, "--change");
|
|
966
|
+
const result = deriveWorkflowStatus(projectPath, { changeId });
|
|
967
|
+
|
|
968
|
+
if (argv.includes("--json")) {
|
|
969
|
+
console.log(JSON.stringify(result, null, 2));
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
console.log(formatWorkflowStatusReport(result));
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
if (command === "next-step") {
|
|
978
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
979
|
+
const changeId = getOption(argv, "--change");
|
|
980
|
+
const result = deriveWorkflowStatus(projectPath, { changeId });
|
|
981
|
+
|
|
982
|
+
if (argv.includes("--json")) {
|
|
983
|
+
console.log(JSON.stringify(result.nextStep || null, null, 2));
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
console.log(formatNextStepReport(result));
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
if (command === "lint-spec") {
|
|
992
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
993
|
+
const changeId = getOption(argv, "--change");
|
|
994
|
+
const strict = argv.includes("--strict");
|
|
995
|
+
const result = lintRuntimeSpecs(projectPath, { changeId, strict });
|
|
996
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "lint-spec", result, strict);
|
|
997
|
+
const useJson = argv.includes("--json");
|
|
998
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatLintSpecReport(result);
|
|
999
|
+
|
|
1000
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
console.log(output);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (command === "scope-check") {
|
|
1009
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1010
|
+
const changeId = getOption(argv, "--change");
|
|
1011
|
+
const strict = argv.includes("--strict");
|
|
1012
|
+
const result = runScopeCheck(projectPath, { changeId, strict });
|
|
1013
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "scope-check", result, strict);
|
|
1014
|
+
const useJson = argv.includes("--json");
|
|
1015
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatScopeCheckReport(result);
|
|
1016
|
+
|
|
1017
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
console.log(output);
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
if (command === "lint-tasks") {
|
|
1026
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1027
|
+
const changeId = getOption(argv, "--change");
|
|
1028
|
+
const strict = argv.includes("--strict");
|
|
1029
|
+
const result = lintTasks(projectPath, { changeId, strict });
|
|
1030
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "lint-tasks", result, strict);
|
|
1031
|
+
const useJson = argv.includes("--json");
|
|
1032
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatLintTasksReport(result);
|
|
1033
|
+
|
|
1034
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
console.log(output);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
if (command === "lint-bindings") {
|
|
1043
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1044
|
+
const changeId = getOption(argv, "--change");
|
|
1045
|
+
const strict = argv.includes("--strict");
|
|
1046
|
+
const result = lintBindings(projectPath, { changeId, strict });
|
|
1047
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "lint-bindings", result, strict);
|
|
1048
|
+
const useJson = argv.includes("--json");
|
|
1049
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatLintBindingsReport(result);
|
|
1050
|
+
|
|
1051
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
console.log(output);
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
if (command === "generate-sidecars") {
|
|
1060
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1061
|
+
const changeId = getOption(argv, "--change");
|
|
1062
|
+
const result = generatePlanningSidecars(projectPath, { changeId, write: true });
|
|
1063
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "generate-sidecars", result, false);
|
|
1064
|
+
const useJson = argv.includes("--json");
|
|
1065
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatGenerateSidecarsReport(result);
|
|
1066
|
+
|
|
1067
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
console.log(output);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
if (command === "verify-bindings") {
|
|
1076
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1077
|
+
const changeId = getOption(argv, "--change");
|
|
1078
|
+
const strict = argv.includes("--strict");
|
|
1079
|
+
const result = verifyBindings(projectPath, { changeId, strict });
|
|
1080
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "verify-bindings", result, strict);
|
|
1081
|
+
const useJson = argv.includes("--json");
|
|
1082
|
+
const output = useJson
|
|
1083
|
+
? JSON.stringify(result, null, 2)
|
|
1084
|
+
: formatVerifyReport(result, "Da Vinci verify-bindings");
|
|
1085
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
console.log(output);
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
if (command === "verify-implementation") {
|
|
1093
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1094
|
+
const changeId = getOption(argv, "--change");
|
|
1095
|
+
const strict = argv.includes("--strict");
|
|
1096
|
+
const result = verifyImplementation(projectPath, { changeId, strict });
|
|
1097
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "verify-implementation", result, strict);
|
|
1098
|
+
const useJson = argv.includes("--json");
|
|
1099
|
+
const output = useJson
|
|
1100
|
+
? JSON.stringify(result, null, 2)
|
|
1101
|
+
: formatVerifyReport(result, "Da Vinci verify-implementation");
|
|
1102
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
console.log(output);
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (command === "verify-structure") {
|
|
1110
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1111
|
+
const changeId = getOption(argv, "--change");
|
|
1112
|
+
const strict = argv.includes("--strict");
|
|
1113
|
+
const result = verifyStructure(projectPath, { changeId, strict });
|
|
1114
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "verify-structure", result, strict);
|
|
1115
|
+
const useJson = argv.includes("--json");
|
|
1116
|
+
const output = useJson
|
|
1117
|
+
? JSON.stringify(result, null, 2)
|
|
1118
|
+
: formatVerifyReport(result, "Da Vinci verify-structure");
|
|
1119
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
console.log(output);
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (command === "verify-coverage") {
|
|
1127
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1128
|
+
const changeId = getOption(argv, "--change");
|
|
1129
|
+
const strict = argv.includes("--strict");
|
|
1130
|
+
const result = verifyCoverage(projectPath, { changeId, strict });
|
|
1131
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "verify-coverage", result, strict);
|
|
1132
|
+
const useJson = argv.includes("--json");
|
|
1133
|
+
const output = useJson
|
|
1134
|
+
? JSON.stringify(result, null, 2)
|
|
1135
|
+
: formatVerifyReport(result, "Da Vinci verify-coverage");
|
|
1136
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
console.log(output);
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if (command === "diff-spec") {
|
|
1144
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1145
|
+
const changeId = getOption(argv, "--change");
|
|
1146
|
+
const fromDir = getOption(argv, "--from");
|
|
1147
|
+
const result = diffSpec(projectPath, { changeId, fromDir });
|
|
1148
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "diff-spec", result, false);
|
|
1149
|
+
const useJson = argv.includes("--json");
|
|
1150
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatDiffSpecReport(result);
|
|
1151
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
console.log(output);
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
if (command === "scaffold") {
|
|
1159
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
1160
|
+
const changeId = getOption(argv, "--change");
|
|
1161
|
+
const outputDir = getOption(argv, "--output");
|
|
1162
|
+
const result = scaffoldFromBindings(projectPath, { changeId, outputDir });
|
|
1163
|
+
persistExecutionSignal(projectPath, result.changeId || changeId, "scaffold", result, false);
|
|
1164
|
+
const useJson = argv.includes("--json");
|
|
1165
|
+
const output = useJson ? JSON.stringify(result, null, 2) : formatScaffoldReport(result);
|
|
1166
|
+
if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
console.log(output);
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
840
1173
|
if (command === "validate-assets") {
|
|
841
1174
|
const result = validateAssets();
|
|
842
1175
|
console.log(`Da Vinci v${result.version} assets are complete (${result.requiredAssets} required files).`);
|
|
@@ -850,8 +1183,8 @@ async function runCli(argv) {
|
|
|
850
1183
|
const result = auditProject(projectPath, { mode, changeId });
|
|
851
1184
|
const report = formatAuditReport(result);
|
|
852
1185
|
|
|
853
|
-
if (result.status
|
|
854
|
-
|
|
1186
|
+
if (emitOrThrowOnStatus(result.status, ["FAIL"], report, continueOnError)) {
|
|
1187
|
+
return;
|
|
855
1188
|
}
|
|
856
1189
|
|
|
857
1190
|
console.log(report);
|
|
@@ -903,8 +1236,8 @@ async function runCli(argv) {
|
|
|
903
1236
|
const result = preflightPencilBatch(operations);
|
|
904
1237
|
const report = formatPencilPreflightReport(result);
|
|
905
1238
|
|
|
906
|
-
if (result.status
|
|
907
|
-
|
|
1239
|
+
if (emitOrThrowOnStatus(result.status, ["FAIL"], report, continueOnError)) {
|
|
1240
|
+
return;
|
|
908
1241
|
}
|
|
909
1242
|
|
|
910
1243
|
console.log(report);
|
|
@@ -1028,13 +1361,18 @@ async function runCli(argv) {
|
|
|
1028
1361
|
preferredSource
|
|
1029
1362
|
});
|
|
1030
1363
|
|
|
1031
|
-
if (
|
|
1032
|
-
|
|
1364
|
+
if (
|
|
1365
|
+
emitOrThrowOnStatus(
|
|
1366
|
+
result.status,
|
|
1367
|
+
["BLOCK"],
|
|
1033
1368
|
[
|
|
1034
1369
|
"Baseline alignment check failed.",
|
|
1035
1370
|
formatPenBaselineAlignmentReport(result)
|
|
1036
|
-
].join("\n")
|
|
1037
|
-
|
|
1371
|
+
].join("\n"),
|
|
1372
|
+
continueOnError
|
|
1373
|
+
)
|
|
1374
|
+
) {
|
|
1375
|
+
return;
|
|
1038
1376
|
}
|
|
1039
1377
|
|
|
1040
1378
|
console.log(formatPenBaselineAlignmentReport(result));
|