@documentation-robotics/cli 0.3.0 → 0.4.0

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 (182) hide show
  1. package/README.md +10 -14
  2. package/dist/analyzers/active-analyzer.js +62 -0
  3. package/dist/analyzers/active-analyzer.js.map +7 -0
  4. package/dist/analyzers/cards/build.js +21 -1
  5. package/dist/analyzers/cards/build.js.map +2 -2
  6. package/dist/analyzers/cards/extract.js +138 -0
  7. package/dist/analyzers/cards/extract.js.map +7 -0
  8. package/dist/analyzers/cards/routes.js +96 -0
  9. package/dist/analyzers/cards/routes.js.map +7 -0
  10. package/dist/analyzers/cbm-analyzer.js +76 -4
  11. package/dist/analyzers/cbm-analyzer.js.map +2 -2
  12. package/dist/analyzers/cbm-responses.js +12 -6
  13. package/dist/analyzers/cbm-responses.js.map +2 -2
  14. package/dist/analyzers/code-changes.js +159 -0
  15. package/dist/analyzers/code-changes.js.map +7 -0
  16. package/dist/analyzers/edge-relationships.js +6 -2
  17. package/dist/analyzers/edge-relationships.js.map +2 -2
  18. package/dist/analyzers/extraction-plan.js +541 -0
  19. package/dist/analyzers/extraction-plan.js.map +7 -0
  20. package/dist/analyzers/index-job.js +229 -0
  21. package/dist/analyzers/index-job.js.map +7 -0
  22. package/dist/cli.js +12 -10
  23. package/dist/cli.js.map +2 -2
  24. package/dist/coding-agents/drbot-mcp.js +120 -18
  25. package/dist/coding-agents/drbot-mcp.js.map +2 -2
  26. package/dist/commands/add.js +4 -2
  27. package/dist/commands/add.js.map +2 -2
  28. package/dist/commands/analyzer.js +35 -35
  29. package/dist/commands/analyzer.js.map +2 -2
  30. package/dist/commands/changeset-verify.js +16 -8
  31. package/dist/commands/changeset-verify.js.map +2 -2
  32. package/dist/commands/changeset.js +41 -11
  33. package/dist/commands/changeset.js.map +2 -2
  34. package/dist/commands/init.js +9 -26
  35. package/dist/commands/init.js.map +2 -2
  36. package/dist/commands/mcp.js +35 -31
  37. package/dist/commands/mcp.js.map +3 -3
  38. package/dist/commands/relationship.js +2 -12
  39. package/dist/commands/relationship.js.map +2 -2
  40. package/dist/commands/reports.js +3 -3
  41. package/dist/commands/reports.js.map +2 -2
  42. package/dist/commands/update.js +14 -13
  43. package/dist/commands/update.js.map +2 -2
  44. package/dist/commands/upgrade.js +28 -120
  45. package/dist/commands/upgrade.js.map +2 -2
  46. package/dist/commands/validate.js +19 -3
  47. package/dist/commands/validate.js.map +2 -2
  48. package/dist/commands/version.js +1 -1
  49. package/dist/commands/visualize.js +30 -12
  50. package/dist/commands/visualize.js.map +2 -2
  51. package/dist/core/annotation-store.js +15 -5
  52. package/dist/core/annotation-store.js.map +2 -2
  53. package/dist/core/catalog-migration.js +2 -1
  54. package/dist/core/catalog-migration.js.map +2 -2
  55. package/dist/core/changeset-base-index.js +7 -0
  56. package/dist/core/changeset-base-index.js.map +2 -2
  57. package/dist/core/changeset-brief-compact.js +256 -0
  58. package/dist/core/changeset-brief-compact.js.map +7 -0
  59. package/dist/core/changeset-verify.js +356 -26
  60. package/dist/core/changeset-verify.js.map +3 -3
  61. package/dist/core/extraction-observability.js +161 -0
  62. package/dist/core/extraction-observability.js.map +7 -0
  63. package/dist/core/migration-registry.js +13 -1
  64. package/dist/core/migration-registry.js.map +2 -2
  65. package/dist/core/project-init.js +53 -0
  66. package/dist/core/project-init.js.map +7 -0
  67. package/dist/core/relationship-staging.js +94 -1
  68. package/dist/core/relationship-staging.js.map +2 -2
  69. package/dist/core/staged-changeset-storage.js +83 -3
  70. package/dist/core/staged-changeset-storage.js.map +2 -2
  71. package/dist/core/staging-area.js +85 -8
  72. package/dist/core/staging-area.js.map +2 -2
  73. package/dist/core/upgrade-plan.js +117 -0
  74. package/dist/core/upgrade-plan.js.map +7 -0
  75. package/dist/generated/relationship-index.js +124 -15
  76. package/dist/generated/relationship-index.js.map +2 -2
  77. package/dist/integrations/claude_code/.claude-plugin/plugin.json +24 -0
  78. package/dist/integrations/claude_code/agents/dr-architect.md +19 -5
  79. package/dist/integrations/claude_code/agents/dr-extractor.md +7 -1
  80. package/dist/integrations/claude_code/commands/dr-changeset.md +2 -1
  81. package/dist/integrations/claude_code/commands/dr-map.md +7 -1
  82. package/dist/integrations/claude_code/commands/dr-plan.md +42 -16
  83. package/dist/integrations/claude_code/docs/MCP_SERVER.md +28 -31
  84. package/dist/integrations/claude_code/docs/README.md +20 -1
  85. package/dist/integrations/claude_code/plugin-agents/dr-advisor.md +255 -0
  86. package/dist/integrations/claude_code/plugin-agents/dr-architect.md +306 -0
  87. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +3 -0
  88. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +90 -31
  89. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +5 -0
  90. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +5 -0
  91. package/dist/integrations/github_copilot/agents/dr-architect.md +2 -0
  92. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +3 -0
  93. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +88 -30
  94. package/dist/mcp/changeset-only.js +14 -3
  95. package/dist/mcp/changeset-only.js.map +2 -2
  96. package/dist/mcp/project-root.js +85 -0
  97. package/dist/mcp/project-root.js.map +7 -0
  98. package/dist/mcp/server-instructions.js +27 -0
  99. package/dist/mcp/server-instructions.js.map +7 -0
  100. package/dist/mcp/strict-schema.js +52 -0
  101. package/dist/mcp/strict-schema.js.map +7 -0
  102. package/dist/mcp/tool-registry.js +39 -5
  103. package/dist/mcp/tool-registry.js.map +2 -2
  104. package/dist/mcp/tools/changeset-analysis-tools.js +25 -5
  105. package/dist/mcp/tools/changeset-analysis-tools.js.map +3 -3
  106. package/dist/mcp/tools/changeset-commit.js +224 -0
  107. package/dist/mcp/tools/changeset-commit.js.map +7 -0
  108. package/dist/mcp/tools/changeset-editing.js +133 -57
  109. package/dist/mcp/tools/changeset-editing.js.map +3 -3
  110. package/dist/mcp/tools/changeset-lifecycle.js +1 -1
  111. package/dist/mcp/tools/changeset-lifecycle.js.map +2 -2
  112. package/dist/mcp/tools/changeset-verify.js +12 -6
  113. package/dist/mcp/tools/changeset-verify.js.map +2 -2
  114. package/dist/mcp/tools/codebase-extract.js +836 -0
  115. package/dist/mcp/tools/codebase-extract.js.map +7 -0
  116. package/dist/mcp/tools/codebase-index.js +125 -0
  117. package/dist/mcp/tools/codebase-index.js.map +7 -0
  118. package/dist/mcp/tools/codebase-shared.js +265 -0
  119. package/dist/mcp/tools/codebase-shared.js.map +7 -0
  120. package/dist/mcp/tools/codebase-verify.js +48 -0
  121. package/dist/mcp/tools/codebase-verify.js.map +7 -0
  122. package/dist/mcp/tools/index.js +15 -0
  123. package/dist/mcp/tools/index.js.map +2 -2
  124. package/dist/mcp/tools/model-add.js +189 -73
  125. package/dist/mcp/tools/model-add.js.map +2 -2
  126. package/dist/mcp/tools/model-update.js +16 -12
  127. package/dist/mcp/tools/model-update.js.map +2 -2
  128. package/dist/mcp/tools/model-validate.js +46 -4
  129. package/dist/mcp/tools/model-validate.js.map +2 -2
  130. package/dist/mcp/tools/project-status-collectors.js +381 -0
  131. package/dist/mcp/tools/project-status-collectors.js.map +7 -0
  132. package/dist/mcp/tools/project-status.js +237 -0
  133. package/dist/mcp/tools/project-status.js.map +7 -0
  134. package/dist/mcp/tools/project-tools.js +505 -0
  135. package/dist/mcp/tools/project-tools.js.map +7 -0
  136. package/dist/mcp/tools/relationship-tools.js +89 -20
  137. package/dist/mcp/tools/relationship-tools.js.map +2 -2
  138. package/dist/mcp/tools/review-open.js +210 -0
  139. package/dist/mcp/tools/review-open.js.map +7 -0
  140. package/dist/mcp/tools/shared.js +10 -2
  141. package/dist/mcp/tools/shared.js.map +2 -2
  142. package/dist/reports/model-report-orchestrator.js +31 -2
  143. package/dist/reports/model-report-orchestrator.js.map +2 -2
  144. package/dist/schemas/bundled/analyzers/cbm.json +22 -10
  145. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  146. package/dist/schemas/bundled/api.json +1 -1
  147. package/dist/schemas/bundled/apm.json +1 -1
  148. package/dist/schemas/bundled/application.json +1 -1
  149. package/dist/schemas/bundled/base.json +1 -1
  150. package/dist/schemas/bundled/business.json +1 -1
  151. package/dist/schemas/bundled/data-model.json +1 -1
  152. package/dist/schemas/bundled/data-store.json +1 -1
  153. package/dist/schemas/bundled/manifest.json +3 -3
  154. package/dist/schemas/bundled/motivation.json +1 -1
  155. package/dist/schemas/bundled/navigation.json +1 -1
  156. package/dist/schemas/bundled/product.json +1 -1
  157. package/dist/schemas/bundled/security.json +1 -1
  158. package/dist/schemas/bundled/technology.json +1 -1
  159. package/dist/schemas/bundled/testing.json +145 -1
  160. package/dist/schemas/bundled/ux.json +1 -1
  161. package/dist/server/server.js +508 -237
  162. package/dist/server/server.js.map +3 -3
  163. package/dist/server/viewer-registry.js +264 -0
  164. package/dist/server/viewer-registry.js.map +7 -0
  165. package/dist/telemetry/index.js +1 -1
  166. package/dist/utils/id-generator.js +5 -2
  167. package/dist/utils/id-generator.js.map +2 -2
  168. package/dist/utils/source-reference.js +58 -27
  169. package/dist/utils/source-reference.js.map +2 -2
  170. package/dist/utils/spec-version.js +2 -2
  171. package/dist/utils/spec-version.js.map +1 -1
  172. package/dist/validators/model-warnings.js +28 -0
  173. package/dist/validators/model-warnings.js.map +7 -0
  174. package/dist/viewer/README.md +6 -6
  175. package/dist/viewer/assets/main-CUOQGGRA.css +1 -0
  176. package/dist/viewer/assets/main-DvH3gYrI.js +84 -0
  177. package/dist/viewer/index.html +2 -2
  178. package/dist/viewer/manifest.json +10 -10
  179. package/dist/viewer/version.json +1 -1
  180. package/package.json +1 -1
  181. package/dist/viewer/assets/main-CxzcuU_o.js +0 -84
  182. package/dist/viewer/assets/main-j-7ULYpm.css +0 -1
