@xenonbyte/da-vinci-workflow 0.2.4 → 0.2.6

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 (42) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +15 -9
  3. package/README.zh-CN.md +16 -9
  4. package/SKILL.md +45 -704
  5. package/docs/dv-command-reference.md +33 -5
  6. package/docs/execution-chain-migration.md +14 -3
  7. package/docs/maintainer-bootstrap.md +102 -0
  8. package/docs/pencil-rendering-workflow.md +1 -1
  9. package/docs/prompt-entrypoints.md +1 -0
  10. package/docs/skill-contract-maintenance.md +14 -0
  11. package/docs/skill-usage.md +31 -0
  12. package/docs/workflow-overview.md +40 -5
  13. package/docs/zh-CN/dv-command-reference.md +31 -5
  14. package/docs/zh-CN/maintainer-bootstrap.md +101 -0
  15. package/docs/zh-CN/pencil-rendering-workflow.md +1 -1
  16. package/docs/zh-CN/prompt-entrypoints.md +1 -0
  17. package/docs/zh-CN/skill-usage.md +30 -0
  18. package/docs/zh-CN/workflow-overview.md +38 -5
  19. package/lib/audit.js +19 -0
  20. package/lib/cli/helpers.js +104 -0
  21. package/lib/cli/lint-family.js +56 -0
  22. package/lib/cli/verify-family.js +79 -0
  23. package/lib/cli.js +143 -172
  24. package/lib/gate-utils.js +56 -0
  25. package/lib/install.js +134 -6
  26. package/lib/lint-bindings.js +41 -28
  27. package/lib/lint-spec.js +403 -109
  28. package/lib/lint-tasks.js +571 -21
  29. package/lib/maintainer-readiness.js +317 -0
  30. package/lib/planning-parsers.js +198 -2
  31. package/lib/planning-quality-utils.js +81 -0
  32. package/lib/planning-signal-freshness.js +205 -0
  33. package/lib/scaffold.js +454 -23
  34. package/lib/scope-check.js +751 -82
  35. package/lib/sidecars.js +396 -1
  36. package/lib/task-review.js +2 -1
  37. package/lib/utils.js +34 -0
  38. package/lib/verify.js +1160 -88
  39. package/lib/workflow-persisted-state.js +52 -32
  40. package/lib/workflow-state.js +1187 -249
  41. package/package.json +1 -1
  42. package/references/skill-workflow-detail.md +66 -0
package/lib/cli.js CHANGED
@@ -4,6 +4,7 @@ const {
4
4
  installPlatforms,
5
5
  uninstallPlatforms,
6
6
  getStatus,
7
+ verifyInstall,
7
8
  validateAssets
8
9
  } = require("./install");
9
10
  const { auditProject, formatAuditReport } = require("./audit");
@@ -78,7 +79,9 @@ const {
78
79
  } = require("./verify");
79
80
  const { diffSpec, formatDiffSpecReport } = require("./diff-spec");
80
81
  const { scaffoldFromBindings, formatScaffoldReport } = require("./scaffold");
