@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +28 -65
  3. package/README.zh-CN.md +28 -65
  4. package/bin/da-vinci-tui.js +8 -0
  5. package/commands/claude/dv/continue.md +5 -0
  6. package/commands/codex/prompts/dv-continue.md +6 -1
  7. package/commands/gemini/dv/continue.toml +5 -0
  8. package/commands/templates/dv-continue.shared.md +33 -0
  9. package/docs/dv-command-reference.md +35 -0
  10. package/docs/execution-chain-migration.md +46 -0
  11. package/docs/execution-chain-plan.md +125 -0
  12. package/docs/prompt-entrypoints.md +8 -0
  13. package/docs/skill-usage.md +217 -0
  14. package/docs/workflow-examples.md +10 -0
  15. package/docs/workflow-overview.md +26 -0
  16. package/docs/zh-CN/dv-command-reference.md +35 -0
  17. package/docs/zh-CN/execution-chain-migration.md +46 -0
  18. package/docs/zh-CN/prompt-entrypoints.md +8 -0
  19. package/docs/zh-CN/skill-usage.md +217 -0
  20. package/docs/zh-CN/workflow-examples.md +10 -0
  21. package/docs/zh-CN/workflow-overview.md +26 -0
  22. package/lib/artifact-parsers.js +120 -0
  23. package/lib/audit.js +61 -0
  24. package/lib/cli.js +351 -13
  25. package/lib/diff-spec.js +242 -0
  26. package/lib/execution-signals.js +136 -0
  27. package/lib/lint-bindings.js +143 -0
  28. package/lib/lint-spec.js +408 -0
  29. package/lib/lint-tasks.js +176 -0
  30. package/lib/planning-parsers.js +567 -0
  31. package/lib/scaffold.js +193 -0
  32. package/lib/scope-check.js +603 -0
  33. package/lib/sidecars.js +369 -0
  34. package/lib/supervisor-review.js +28 -3
  35. package/lib/utils.js +10 -2
  36. package/lib/verify.js +652 -0
  37. package/lib/workflow-contract.js +107 -0
  38. package/lib/workflow-persisted-state.js +297 -0
  39. package/lib/workflow-state.js +785 -0
  40. package/package.json +13 -3
  41. package/references/artifact-templates.md +26 -0
  42. package/references/checkpoints.md +14 -0
  43. package/references/modes.md +10 -0
  44. package/tui/catalog.js +1190 -0
  45. 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
- { flag: "--strict", description: "fail icon-sync when any upstream source request fails" },
163
- { flag: "--json", description: "print structured JSON for icon-search" },
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
- { flag: "--from <path>", description: "source .pen path for sync-pen-source" },
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
- sleepSyncMs(Math.min(5 * transientReadRetries, 25));
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 === "FAIL") {
854
- throw new Error(report);
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 === "FAIL") {
907
- throw new Error(report);
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 (result.status === "BLOCK") {
1032
- throw new Error(
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));