@@ -9,7 +9,7 @@ async function changesetVerifyCommand(planned, options = {}) {
9
9
  json ? console.log(JSON.stringify({ status: "error", code: 1, message })) : (console.error(ansis.red(`Error: ${message}`)), hint && console.error(ansis.dim(hint))), process.exit(1);
10
10
  };
11
11
  try {
12
- options.against && options.base && fail("Pass either --against <changeset> or --base, not both"), !options.against && !options.base && fail("Nothing to verify against", "Use --against <changeset> to compare with an as-built changeset, or --base to compare with the base model");
12
+ options.against && options.base && fail("Pass either --against <changeset> or --base, not both"), options.extracted && !options.base && fail("--extracted goes with --base"), !options.against && !options.base && fail("Nothing to verify against", "Use --against <changeset> to compare with an as-built changeset, or --base to compare with the base model");
13
13
  const model = await Model.load(options.model, { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), load = async (name, role) => {
14
14
  const changeset = await manager.load(name);
15
15
  return changeset || fail(`${role} changeset '${name}' not found`, "Run 'dr changeset list' to see changesets");
@@ -18,7 +18,7 @@ async function changesetVerifyCommand(planned, options = {}) {
18
18
  return fail("No changeset named and none is active", "Pass the planned changeset's name or ID");
19
19
  const plannedChangeset = await load(plannedName, "Planned"), asBuilt = options.against ? await load(options.against, "As-built") : void 0, report = await verifyChangesetInModel(model, {
20
20
  planned: plannedChangeset,
21
- against: asBuilt ? { changeset: asBuilt } : { base: !0 },
21
+ against: asBuilt ? { changeset: asBuilt } : { base: !0, extracted: options.extracted === !0 },
22
22
  matchBySource: options.matchBySource
23
23
  });
24
24
  console.log(json ? JSON.stringify(report, null, 2) : formatVerifyReport(report));
@@ -26,12 +26,18 @@ async function changesetVerifyCommand(planned, options = {}) {
26
26
  fail(getErrorMessage(error));
27
27
  }
28
28
  }
29
- const rel = (r) => `${r.source} --${r.predicate}--> ${r.target}`, value = (v) => v === void 0 ? "(none)" : JSON.stringify(v);
29
+ const rel = (r) => `${r.source} --${r.predicate}--> ${r.target}`, value = (v) => v == null ? "(none)" : JSON.stringify(v);
30
30
  function formatVerifyReport(report) {
31
- const lines = [], verdictColor = report.verdict === "match" ? ansis.green : report.verdict === "probable-match" ? ansis.cyan : report.verdict === "diverges" ? ansis.yellow : report.verdict === "incomplete" ? ansis.red : ansis.dim;
31
+ const lines = [], verdictColor = report.verdict === "match" ? ansis.green : report.verdict === "probable-match" ? ansis.cyan : report.verdict === "diverges" ? ansis.yellow : report.verdict === "incomplete" ? ansis.red : ansis.dim, unverifiable = (n) => n > 0 ? `, ${n} unverifiable` : "";
32
32
  lines.push(ansis.bold(`Verify: ${report.planned.label} vs ${report.against.label}`)), lines.push(verdictColor(`${report.verdict}: ${report.verdictText}`));
33
33
  const e = report.summary.elements, r = report.summary.relationships;
34
- lines.push(ansis.dim(`Elements: ${e.planned} planned, ${e.matched} matched (${e.divergent} with differences), ${e.missing} missing, ${e.unplanned} unplanned, ${e.deletionsNotDone} deletions not done (${e.probable} probable)`)), lines.push(ansis.dim(`Relationships: ${r.planned} planned, ${r.matched} matched (${r.divergent} with differences), ${r.missing} missing, ${r.unplanned} unplanned, ${r.deletionsNotDone} deletions not done (${r.probable} probable)`));
34
+ lines.push(ansis.dim(`Elements: ${e.planned} planned, ${e.matched} matched (${e.divergent} with differences), ${e.missing} missing${unverifiable(e.unverifiable)}, ${e.unplanned} unplanned, ${e.deletionsNotDone} deletions not done (${e.probable} probable)`)), lines.push(ansis.dim(`Relationships: ${r.planned} planned, ${r.matched} matched (${r.divergent} with differences), ${r.missing} missing${unverifiable(r.unverifiable)}, ${r.unplanned} unplanned, ${r.deletionsNotDone} deletions not done (${r.probable} probable)`));
35
+ const share = report.extraction?.asBuilt;
36
+ if (share) {
37
+ const provenance = Object.entries(share.elements.byProvenance).map(([k, n]) => `${n} ${k}`).join(", ");
38
+ lines.push(ansis.dim(`As-built: ${share.elements.total} elements (${provenance || "none"} declared; ${share.elements.fromExtraction} from extraction, ${share.elements.agentAdded} added by the agent), ${share.relationships.total} relationships (${share.relationships.betweenExtracted} between extracted elements)`));
39
+ }
40
+ report.warning && lines.push(ansis.yellow(`Warning: ${report.warning}`));
35
41
  const section = (title, items) => {
36
42
  if (items.length !== 0) {
37
43
  lines.push("", ansis.bold(title));
@@ -41,10 +47,12 @@ function formatVerifyReport(report) {
41
47
  }, probable = (m) => m.match === "probable" ? ansis.cyan(` [probable: ${m.reason}]`) : "";
42
48
  return section("Missing elements (planned, not built)", report.elements.missing.map((m) => `${ansis.red("-")} ${m.planned} (${m.op}): ${m.reason}` + (m.candidates ? `
43
49
  candidates: ${m.candidates.join(", ")}` : "") + (m.fields ? m.fields.map((f) => `
44
- ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join("") : ""))), section("Divergent elements", report.elements.divergent.map((d) => `${ansis.yellow("~")} ${d.planned}${d.built !== d.planned ? ` \u2192 ${d.built}` : ""}${probable(d)}` + d.fields.map((f) => `
50
+ ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join("") : ""))), section("Unverifiable elements (extraction can't see them)", report.elements.unverifiable.map((u) => `${ansis.dim("?")} ${u.planned} (${u.op}): ${u.reason}`)), section("Divergent elements", report.elements.divergent.map((d) => `${ansis.yellow("~")} ${d.planned}${d.built !== d.planned ? ` \u2192 ${d.built}` : ""}${probable(d)}` + d.fields.map((f) => `
45
51
  ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join(""))), section("Planned deletions that still exist", report.elements.deletionsNotDone.map((d) => `${ansis.red("!")} ${d.built}`)), section("Unplanned elements (built, not planned)", report.elements.unplanned.map((u) => `${ansis.yellow("+")} ${u.id} (${u.change})`)), section("Matched elements", report.elements.matched.map((m) => `${ansis.green("\u2713")} ${m.planned}${m.built !== m.planned ? ` \u2192 ${m.built}` : ""} (${m.op})${probable(m)}` + (m.lifecycle ?? []).map((f) => ansis.dim(`
46
- ${f.field}: ${value(f.planned)} \u2192 ${value(f.built)} (lifecycle)`)).join(""))), section("Missing relationships", report.relationships.missing.map((m) => `${ansis.red("-")} ${rel(m.planned)}: ${m.reason}` + (m.candidates ? `
47
- candidates: ${m.candidates.map(rel).join(", ")}` : ""))), section("Divergent relationships", report.relationships.divergent.map((d) => `${ansis.yellow("~")} ${rel(d.planned)} built as ${rel(d.built)}${probable(d)}` + d.fields.map((f) => `
52
+ ${f.field}: ${value(f.planned)} \u2192 ${value(f.built)} (lifecycle)`)).join("") + (m.notes ?? []).map((f) => ansis.dim(`
53
+ ${f.field} differs (note): built ${value(f.built)}`)).join("") + (m.unverifiableFields ?? []).map((f) => ansis.dim(`
54
+ ${f.field}: planned ${value(f.planned)} (extraction doesn't write it)`)).join(""))), section("Missing relationships", report.relationships.missing.map((m) => `${ansis.red("-")} ${rel(m.planned)}: ${m.reason}` + (m.candidates ? `
55
+ candidates: ${m.candidates.map(rel).join(", ")}` : ""))), section("Unverifiable relationships (extraction can't see them)", report.relationships.unverifiable.map((u) => `${ansis.dim("?")} ${rel(u.planned)}: ${u.reason}`)), section("Divergent relationships", report.relationships.divergent.map((d) => `${ansis.yellow("~")} ${rel(d.planned)} built as ${rel(d.built)}${probable(d)}` + d.fields.map((f) => `
48
56
  ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join(""))), section("Planned relationship deletions that still exist", report.relationships.deletionsNotDone.map((d) => `${ansis.red("!")} ${rel(d.built)}`)), section("Unplanned relationships", report.relationships.unplanned.map((u) => `${ansis.yellow("+")} ${rel(u)} (${u.change})`)), section("Matched relationships", report.relationships.matched.map((m) => `${ansis.green("\u2713")} ${rel(m.built)} (${m.op})${probable(m)}`)), section("Notes", report.notes.map((n) => ansis.dim(n))), lines.join(`
49
57
  `);
50
58
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/commands/changeset-verify.ts"],
4
- "sourcesContent": ["/**\n * dr changeset verify \u2014 compare a planned changeset with what was built (another changeset, or the\n * base model). The same comparison as the `changeset_verify` MCP tool; see\n * `cli/src/core/changeset-verify.ts`.\n */\n\nimport ansis from \"ansis\";\nimport { Model } from \"../core/model.js\";\nimport { StagingAreaManager } from \"../core/staging-area.js\";\nimport { verifyChangesetInModel, type VerifyReport } from \"../core/changeset-verify.js\";\nimport type { Changeset } from \"../core/changeset.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { isJson } from \"../utils/globals.js\";\n\nexport interface ChangesetVerifyOptions {\n model?: string;\n against?: string;\n base?: boolean;\n json?: boolean;\n matchBySource?: boolean;\n}\n\nexport async function changesetVerifyCommand(\n planned: string | undefined,\n options: ChangesetVerifyOptions = {}\n): Promise<void> {\n const json = options.json === true || isJson();\n const fail = (message: string, hint?: string): never => {\n if (json) {\n console.log(JSON.stringify({ status: \"error\", code: 1, message }));\n } else {\n console.error(ansis.red(`Error: ${message}`));\n if (hint) console.error(ansis.dim(hint));\n }\n process.exit(1);\n };\n\n try {\n if (options.against && options.base) {\n fail(\"Pass either --against <changeset> or --base, not both\");\n }\n if (!options.against && !options.base) {\n fail(\n \"Nothing to verify against\",\n \"Use --against <changeset> to compare with an as-built changeset, or --base to compare with the base model\"\n );\n }\n\n const model = await Model.load(options.model, { lazyLoad: false });\n const manager = new StagingAreaManager(model.rootPath, model);\n const load = async (name: string, role: string): Promise<Changeset> => {\n const changeset = await manager.load(name);\n if (!changeset) return fail(`${role} changeset '${name}' not found`, \"Run 'dr changeset list' to see changesets\");\n return changeset;\n };\n\n const plannedName = planned ?? (await manager.getActiveId());\n if (!plannedName) {\n return fail(\"No changeset named and none is active\", \"Pass the planned changeset's name or ID\");\n }\n const plannedChangeset = await load(plannedName, \"Planned\");\n const asBuilt = options.against ? await load(options.against, \"As-built\") : undefined;\n\n const report = await verifyChangesetInModel(model, {\n planned: plannedChangeset,\n against: asBuilt ? { changeset: asBuilt } : { base: true },\n matchBySource: options.matchBySource,\n });\n\n if (json) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n console.log(formatVerifyReport(report));\n }\n } catch (error) {\n fail(getErrorMessage(error));\n }\n}\n\nconst rel = (r: { source: string; predicate: string; target: string }) => `${r.source} --${r.predicate}--> ${r.target}`;\nconst value = (v: unknown) => (v === undefined ? \"(none)\" : JSON.stringify(v));\n\n/** Human-readable rendering of a verify report. */\nexport function formatVerifyReport(report: VerifyReport): string {\n const lines: string[] = [];\n const verdictColor =\n report.verdict === \"match\"\n ? ansis.green\n : report.verdict === \"probable-match\"\n ? ansis.cyan\n : report.verdict === \"diverges\"\n ? ansis.yellow\n : report.verdict === \"incomplete\"\n ? ansis.red\n : ansis.dim;\n\n lines.push(ansis.bold(`Verify: ${report.planned.label} vs ${report.against.label}`));\n lines.push(verdictColor(`${report.verdict}: ${report.verdictText}`));\n const e = report.summary.elements;\n const r = report.summary.relationships;\n lines.push(\n ansis.dim(\n `Elements: ${e.planned} planned, ${e.matched} matched (${e.divergent} with differences), ${e.missing} missing, ` +\n `${e.unplanned} unplanned, ${e.deletionsNotDone} deletions not done (${e.probable} probable)`\n )\n );\n lines.push(\n ansis.dim(\n `Relationships: ${r.planned} planned, ${r.matched} matched (${r.divergent} with differences), ${r.missing} missing, ` +\n `${r.unplanned} unplanned, ${r.deletionsNotDone} deletions not done (${r.probable} probable)`\n )\n );\n\n const section = (title: string, items: string[]) => {\n if (items.length === 0) return;\n lines.push(\"\", ansis.bold(title));\n for (const item of items) lines.push(` ${item}`);\n };\n const probable = (m: { match: string; reason?: string }) => (m.match === \"probable\" ? ansis.cyan(` [probable: ${m.reason}]`) : \"\");\n\n section(\n \"Missing elements (planned, not built)\",\n report.elements.missing.map(\n (m) =>\n `${ansis.red(\"-\")} ${m.planned} (${m.op}): ${m.reason}` +\n (m.candidates ? `\\n candidates: ${m.candidates.join(\", \")}` : \"\") +\n (m.fields ? m.fields.map((f) => `\\n ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join(\"\") : \"\")\n )\n );\n section(\n \"Divergent elements\",\n report.elements.divergent.map(\n (d) =>\n `${ansis.yellow(\"~\")} ${d.planned}${d.built !== d.planned ? ` \u2192 ${d.built}` : \"\"}${probable(d)}` +\n d.fields.map((f) => `\\n ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join(\"\")\n )\n );\n section(\n \"Planned deletions that still exist\",\n report.elements.deletionsNotDone.map((d) => `${ansis.red(\"!\")} ${d.built}`)\n );\n section(\n \"Unplanned elements (built, not planned)\",\n report.elements.unplanned.map((u) => `${ansis.yellow(\"+\")} ${u.id} (${u.change})`)\n );\n section(\n \"Matched elements\",\n report.elements.matched.map(\n (m) =>\n `${ansis.green(\"\u2713\")} ${m.planned}${m.built !== m.planned ? ` \u2192 ${m.built}` : \"\"} (${m.op})${probable(m)}` +\n (m.lifecycle ?? []).map((f) => ansis.dim(`\\n ${f.field}: ${value(f.planned)} \u2192 ${value(f.built)} (lifecycle)`)).join(\"\")\n )\n );\n\n section(\n \"Missing relationships\",\n report.relationships.missing.map(\n (m) =>\n `${ansis.red(\"-\")} ${rel(m.planned)}: ${m.reason}` +\n (m.candidates ? `\\n candidates: ${m.candidates.map(rel).join(\", \")}` : \"\")\n )\n );\n section(\n \"Divergent relationships\",\n report.relationships.divergent.map(\n (d) =>\n `${ansis.yellow(\"~\")} ${rel(d.planned)} built as ${rel(d.built)}${probable(d)}` +\n d.fields.map((f) => `\\n ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join(\"\")\n )\n );\n section(\n \"Planned relationship deletions that still exist\",\n report.relationships.deletionsNotDone.map((d) => `${ansis.red(\"!\")} ${rel(d.built)}`)\n );\n section(\n \"Unplanned relationships\",\n report.relationships.unplanned.map((u) => `${ansis.yellow(\"+\")} ${rel(u)} (${u.change})`)\n );\n section(\n \"Matched relationships\",\n report.relationships.matched.map((m) => `${ansis.green(\"\u2713\")} ${rel(m.built)} (${m.op})${probable(m)}`)\n );\n\n section(\n \"Notes\",\n report.notes.map((n) => ansis.dim(n))\n );\n return lines.join(\"\\n\");\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * dr changeset verify \u2014 compare a planned changeset with what was built (another changeset, or the\n * base model). The same comparison as the `changeset_verify` MCP tool; see\n * `cli/src/core/changeset-verify.ts`.\n */\n\nimport ansis from \"ansis\";\nimport { Model } from \"../core/model.js\";\nimport { StagingAreaManager } from \"../core/staging-area.js\";\nimport { verifyChangesetInModel, type VerifyReport } from \"../core/changeset-verify.js\";\nimport type { Changeset } from \"../core/changeset.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { isJson } from \"../utils/globals.js\";\n\nexport interface ChangesetVerifyOptions {\n model?: string;\n against?: string;\n base?: boolean;\n /** With base: apply the extraction rule (the as-built side landed from codebase_extract). */\n extracted?: boolean;\n json?: boolean;\n matchBySource?: boolean;\n}\n\nexport async function changesetVerifyCommand(\n planned: string | undefined,\n options: ChangesetVerifyOptions = {}\n): Promise<void> {\n const json = options.json === true || isJson();\n const fail = (message: string, hint?: string): never => {\n if (json) {\n console.log(JSON.stringify({ status: \"error\", code: 1, message }));\n } else {\n console.error(ansis.red(`Error: ${message}`));\n if (hint) console.error(ansis.dim(hint));\n }\n process.exit(1);\n };\n\n try {\n if (options.against && options.base) {\n fail(\"Pass either --against <changeset> or --base, not both\");\n }\n if (options.extracted && !options.base) {\n fail(\"--extracted goes with --base\");\n }\n if (!options.against && !options.base) {\n fail(\n \"Nothing to verify against\",\n \"Use --against <changeset> to compare with an as-built changeset, or --base to compare with the base model\"\n );\n }\n\n const model = await Model.load(options.model, { lazyLoad: false });\n const manager = new StagingAreaManager(model.rootPath, model);\n const load = async (name: string, role: string): Promise<Changeset> => {\n const changeset = await manager.load(name);\n if (!changeset) return fail(`${role} changeset '${name}' not found`, \"Run 'dr changeset list' to see changesets\");\n return changeset;\n };\n\n const plannedName = planned ?? (await manager.getActiveId());\n if (!plannedName) {\n return fail(\"No changeset named and none is active\", \"Pass the planned changeset's name or ID\");\n }\n const plannedChangeset = await load(plannedName, \"Planned\");\n const asBuilt = options.against ? await load(options.against, \"As-built\") : undefined;\n\n const report = await verifyChangesetInModel(model, {\n planned: plannedChangeset,\n against: asBuilt ? { changeset: asBuilt } : { base: true, extracted: options.extracted === true },\n matchBySource: options.matchBySource,\n });\n\n if (json) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n console.log(formatVerifyReport(report));\n }\n } catch (error) {\n fail(getErrorMessage(error));\n }\n}\n\nconst rel = (r: { source: string; predicate: string; target: string }) => `${r.source} --${r.predicate}--> ${r.target}`;\nconst value = (v: unknown) => (v === undefined || v === null ? \"(none)\" : JSON.stringify(v));\n\n/** Human-readable rendering of a verify report. */\nexport function formatVerifyReport(report: VerifyReport): string {\n const lines: string[] = [];\n const verdictColor =\n report.verdict === \"match\"\n ? ansis.green\n : report.verdict === \"probable-match\"\n ? ansis.cyan\n : report.verdict === \"diverges\"\n ? ansis.yellow\n : report.verdict === \"incomplete\"\n ? ansis.red\n : ansis.dim;\n const unverifiable = (n: number) => (n > 0 ? `, ${n} unverifiable` : \"\");\n\n lines.push(ansis.bold(`Verify: ${report.planned.label} vs ${report.against.label}`));\n lines.push(verdictColor(`${report.verdict}: ${report.verdictText}`));\n const e = report.summary.elements;\n const r = report.summary.relationships;\n lines.push(\n ansis.dim(\n `Elements: ${e.planned} planned, ${e.matched} matched (${e.divergent} with differences), ${e.missing} missing` +\n `${unverifiable(e.unverifiable)}, ${e.unplanned} unplanned, ${e.deletionsNotDone} deletions not done (${e.probable} probable)`\n )\n );\n lines.push(\n ansis.dim(\n `Relationships: ${r.planned} planned, ${r.matched} matched (${r.divergent} with differences), ${r.missing} missing` +\n `${unverifiable(r.unverifiable)}, ${r.unplanned} unplanned, ${r.deletionsNotDone} deletions not done (${r.probable} probable)`\n )\n );\n const share = report.extraction?.asBuilt;\n if (share) {\n const provenance = Object.entries(share.elements.byProvenance)\n .map(([k, n]) => `${n} ${k}`)\n .join(\", \");\n lines.push(\n ansis.dim(\n `As-built: ${share.elements.total} elements (${provenance || \"none\"} declared; ${share.elements.fromExtraction} ` +\n `from extraction, ${share.elements.agentAdded} added by the agent), ${share.relationships.total} relationships ` +\n `(${share.relationships.betweenExtracted} between extracted elements)`\n )\n );\n }\n if (report.warning) lines.push(ansis.yellow(`Warning: ${report.warning}`));\n\n const section = (title: string, items: string[]) => {\n if (items.length === 0) return;\n lines.push(\"\", ansis.bold(title));\n for (const item of items) lines.push(` ${item}`);\n };\n const probable = (m: { match: string; reason?: string }) => (m.match === \"probable\" ? ansis.cyan(` [probable: ${m.reason}]`) : \"\");\n\n section(\n \"Missing elements (planned, not built)\",\n report.elements.missing.map(\n (m) =>\n `${ansis.red(\"-\")} ${m.planned} (${m.op}): ${m.reason}` +\n (m.candidates ? `\\n candidates: ${m.candidates.join(\", \")}` : \"\") +\n (m.fields ? m.fields.map((f) => `\\n ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join(\"\") : \"\")\n )\n );\n section(\n \"Unverifiable elements (extraction can't see them)\",\n report.elements.unverifiable.map((u) => `${ansis.dim(\"?\")} ${u.planned} (${u.op}): ${u.reason}`)\n );\n section(\n \"Divergent elements\",\n report.elements.divergent.map(\n (d) =>\n `${ansis.yellow(\"~\")} ${d.planned}${d.built !== d.planned ? ` \u2192 ${d.built}` : \"\"}${probable(d)}` +\n d.fields.map((f) => `\\n ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join(\"\")\n )\n );\n section(\n \"Planned deletions that still exist\",\n report.elements.deletionsNotDone.map((d) => `${ansis.red(\"!\")} ${d.built}`)\n );\n section(\n \"Unplanned elements (built, not planned)\",\n report.elements.unplanned.map((u) => `${ansis.yellow(\"+\")} ${u.id} (${u.change})`)\n );\n section(\n \"Matched elements\",\n report.elements.matched.map(\n (m) =>\n `${ansis.green(\"\u2713\")} ${m.planned}${m.built !== m.planned ? ` \u2192 ${m.built}` : \"\"} (${m.op})${probable(m)}` +\n (m.lifecycle ?? []).map((f) => ansis.dim(`\\n ${f.field}: ${value(f.planned)} \u2192 ${value(f.built)} (lifecycle)`)).join(\"\") +\n (m.notes ?? []).map((f) => ansis.dim(`\\n ${f.field} differs (note): built ${value(f.built)}`)).join(\"\") +\n (m.unverifiableFields ?? [])\n .map((f) => ansis.dim(`\\n ${f.field}: planned ${value(f.planned)} (extraction doesn't write it)`))\n .join(\"\")\n )\n );\n\n section(\n \"Missing relationships\",\n report.relationships.missing.map(\n (m) =>\n `${ansis.red(\"-\")} ${rel(m.planned)}: ${m.reason}` +\n (m.candidates ? `\\n candidates: ${m.candidates.map(rel).join(\", \")}` : \"\")\n )\n );\n section(\n \"Unverifiable relationships (extraction can't see them)\",\n report.relationships.unverifiable.map((u) => `${ansis.dim(\"?\")} ${rel(u.planned)}: ${u.reason}`)\n );\n section(\n \"Divergent relationships\",\n report.relationships.divergent.map(\n (d) =>\n `${ansis.yellow(\"~\")} ${rel(d.planned)} built as ${rel(d.built)}${probable(d)}` +\n d.fields.map((f) => `\\n ${f.field}: planned ${value(f.planned)}, built ${value(f.built)}`).join(\"\")\n )\n );\n section(\n \"Planned relationship deletions that still exist\",\n report.relationships.deletionsNotDone.map((d) => `${ansis.red(\"!\")} ${rel(d.built)}`)\n );\n section(\n \"Unplanned relationships\",\n report.relationships.unplanned.map((u) => `${ansis.yellow(\"+\")} ${rel(u)} (${u.change})`)\n );\n section(\n \"Matched relationships\",\n report.relationships.matched.map((m) => `${ansis.green(\"\u2713\")} ${rel(m.built)} (${m.op})${probable(m)}`)\n );\n\n section(\n \"Notes\",\n report.notes.map((n) => ansis.dim(n))\n );\n return lines.join(\"\\n\");\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -8,7 +8,7 @@ import { Option } from "commander";
8
8
  import * as prompts from "@clack/prompts";
9
9
  import path from "path";
10
10
  import { isTelemetryEnabled, startSpan, endSpan, startActiveSpan } from "../telemetry/index.js";
11
- import { getErrorMessage, handleSuccess, handleInfo, handleWarning } from "../utils/errors.js";
11
+ import { CLIError, ErrorCategory, getErrorMessage, handleSuccess, handleInfo, handleWarning } from "../utils/errors.js";
12
12
  import { isJson } from "../utils/globals.js";
13
13
  import { findElementLayer } from "../utils/element-utils.js";
14
14
  import { changesetVerifyCommand } from "./changeset-verify.js";
@@ -543,9 +543,29 @@ Diff: Base Model vs Staged Changes
543
543
  endSpan(span);
544
544
  }
545
545
  }
546
- async function changesetCommitCommand(options) {
546
+ async function changesetCommitCommand(nameOrId, options) {
547
+ let restoreActive = null;
548
+ const restore = async () => {
549
+ if (!restoreActive)
550
+ return;
551
+ const { rootPath, previous } = restoreActive;
552
+ restoreActive = null;
553
+ const probe = await Model.load(rootPath, { lazyLoad: !0 }), manager = new StagingAreaManager(probe.rootPath, probe), prior = previous ? await manager.load(previous) : null;
554
+ prior?.status === "staged" ? (await manager.setActive(previous), isJson() || console.log(ansis.dim(`Changeset '${prior.name}' is active again.`))) : await manager.clearActive();
555
+ };
547
556
  await startActiveSpan("changeset.commit", async (span) => {
548
557
  try {
558
+ if (nameOrId) {
559
+ const probe = await Model.load(options?.model, { lazyLoad: !0 }), probeManager = new StagingAreaManager(probe.rootPath, probe), target = await probeManager.load(nameOrId);
560
+ if (!target || !target.id)
561
+ throw new CLIError(`Changeset '${nameOrId}' not found`, ErrorCategory.USER, [
562
+ 'Run "dr changeset list" to see changesets by name and id'
563
+ ]);
564
+ if (target.status !== "staged")
565
+ throw new CLIError(`Changeset '${target.name}' is ${target.status}; only a staged changeset can be committed`, ErrorCategory.USER, ['Run "dr changeset list" to find a staged changeset']);
566
+ const activeId = await probeManager.getActiveId();
567
+ activeId !== target.id && (restoreActive = { rootPath: probe.rootPath, previous: activeId }, await probeManager.setActive(target.id));
568
+ }
549
569
  const model = await Model.load(options?.model, { lazyLoad: !1 }), stagingManager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await stagingManager.getActiveId();
550
570
  if (!activeChangesetId) {
551
571
  isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: "No active changeset" })) : console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span.setStatus({ code: 2, message: "No active changeset" });
@@ -559,7 +579,7 @@ async function changesetCommitCommand(options) {
559
579
  name: changeset.name,
560
580
  changesetId: activeChangesetId,
561
581
  stagedChanges: 0
562
- }, { verbose: !0 }), isTelemetryEnabled && span.setStatus({ code: 0 });
582
+ }, { verbose: !0 }), await restore(), isTelemetryEnabled && span.setStatus({ code: 0 });
563
583
  return;
564
584
  }
565
585
  isJson() || (console.log(ansis.bold(`
@@ -583,15 +603,20 @@ Committing changeset: ${ansis.cyan(changeset.name)}`)), console.log(ansis.dim(`S
583
603
  const unaccounted = changeCount - accountedFor;
584
604
  commitDetails.unaccountedChanges = unaccounted, isJson() || console.log(ansis.yellow(`\u26A0 Warning: ${unaccounted} staged change(s) were not applied, skipped, or reported as failed. This is unexpected \u2014 check the changeset log for details.`));
585
605
  }
586
- result.driftWarning && (commitDetails.driftDetected = !0, isJson() || console.log(ansis.yellow("\u26A0 Warning: Model had drifted since changeset creation (--force was used)")), isTelemetryEnabled && span.setAttribute("commit.driftDetected", !0)), isTelemetryEnabled && span.setStatus({ code: 0 }), handleSuccess(`Committed ${result.committed} change(s)`, commitDetails, { verbose: !0 }), isJson() || console.log();
606
+ result.driftWarning && (commitDetails.driftDetected = !0, isJson() || console.log(ansis.yellow("\u26A0 Warning: Model had drifted since changeset creation (--force was used)")), isTelemetryEnabled && span.setAttribute("commit.driftDetected", !0)), isTelemetryEnabled && span.setStatus({ code: 0 }), handleSuccess(`Committed ${result.committed} change(s)`, commitDetails, { verbose: !0 }), await restore(), isJson() || console.log();
587
607
  } catch (error) {
588
608
  throw isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: `Commit failed and rolled back: ${getErrorMessage(error)}` })) : console.log(ansis.red(`\u2717 Commit failed and rolled back: ${getErrorMessage(error)}`)), isTelemetryEnabled && span.setAttribute("commit.rolledBack", !0), error;
589
609
  }
590
610
  } catch (error) {
591
- isTelemetryEnabled && (span.recordException(error), span.setStatus({
611
+ if (isTelemetryEnabled && (span.recordException(error), span.setStatus({
592
612
  code: 2,
593
613
  message: getErrorMessage(error)
594
- })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span.end(), process.exit(1);
614
+ })), isJson())
615
+ console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) }));
616
+ else if (console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), error instanceof CLIError && error.suggestions?.length)
617
+ for (const suggestion of error.suggestions)
618
+ console.error(ansis.dim(` ${suggestion}`));
619
+ await restore().catch((restoreError) => console.error(ansis.red(`Error: could not reactivate the previous changeset: ${getErrorMessage(restoreError)}`))), span.end(), process.exit(1);
595
620
  }
596
621
  }, {
597
622
  "commit.validate": options?.validate !== !1,
@@ -744,16 +769,20 @@ Examples:
744
769
  $ dr changeset diff
745
770
  $ dr changeset diff --layer api`).action(async (options) => {
746
771
  await changesetDiffCommand(options);
747
- }), changesetGroup.command("commit").description("Apply the active changeset's staged changes to the base model").allowExcessArguments(!1).option("--model <path>", "Path to model root (contains model/manifest.yaml)").addOption(new Option("--validate", "Run validation before commit (default)").hideHelp()).option("--no-validate", "Skip validation before commit (validation runs by default; only errors the changeset introduces block, errors already in the base model are reported as a count)").option("--force", "Commit despite drift warnings (does not skip validation)", !1).addHelpText("after", `
772
+ }), changesetGroup.command("commit [changeset]").description("Apply a changeset's staged changes to the base model (the active one, or the one named)").allowExcessArguments(!1).option("--model <path>", "Path to model root (contains model/manifest.yaml)").addOption(new Option("--validate", "Run validation before commit (default)").hideHelp()).option("--no-validate", "Skip validation before commit (validation runs by default; only errors the changeset introduces block, errors already in the base model are reported as a count)").option("--force", "Commit despite drift warnings (does not skip validation)", !1).addHelpText("after", `
748
773
  Validation compares the model with and without the changeset. Errors the
749
774
  changeset introduces block the commit; errors the base model already had are
750
775
  not counted against it, and the output says how many there were.
751
776
 
777
+ A named changeset (id or name) need not be active: it is activated for the
778
+ commit, and the changeset that was active before is active again afterwards.
779
+
752
780
  Examples:
753
781
  $ dr changeset commit
782
+ $ dr changeset commit add-order-api # commit the changeset named add-order-api
754
783
  $ dr changeset commit --force # commit despite drift
755
- $ dr changeset commit --no-validate # skip validation entirely`).action(async (options) => {
756
- await changesetCommitCommand(options);
784
+ $ dr changeset commit --no-validate # skip validation entirely`).action(async (name, options) => {
785
+ await changesetCommitCommand(name, options);
757
786
  }), changesetGroup.command("export <changeset-id>").description("Export changeset to portable file").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("-o, --output <file>", "Output file path").option("-f, --format <format>", "Export format (yaml|json|patch)", "yaml").addHelpText("after", `
758
787
  Examples:
759
788
  $ dr changeset export api-updates
@@ -767,12 +796,13 @@ Examples:
767
796
  $ dr changeset import ../team-changes.json
768
797
  $ dr changeset import changes.yaml --force`).action(async (file, options) => {
769
798
  await changesetImportCommand(file, options);
770
- }), changesetGroup.command("verify [planned]").description("Compare a planned changeset with what was built (an as-built changeset, or the base model)").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("--against <changeset>", "As-built changeset (ID or name) to compare with").option("--base", "Compare with the base model").option("--no-match-by-source", "Do not match elements by shared source file").option("--json", "Output as JSON").addHelpText("after", `
799
+ }), changesetGroup.command("verify [planned]").description("Compare a planned changeset with what was built (an as-built changeset, or the base model)").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("--against <changeset>", "As-built changeset (ID or name) to compare with").option("--base", "Compare with the base model").option("--extracted", "With --base: the as-built side landed from codebase_extract, so planned parts extraction can't observe are unverifiable, not missing").option("--no-match-by-source", "Do not match elements by shared source file").option("--json", "Output as JSON").addHelpText("after", `
771
800
  The planned changeset defaults to the active one.
772
801
 
773
802
  Examples:
774
803
  $ dr changeset verify export-orders --against export-orders-as-built
775
- $ dr changeset verify export-orders --base --json`).action(async (planned, options) => {
804
+ $ dr changeset verify export-orders --base --json
805
+ $ dr changeset verify export-orders --base --extracted`).action(async (planned, options) => {
776
806
  await changesetVerifyCommand(planned, options);
777
807
  }), changesetGroup.showSuggestionAfterError();
778
808
  }