81
- const { writeExecutionSignal } = require("./execution-signals");
82
+ const { emitOrThrowOnStatus, persistExecutionSignal } = require("./cli/helpers");
83
+ const { handleVerifyFamilyCommand } = require("./cli/verify-family");
84
+ const { handleLintFamilyCommand } = require("./cli/lint-family");
82
85
  const {
83
86
  writeTaskExecutionEnvelope,
84
87
  formatTaskExecutionReport
@@ -92,6 +95,10 @@ const {
92
95
  formatWorktreePreflightReport
93
96
  } = require("./worktree-preflight");
94
97
  const { formatTuiHelp, launchTui } = require("../tui");
98
+ const {
99
+ runMaintainerReadinessCheck,
100
+ formatMaintainerReadinessReport
101
+ } = require("./maintainer-readiness");
95
102
 
96
103
  const DEFAULT_MAX_PREFLIGHT_STDIN_BYTES = 1024 * 1024;
97
104
  const DEFAULT_MAX_STDIN_TRANSIENT_RETRIES = 2000;
@@ -187,7 +194,10 @@ const HELP_OPTION_SPECS = [
187
194
  { flag: "--stage <value>", description: "task-review stage: spec or quality" },
188
195
  { flag: "--summary <text>", description: "task-execution/task-review summary text" },
189
196
  { flag: "--task-group <id>", description: "task group identifier for task-execution/task-review" },
190
- { flag: "--changed-files <csv>", description: "comma-separated changed files for task-execution" },
197
+ {
198
+ flag: "--changed-files <csv>",
199
+ description: "comma-separated changed files for verify-implementation/verify-structure/verify-coverage/task-execution"
200
+ },
191
201
  { flag: "--test-evidence <csv>", description: "comma-separated test evidence commands for task-execution" },
192
202
  { flag: "--concerns <csv>", description: "comma-separated concern text for task-execution" },
193
203
  { flag: "--blockers <csv>", description: "comma-separated blocker text for task-execution" },
@@ -379,17 +389,6 @@ function shouldContinueOnError(args) {
379
389
  return Array.isArray(args) && args.includes("--continue-on-error");
380
390
  }
381
391
 
382
- function emitOrThrowOnStatus(status, blockedStatuses, output, continueOnError) {
383
- if (!Array.isArray(blockedStatuses) || !blockedStatuses.includes(status)) {
384
- return false;
385
- }
386
- if (continueOnError) {
387
- console.log(output);
388
- return true;
389
- }
390
- throw new Error(output);
391
- }
392
-
393
392
  function getIntegerOption(args, name, options = {}) {
394
393
  const raw = getOption(args, name);
395
394
  if (raw === undefined) {
@@ -465,6 +464,60 @@ function formatStatus(status) {
465
464
  return lines.join("\n");
466
465
  }
467
466
 
467
+ function formatVerifyInstallReport(result) {
468
+ const scopeLabel = result.scope && result.scope.known
469
+ ? result.scope.selectedPlatforms.join(", ")
470
+ : "unknown";
471
+ const lines = [
472
+ "Da Vinci verify-install",
473
+ `Status: ${result.status}`,
474
+ `Home: ${result.homeDir}`,
475
+ "Surface: install verification (bootstrap-adjacent), not full repository readiness",
476
+ `Selected platform scope: ${scopeLabel}`,
477
+ "Platform coverage:"
478
+ ];
479
+
480
+ for (const platform of result.scope.supportedPlatforms || []) {
481
+ const coverage = result.platformCoverage && result.platformCoverage[platform];
482
+ if (!coverage) {
483
+ continue;
484
+ }
485
+ const checkSummary = Object.entries(coverage.checks || {})
486
+ .map(([name, ok]) => `${name}=${ok ? "yes" : "no"}`)
487
+ .join(" ");
488
+ lines.push(
489
+ `- ${platform}: scope=${coverage.scope} healthy=${coverage.healthy ? "yes" : "no"}${checkSummary ? ` ${checkSummary}` : ""}`
490
+ );
491
+ }
492
+
493
+ if (Array.isArray(result.failures) && result.failures.length > 0) {
494
+ lines.push("Failures:");
495
+ for (const message of result.failures) {
496
+ lines.push(`- ${message}`);
497
+ }
498
+ }
499
+ if (Array.isArray(result.warnings) && result.warnings.length > 0) {
500
+ lines.push("Warnings:");
501
+ for (const message of result.warnings) {
502
+ lines.push(`- ${message}`);
503
+ }
504
+ }
505
+ if (Array.isArray(result.notes) && result.notes.length > 0) {
506
+ lines.push("Notes:");
507
+ for (const message of result.notes) {
508
+ lines.push(`- ${message}`);
509
+ }
510
+ }
511
+ if (Array.isArray(result.nextSteps) && result.nextSteps.length > 0) {
512
+ lines.push("Next steps:");
513
+ for (const step of result.nextSteps) {
514
+ lines.push(`- ${step}`);
515
+ }
516
+ }
517
+
518
+ return lines.join("\n");
519
+ }
520
+
468
521
  function appendStatusIssues(lines, label, missing = [], mismatched = [], unreadable = []) {
469
522
  if (missing.length > 0) {
470
523
  lines.push(` ${label} missing: ${missing.join(", ")}`);
@@ -477,32 +530,6 @@ function appendStatusIssues(lines, label, missing = [], mismatched = [], unreada
477
530
  }
478
531
  }
479
532
 
480
- function persistExecutionSignal(projectPath, changeId, surface, result, strict = false) {
481
- try {
482
- writeExecutionSignal(projectPath, {
483
- changeId: changeId || "global",
484
- surface,
485
- status: result.status,
486
- advisory: strict ? false : true,
487
- strict,
488
- failures: result.failures || [],
489
- warnings: result.warnings || [],
490
- notes: result.notes || []
491
- });
492
- } catch (error) {
493
- // Signals are advisory metadata and should not break command execution.
494
- const code = error && error.code ? String(error.code).toUpperCase() : "";
495
- if (code === "EACCES" || code === "ENOSPC") {
496
- return;
497
- }
498
-
499
- const message = error && error.message ? error.message : String(error);
500
- console.error(
501
- `Warning: failed to persist execution signal (${surface}) for change ${changeId || "global"}: ${message}`
502
- );
503
- }
504
- }
505
-
506
533
  function printHelp() {
507
534
  const optionLines = HELP_OPTION_SPECS.map((optionSpec) => {
508
535
  const paddedFlag = optionSpec.flag.padEnd(30, " ");
@@ -517,6 +544,10 @@ function printHelp() {
517
544
  " da-vinci install --platform codex,claude,gemini",
518
545
  " da-vinci uninstall --platform codex,claude,gemini",
519
546
  " da-vinci status",
547
+ " da-vinci verify-install [--platform <value>] [--json]",
548
+ " (bootstrap-adjacent install verification for selected maintainer platforms)",
549
+ " da-vinci maintainer-readiness [--platform <value>] [--project <path>] [--json]",
550
+ " (canonical repository maintainer diagnosis/readiness; not downstream bootstrap)",
520
551
  " da-vinci tui [--project <path>] [--change <id>] [--lang en|zh] [--strict] [--json] [--continue-on-error] [--tui-width <cols>] [--alt-screen|--no-alt-screen]",
521
552
  " da-vinci workflow-status [--project <path>] [--change <id>] [--json]",
522
553
  " da-vinci next-step [--project <path>] [--change <id>] [--json]",
@@ -526,9 +557,9 @@ function printHelp() {
526
557
  " da-vinci lint-bindings [--project <path>] [--change <id>] [--strict] [--json]",
527
558
  " da-vinci generate-sidecars [--project <path>] [--change <id>] [--json]",
528
559
  " da-vinci verify-bindings [--project <path>] [--change <id>] [--strict] [--json]",
529
- " da-vinci verify-implementation [--project <path>] [--change <id>] [--strict] [--json]",
530
- " da-vinci verify-structure [--project <path>] [--change <id>] [--strict] [--json]",
531
- " da-vinci verify-coverage [--project <path>] [--change <id>] [--strict] [--json]",
560
+ " da-vinci verify-implementation [--project <path>] [--change <id>] [--changed-files <csv>] [--strict] [--json]",
561
+ " da-vinci verify-structure [--project <path>] [--change <id>] [--changed-files <csv>] [--strict] [--json]",
562
+ " da-vinci verify-coverage [--project <path>] [--change <id>] [--changed-files <csv>] [--strict] [--json]",
532
563
  " da-vinci task-execution --project <path> --change <id> --task-group <id> --status <DONE|DONE_WITH_CONCERNS|NEEDS_CONTEXT|BLOCKED> --summary <text> [--changed-files <csv>] [--test-evidence <csv>] [--concerns <csv>] [--blockers <csv>] [--json]",
533
564
  " da-vinci task-review --project <path> --change <id> --task-group <id> --stage <spec|quality> --status <PASS|WARN|BLOCK> --summary <text> [--issues <csv>] [--reviewer <name>] [--write-verification] [--json]",
534
565
  " da-vinci worktree-preflight --project <path> [--change <id>] [--json]",
@@ -987,6 +1018,40 @@ async function runCli(argv) {
987
1018
  return;
988
1019
  }
989
1020
 
1021
+ if (command === "verify-install") {
1022
+ const platformValue = getOption(argv, "--platform") || "";
1023
+ const result = verifyInstall({
1024
+ homeDir,
1025
+ platforms: platformValue
1026
+ });
1027
+ const output = argv.includes("--json")
1028
+ ? JSON.stringify(result, null, 2)
1029
+ : formatVerifyInstallReport(result);
1030
+ if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1031
+ return;
1032
+ }
1033
+ console.log(output);
1034
+ return;
1035
+ }
1036
+
1037
+ if (command === "maintainer-readiness") {
1038
+ const platformValue = getOption(argv, "--platform") || "";
1039
+ const repoRoot = getOption(argv, "--project") || process.cwd();
1040
+ const result = runMaintainerReadinessCheck({
1041
+ repoRoot,
1042
+ homeDir,
1043
+ platforms: platformValue
1044
+ });
1045
+ const output = argv.includes("--json")
1046
+ ? JSON.stringify(result, null, 2)
1047
+ : formatMaintainerReadinessReport(result);
1048
+ if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1049
+ return;
1050
+ }
1051
+ console.log(output);
1052
+ return;
1053
+ }
1054
+
990
1055
  if (command === "tui") {
991
1056
  const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
992
1057
  const changeId = getOption(argv, "--change");
@@ -1040,6 +1105,7 @@ async function runCli(argv) {
1040
1105
  stage: result.stage,
1041
1106
  checkpointState: result.checkpointState,
1042
1107
  nextStep: result.nextStep || null,
1108
+ blockingGate: result.blockingGate || null,
1043
1109
  discipline: result.discipline || null,
1044
1110
  executionProfile: result.executionProfile || null,
1045
1111
  worktreePreflight: result.worktreePreflight || null,
@@ -1056,71 +1122,24 @@ async function runCli(argv) {
1056
1122
  return;
1057
1123
  }
1058
1124
 
1059
- if (command === "lint-spec") {
1060
- const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
1061
- const changeId = getOption(argv, "--change");
1062
- const strict = argv.includes("--strict");
1063
- const result = lintRuntimeSpecs(projectPath, { changeId, strict });
1064
- persistExecutionSignal(projectPath, result.changeId || changeId, "lint-spec", result, strict);
1065
- const useJson = argv.includes("--json");
1066
- const output = useJson ? JSON.stringify(result, null, 2) : formatLintSpecReport(result);
1067
-
1068
- if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1069
- return;
1070
- }
1071
-
1072
- console.log(output);
1073
- return;
1074
- }
1075
-
1076
- if (command === "scope-check") {
1077
- const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
1078
- const changeId = getOption(argv, "--change");
1079
- const strict = argv.includes("--strict");
1080
- const result = runScopeCheck(projectPath, { changeId, strict });
1081
- persistExecutionSignal(projectPath, result.changeId || changeId, "scope-check", result, strict);
1082
- const useJson = argv.includes("--json");
1083
- const output = useJson ? JSON.stringify(result, null, 2) : formatScopeCheckReport(result);
1084
-
1085
- if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1086
- return;
1087
- }
1088
-
1089
- console.log(output);
1090
- return;
1091
- }
1092
-
1093
- if (command === "lint-tasks") {
1094
- const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
1095
- const changeId = getOption(argv, "--change");
1096
- const strict = argv.includes("--strict");
1097
- const result = lintTasks(projectPath, { changeId, strict });
1098
- persistExecutionSignal(projectPath, result.changeId || changeId, "lint-tasks", result, strict);
1099
- const useJson = argv.includes("--json");
1100
- const output = useJson ? JSON.stringify(result, null, 2) : formatLintTasksReport(result);
1101
-
1102
- if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1103
- return;
1104
- }
1105
-
1106
- console.log(output);
1107
- return;
1108
- }
1109
-
1110
- if (command === "lint-bindings") {
1111
- const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
1112
- const changeId = getOption(argv, "--change");
1113
- const strict = argv.includes("--strict");
1114
- const result = lintBindings(projectPath, { changeId, strict });
1115
- persistExecutionSignal(projectPath, result.changeId || changeId, "lint-bindings", result, strict);
1116
- const useJson = argv.includes("--json");
1117
- const output = useJson ? JSON.stringify(result, null, 2) : formatLintBindingsReport(result);
1118
-
1119
- if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1120
- return;
1121
- }
1122
-
1123
- console.log(output);
1125
+ if (
1126
+ handleLintFamilyCommand(command, {
1127
+ argv,
1128
+ positionalArgs,
1129
+ continueOnError,
1130
+ getOption,
1131
+ lintRuntimeSpecs,
1132
+ formatLintSpecReport,
1133
+ runScopeCheck,
1134
+ formatScopeCheckReport,
1135
+ lintTasks,
1136
+ formatLintTasksReport,
1137
+ lintBindings,
1138
+ formatLintBindingsReport,
1139
+ emitOrThrowOnStatus,
1140
+ persistExecutionSignal
1141
+ })
1142
+ ) {
1124
1143
  return;
1125
1144
  }
1126
1145
 
@@ -1140,71 +1159,23 @@ async function runCli(argv) {
1140
1159
  return;
1141
1160
  }
1142
1161
 
1143
- if (command === "verify-bindings") {
1144
- const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
1145
- const changeId = getOption(argv, "--change");
1146
- const strict = argv.includes("--strict");
1147
- const result = verifyBindings(projectPath, { changeId, strict });
1148
- persistExecutionSignal(projectPath, result.changeId || changeId, "verify-bindings", result, strict);
1149
- const useJson = argv.includes("--json");
1150
- const output = useJson
1151
- ? JSON.stringify(result, null, 2)
1152
- : formatVerifyReport(result, "Da Vinci verify-bindings");
1153
- if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1154
- return;
1155
- }
1156
- console.log(output);
1157
- return;
1158
- }
1159
-
1160
- if (command === "verify-implementation") {
1161
- const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
1162
- const changeId = getOption(argv, "--change");
1163
- const strict = argv.includes("--strict");
1164
- const result = verifyImplementation(projectPath, { changeId, strict });
1165
- persistExecutionSignal(projectPath, result.changeId || changeId, "verify-implementation", result, strict);
1166
- const useJson = argv.includes("--json");
1167
- const output = useJson
1168
- ? JSON.stringify(result, null, 2)
1169
- : formatVerifyReport(result, "Da Vinci verify-implementation");
1170
- if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1171
- return;
1172
- }
1173
- console.log(output);
1174
- return;
1175
- }
1176
-
1177
- if (command === "verify-structure") {
1178
- const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
1179
- const changeId = getOption(argv, "--change");
1180
- const strict = argv.includes("--strict");
1181
- const result = verifyStructure(projectPath, { changeId, strict });
1182
- persistExecutionSignal(projectPath, result.changeId || changeId, "verify-structure", result, strict);
1183
- const useJson = argv.includes("--json");
1184
- const output = useJson
1185
- ? JSON.stringify(result, null, 2)
1186
- : formatVerifyReport(result, "Da Vinci verify-structure");
1187
- if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1188
- return;
1189
- }
1190
- console.log(output);
1191
- return;
1192
- }
1193
-
1194
- if (command === "verify-coverage") {
1195
- const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
1196
- const changeId = getOption(argv, "--change");
1197
- const strict = argv.includes("--strict");
1198
- const result = verifyCoverage(projectPath, { changeId, strict });
1199
- persistExecutionSignal(projectPath, result.changeId || changeId, "verify-coverage", result, strict);
1200
- const useJson = argv.includes("--json");
1201
- const output = useJson
1202
- ? JSON.stringify(result, null, 2)
1203
- : formatVerifyReport(result, "Da Vinci verify-coverage");
1204
- if (emitOrThrowOnStatus(result.status, ["BLOCK"], output, continueOnError)) {
1205
- return;
1206
- }
1207
- console.log(output);
1162
+ if (
1163
+ handleVerifyFamilyCommand(command, {
1164
+ argv,
1165
+ positionalArgs,
1166
+ continueOnError,
1167
+ getOption,
1168
+ getCommaSeparatedOptionValues,
1169
+ collectOptionEntries,
1170
+ verifyBindings,
1171
+ verifyImplementation,
1172
+ verifyStructure,
1173
+ verifyCoverage,
1174
+ formatVerifyReport,
1175
+ emitOrThrowOnStatus,
1176
+ persistExecutionSignal
1177
+ })
1178
+ ) {
1208
1179
  return;
1209
1180
  }
1210
1181
 
@@ -0,0 +1,56 @@
1
+ const { STATUS } = require("./workflow-contract");
2
+ const { unique } = require("./planning-parsers");
3
+
4
+ function dedupe(values) {
5
+ return unique(values);
6
+ }
7
+
8
+ function buildGateEnvelope(id, options = {}) {
9
+ const gate = {
10
+ id,
11
+ status: STATUS.PASS,
12
+ blocking: [],
13
+ advisory: [],
14
+ compatibility: [],
15
+ evidence: []
16
+ };
17
+ if (options.includeBounded) {
18
+ gate.bounded = [];
19
+ }
20
+ return gate;
21
+ }
22
+
23
+ function finalizeGateEnvelope(gate, options = {}) {
24
+ const strict = options.strict === true;
25
+ const includeBounded = options.includeBounded === true;
26
+ const warnOnBounded = options.warnOnBounded === true;
27
+ const warnOnCompatibility = options.warnOnCompatibility !== false;
28
+ const normalized = {
29
+ ...gate,
30
+ blocking: dedupe(gate && gate.blocking),
31
+ advisory: dedupe(gate && gate.advisory),
32
+ compatibility: dedupe(gate && gate.compatibility),
33
+ evidence: dedupe(gate && gate.evidence)
34
+ };
35
+ if (includeBounded || Array.isArray(gate && gate.bounded)) {
36
+ normalized.bounded = dedupe(gate && gate.bounded);
37
+ }
38
+
39
+ if (normalized.blocking.length > 0) {
40
+ normalized.status = strict ? STATUS.BLOCK : STATUS.WARN;
41
+ return normalized;
42
+ }
43
+
44
+ const hasWarnLevelFindings =
45
+ normalized.advisory.length > 0 ||
46
+ (warnOnCompatibility && normalized.compatibility.length > 0) ||
47
+ (warnOnBounded && Array.isArray(normalized.bounded) && normalized.bounded.length > 0);
48
+ normalized.status = hasWarnLevelFindings ? STATUS.WARN : STATUS.PASS;
49
+ return normalized;
50
+ }
51
+
52
+ module.exports = {
53
+ dedupe,
54
+ buildGateEnvelope,
55
+ finalizeGateEnvelope
56
+ };
package/lib/install.js CHANGED
@@ -18,6 +18,7 @@ const REQUIRED_FILES = [
18
18
  ...listFiles(path.join(REPO_ROOT, "docs")).map((filePath) => path.relative(REPO_ROOT, filePath)),
19
19
  ...listFiles(path.join(REPO_ROOT, "examples")).map((filePath) => path.relative(REPO_ROOT, filePath))
20
20
  ];
21
+ const SUPPORTED_PLATFORMS = ["codex", "claude", "gemini"];
21
22
 
22
23
  const CODEX_PROMPT_TARGET_PAIRS = listFiles(path.join(REPO_ROOT, "commands", "codex", "prompts")).map(
23
24
  (filePath) => ({
@@ -90,7 +91,7 @@ function resolveHome(homeDir) {
90
91
 
91
92
  function parsePlatforms(raw) {
92
93
  if (!raw || raw === "all") {
93
- return ["codex", "claude", "gemini"];
94
+ return SUPPORTED_PLATFORMS.slice();
94
95
  }
95
96
 
96
97
  const platforms = raw
@@ -99,7 +100,7 @@ function parsePlatforms(raw) {
99
100
  .filter(Boolean);
100
101
 
101
102
  const unique = [...new Set(platforms)];
102
- const invalid = unique.filter((value) => !["codex", "claude", "gemini"].includes(value));
103
+ const invalid = unique.filter((value) => !SUPPORTED_PLATFORMS.includes(value));
103
104
 
104
105
  if (invalid.length > 0) {
105
106
  throw new Error(`Unsupported platform value: ${invalid.join(", ")}`);
@@ -108,6 +109,129 @@ function parsePlatforms(raw) {
108
109
  return unique;
109
110
  }
110
111
 
112
+ function summarizePlatformInstallConfidence(statusPayload) {
113
+ const status = statusPayload && typeof statusPayload === "object" ? statusPayload : {};
114
+ const codex = status.codex && typeof status.codex === "object" ? status.codex : {};
115
+ const claude = status.claude && typeof status.claude === "object" ? status.claude : {};
116
+ const gemini = status.gemini && typeof status.gemini === "object" ? status.gemini : {};
117
+
118
+ return {
119
+ codex: {
120
+ checks: {
121
+ prompt: codex.prompt === true,
122
+ skill: codex.skill === true
123
+ },
124
+ missing: [...(codex.promptMissing || []), ...(codex.skillMissing || [])],
125
+ mismatched: [...(codex.promptMismatched || []), ...(codex.skillMismatched || [])],
126
+ unreadable: [...(codex.promptUnreadable || []), ...(codex.skillUnreadable || [])]
127
+ },
128
+ claude: {
129
+ checks: {
130
+ command: claude.command === true,
131
+ actionSet: claude.actionSet === true
132
+ },
133
+ missing: [...(claude.commandMissing || []), ...(claude.actionSetMissing || [])],
134
+ mismatched: [...(claude.commandMismatched || []), ...(claude.actionSetMismatched || [])],
135
+ unreadable: [...(claude.commandUnreadable || []), ...(claude.actionSetUnreadable || [])]
136
+ },
137
+ gemini: {
138
+ checks: {
139
+ command: gemini.command === true,
140
+ actionSet: gemini.actionSet === true
141
+ },
142
+ missing: [...(gemini.commandMissing || []), ...(gemini.actionSetMissing || [])],
143
+ mismatched: [...(gemini.commandMismatched || []), ...(gemini.actionSetMismatched || [])],
144
+ unreadable: [...(gemini.commandUnreadable || []), ...(gemini.actionSetUnreadable || [])]
145
+ }
146
+ };
147
+ }
148
+
149
+ function verifyInstall(options = {}) {
150
+ const homeDir = resolveHome(options.homeDir);
151
+ const explicitScopeRaw = String(options.platforms || "").trim();
152
+ const scopeKnown = explicitScopeRaw.length > 0;
153
+ const selectedPlatforms = scopeKnown ? parsePlatforms(explicitScopeRaw) : [];
154
+ const status = getStatus({ homeDir });
155
+ const confidence = summarizePlatformInstallConfidence(status);
156
+ const platformCoverage = {};
157
+ const failures = [];
158
+ const warnings = [];
159
+ const notes = [];
160
+
161
+ for (const platform of SUPPORTED_PLATFORMS) {
162
+ const selected = selectedPlatforms.includes(platform);
163
+ const scope = scopeKnown ? (selected ? "selected" : "out_of_scope") : "unknown";
164
+ const platformConfidence = confidence[platform];
165
+ const failedChecks = Object.keys(platformConfidence.checks).filter(
166
+ (checkKey) => platformConfidence.checks[checkKey] !== true
167
+ );
168
+ const healthy = failedChecks.length === 0;
169
+ platformCoverage[platform] = {
170
+ scope,
171
+ healthy,
172
+ checks: platformConfidence.checks,
173
+ missing: platformConfidence.missing,
174
+ mismatched: platformConfidence.mismatched,
175
+ unreadable: platformConfidence.unreadable
176
+ };
177
+
178
+ if (scope === "selected" && !healthy) {
179
+ failures.push(
180
+ `${platform} install verification failed for selected scope (${failedChecks.join(
181
+ ", "
182
+ )}). Run \`da-vinci install --platform ${platform}\` and re-run verify-install.`
183
+ );
184
+ }
185
+ if (scope === "unknown" && !healthy) {
186
+ warnings.push(
187
+ `${platform} install verification is incomplete, but selected platform scope is unknown.`
188
+ );
189
+ }
190
+ if (scope === "out_of_scope" && !healthy) {
191
+ notes.push(
192
+ `${platform} is out-of-scope for this verify-install run and is reported as degraded coverage.`
193
+ );
194
+ }
195
+ }
196
+
197
+ if (!scopeKnown) {
198
+ warnings.push(
199
+ "Selected platform scope is unknown; pass `--platform <value>` so verify-install can evaluate selected targets explicitly."
200
+ );
201
+ }
202
+
203
+ const statusToken = failures.length > 0 ? "BLOCK" : warnings.length > 0 ? "WARN" : "PASS";
204
+ const nextSteps = [];
205
+ if (failures.length > 0) {
206
+ nextSteps.push("Fix selected-platform install failures, then rerun `da-vinci verify-install`.");
207
+ }
208
+ if (!scopeKnown) {
209
+ nextSteps.push(
210
+ "Rerun with explicit scope, for example `da-vinci verify-install --platform codex`, to avoid unknown-scope coverage."
211
+ );
212
+ }
213
+ if (statusToken === "PASS") {
214
+ nextSteps.push(
215
+ "Continue with repository readiness checks (for example `da-vinci maintainer-readiness`)."
216
+ );
217
+ }
218
+
219
+ return {
220
+ status: statusToken,
221
+ homeDir,
222
+ scope: {
223
+ known: scopeKnown,
224
+ selectedPlatforms,
225
+ supportedPlatforms: SUPPORTED_PLATFORMS.slice()
226
+ },
227
+ platformCoverage,
228
+ failures: failures,
229
+ warnings: warnings,
230
+ notes: notes,
231
+ nextSteps
232
+ };
233
+ }
234
+
111
235
  function ensureDir(targetPath) {
112
236
  fs.mkdirSync(targetPath, { recursive: true });
113
237
  }
@@ -407,25 +531,29 @@ function getStatus(options = {}) {
407
531
  };
408
532
  }
409
533
 
410
- function validateAssets() {
534
+ function validateAssets(options = {}) {
535
+ const repoRoot = options.repoRoot ? path.resolve(String(options.repoRoot)) : REPO_ROOT;
411
536
  const missing = REQUIRED_FILES.filter((relativePath) => {
412
- return !fs.existsSync(path.join(REPO_ROOT, relativePath));
537
+ return !fs.existsSync(path.join(repoRoot, relativePath));
413
538
  });
414
539
 
415
540
  if (missing.length > 0) {
416
- throw new Error(`Missing required assets:\n${missing.join("\n")}`);
541
+ throw new Error(`Missing required assets under ${repoRoot}:\n${missing.join("\n")}`);
417
542
  }
418
543
 
419
544
  return {
420
545
  version: VERSION,
421
- requiredAssets: REQUIRED_FILES.length
546
+ requiredAssets: REQUIRED_FILES.length,
547
+ repoRoot
422
548
  };
423
549
  }
424
550
 
425
551
  module.exports = {
426
552
  VERSION,
553
+ SUPPORTED_PLATFORMS,
427
554
  installPlatforms,
428
555
  uninstallPlatforms,
429
556
  getStatus,
557
+ verifyInstall,
430
558
  validateAssets
431
559
  };