@documentation-robotics/cli 0.1.13 → 0.3.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 (351) hide show
  1. package/README.md +74 -2
  2. package/dist/analysis/relationship-classifier.js +2 -9
  3. package/dist/analysis/relationship-classifier.js.map +2 -2
  4. package/dist/analyzers/call-graph-utils.js +2 -2
  5. package/dist/analyzers/call-graph-utils.js.map +2 -2
  6. package/dist/analyzers/cards/build.js +171 -0
  7. package/dist/analyzers/cards/build.js.map +7 -0
  8. package/dist/analyzers/cards/classify.js +42 -0
  9. package/dist/analyzers/cards/classify.js.map +7 -0
  10. package/dist/analyzers/cards/edge-index.js +44 -0
  11. package/dist/analyzers/cards/edge-index.js.map +7 -0
  12. package/dist/analyzers/cards/facts.js +46 -0
  13. package/dist/analyzers/cards/facts.js.map +7 -0
  14. package/dist/analyzers/cards/graph.js +42 -0
  15. package/dist/analyzers/cards/graph.js.map +7 -0
  16. package/dist/analyzers/cards/index.js +8 -0
  17. package/dist/analyzers/cards/index.js.map +7 -0
  18. package/dist/analyzers/cards/tier0.js +18 -0
  19. package/dist/analyzers/cards/tier0.js.map +7 -0
  20. package/dist/analyzers/cards/types.js +1 -0
  21. package/dist/analyzers/cards/types.js.map +7 -0
  22. package/dist/analyzers/cbm-analyzer.js +195 -171
  23. package/dist/analyzers/cbm-analyzer.js.map +2 -2
  24. package/dist/analyzers/cbm-responses.js +336 -0
  25. package/dist/analyzers/cbm-responses.js.map +7 -0
  26. package/dist/analyzers/edge-relationships.js +45 -0
  27. package/dist/analyzers/edge-relationships.js.map +7 -0
  28. package/dist/analyzers/mapping-loader.js +9 -0
  29. package/dist/analyzers/mapping-loader.js.map +2 -2
  30. package/dist/analyzers/tier0-rules.js +153 -0
  31. package/dist/analyzers/tier0-rules.js.map +7 -0
  32. package/dist/analyzers/types.js.map +1 -1
  33. package/dist/analyzers/verify-engine.js +22 -19
  34. package/dist/analyzers/verify-engine.js.map +2 -2
  35. package/dist/audit/relationships/ai/prompts.js +10 -10
  36. package/dist/audit/relationships/ai/prompts.js.map +2 -2
  37. package/dist/audit/relationships/analysis/layer-templates.js +52 -52
  38. package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
  39. package/dist/cli.js +112 -28
  40. package/dist/cli.js.map +3 -3
  41. package/dist/coding-agents/agents/claude-code.js +3 -1
  42. package/dist/coding-agents/agents/claude-code.js.map +2 -2
  43. package/dist/coding-agents/claude-code-client.js +2 -1
  44. package/dist/coding-agents/claude-code-client.js.map +2 -2
  45. package/dist/coding-agents/copilot-client.js +17 -12
  46. package/dist/coding-agents/copilot-client.js.map +2 -2
  47. package/dist/coding-agents/default-permissions.js +152 -0
  48. package/dist/coding-agents/default-permissions.js.map +7 -0
  49. package/dist/coding-agents/drbot-mcp.js +167 -0
  50. package/dist/coding-agents/drbot-mcp.js.map +7 -0
  51. package/dist/commands/add.js +12 -7
  52. package/dist/commands/add.js.map +2 -2
  53. package/dist/commands/analyzer.js +134 -23
  54. package/dist/commands/analyzer.js.map +2 -2
  55. package/dist/commands/audit.js +6 -6
  56. package/dist/commands/audit.js.map +2 -2
  57. package/dist/commands/changeset-verify.js +55 -0
  58. package/dist/commands/changeset-verify.js.map +7 -0
  59. package/dist/commands/changeset.js +261 -147
  60. package/dist/commands/changeset.js.map +2 -2
  61. package/dist/commands/delete.js +52 -19
  62. package/dist/commands/delete.js.map +3 -3
  63. package/dist/commands/element.js +3 -2
  64. package/dist/commands/element.js.map +2 -2
  65. package/dist/commands/farm.js +773 -0
  66. package/dist/commands/farm.js.map +7 -0
  67. package/dist/commands/init.js +11 -1
  68. package/dist/commands/init.js.map +2 -2
  69. package/dist/commands/mcp.js +122 -0
  70. package/dist/commands/mcp.js.map +7 -0
  71. package/dist/commands/relationship.js +251 -47
  72. package/dist/commands/relationship.js.map +2 -2
  73. package/dist/commands/reports.js +1 -1
  74. package/dist/commands/reports.js.map +2 -2
  75. package/dist/commands/update.js +56 -24
  76. package/dist/commands/update.js.map +2 -2
  77. package/dist/commands/upgrade.js +99 -11
  78. package/dist/commands/upgrade.js.map +3 -3
  79. package/dist/commands/validate.js +41 -3
  80. package/dist/commands/validate.js.map +2 -2
  81. package/dist/commands/version.js +2 -2
  82. package/dist/commands/version.js.map +2 -2
  83. package/dist/commands/visualize.js +4 -3
  84. package/dist/commands/visualize.js.map +2 -2
  85. package/dist/config.js +83 -0
  86. package/dist/config.js.map +7 -0
  87. package/dist/core/annotation-store.js +138 -0
  88. package/dist/core/annotation-store.js.map +7 -0
  89. package/dist/core/catalog-migration.js +33 -0
  90. package/dist/core/catalog-migration.js.map +7 -0
  91. package/dist/core/changeset-analysis.js +421 -0
  92. package/dist/core/changeset-analysis.js.map +7 -0
  93. package/dist/core/changeset-base-index.js +153 -0
  94. package/dist/core/changeset-base-index.js.map +7 -0
  95. package/dist/core/changeset-brief-delta.js +102 -0
  96. package/dist/core/changeset-brief-delta.js.map +7 -0
  97. package/dist/core/changeset-graph.js +281 -0
  98. package/dist/core/changeset-graph.js.map +7 -0
  99. package/dist/core/changeset-only-guard.js +32 -0
  100. package/dist/core/changeset-only-guard.js.map +7 -0
  101. package/dist/core/changeset-summary.js +68 -0
  102. package/dist/core/changeset-summary.js.map +7 -0
  103. package/dist/core/changeset-validator.js +66 -20
  104. package/dist/core/changeset-validator.js.map +2 -2
  105. package/dist/core/changeset-verify.js +518 -0
  106. package/dist/core/changeset-verify.js.map +7 -0
  107. package/dist/core/changeset.js +56 -9
  108. package/dist/core/changeset.js.map +2 -2
  109. package/dist/core/element-type-migration.js +145 -0
  110. package/dist/core/element-type-migration.js.map +7 -0
  111. package/dist/core/farm-manifest.js +140 -0
  112. package/dist/core/farm-manifest.js.map +7 -0
  113. package/dist/core/farm-sync-engine.js +346 -0
  114. package/dist/core/farm-sync-engine.js.map +7 -0
  115. package/dist/core/farm-sync-state.js +167 -0
  116. package/dist/core/farm-sync-state.js.map +7 -0
  117. package/dist/core/manifest.js +6 -3
  118. package/dist/core/manifest.js.map +2 -2
  119. package/dist/core/migration-registry.js +84 -1
  120. package/dist/core/migration-registry.js.map +2 -2
  121. package/dist/core/model-upgrade.js +102 -0
  122. package/dist/core/model-upgrade.js.map +7 -0
  123. package/dist/core/model.js +257 -44
  124. package/dist/core/model.js.map +3 -3
  125. package/dist/core/mutation-handler.js +71 -6
  126. package/dist/core/mutation-handler.js.map +2 -2
  127. package/dist/core/relationship-migration-table.js +6639 -0
  128. package/dist/core/relationship-migration-table.js.map +7 -0
  129. package/dist/core/relationship-migration.js +272 -0
  130. package/dist/core/relationship-migration.js.map +7 -0
  131. package/dist/core/relationship-prune.js +49 -0
  132. package/dist/core/relationship-prune.js.map +7 -0
  133. package/dist/core/relationship-staging.js +68 -0
  134. package/dist/core/relationship-staging.js.map +7 -0
  135. package/dist/core/relationships.js +26 -4
  136. package/dist/core/relationships.js.map +2 -2
  137. package/dist/core/report-data-model.js +7 -13
  138. package/dist/core/report-data-model.js.map +2 -2
  139. package/dist/core/staged-changeset-storage.js +64 -17
  140. package/dist/core/staged-changeset-storage.js.map +2 -2
  141. package/dist/core/staged-source-reference.js +28 -0
  142. package/dist/core/staged-source-reference.js.map +7 -0
  143. package/dist/core/staging-area.js +83 -18
  144. package/dist/core/staging-area.js.map +2 -2
  145. package/dist/core/virtual-projection.js +14 -5
  146. package/dist/core/virtual-projection.js.map +2 -2
  147. package/dist/export/archimate-exporter.js +1 -0
  148. package/dist/export/archimate-exporter.js.map +2 -2
  149. package/dist/generated/compiled-validators.js +2 -2
  150. package/dist/generated/compiled-validators.js.map +2 -2
  151. package/dist/generated/layer-registry.js +16 -16
  152. package/dist/generated/layer-registry.js.map +2 -2
  153. package/dist/generated/node-types.js +186 -168
  154. package/dist/generated/node-types.js.map +2 -2
  155. package/dist/generated/relationship-index.js +2209 -7727
  156. package/dist/generated/relationship-index.js.map +2 -2
  157. package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
  158. package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
  159. package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
  160. package/dist/integrations/claude_code/commands/dr-map.md +13 -0
  161. package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
  162. package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
  163. package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
  164. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
  165. package/dist/integrations/claude_code/docs/MCP_SERVER.md +79 -0
  166. package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
  167. package/dist/integrations/claude_code/docs/README.md +52 -1
  168. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
  169. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
  170. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
  171. package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
  172. package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
  173. package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
  174. package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
  175. package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
  176. package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
  177. package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
  178. package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
  179. package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
  180. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
  181. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
  182. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
  183. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
  184. package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
  185. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
  186. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
  187. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
  188. package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
  189. package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
  190. package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
  191. package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
  192. package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
  193. package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
  194. package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
  195. package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
  196. package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
  197. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
  198. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
  199. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
  200. package/dist/integrations/hash-utils.js.map +2 -2
  201. package/dist/mcp/api-key-manager.js +111 -0
  202. package/dist/mcp/api-key-manager.js.map +7 -0
  203. package/dist/mcp/changeset-only.js +34 -0
  204. package/dist/mcp/changeset-only.js.map +7 -0
  205. package/dist/mcp/http-transport.js +176 -0
  206. package/dist/mcp/http-transport.js.map +7 -0
  207. package/dist/mcp/resource-registry.js +97 -0
  208. package/dist/mcp/resource-registry.js.map +7 -0
  209. package/dist/mcp/tool-registry.js +60 -0
  210. package/dist/mcp/tool-registry.js.map +7 -0
  211. package/dist/mcp/tools/annotation-create.js +39 -0
  212. package/dist/mcp/tools/annotation-create.js.map +7 -0
  213. package/dist/mcp/tools/annotation-delete.js +29 -0
  214. package/dist/mcp/tools/annotation-delete.js.map +7 -0
  215. package/dist/mcp/tools/annotation-get.js +29 -0
  216. package/dist/mcp/tools/annotation-get.js.map +7 -0
  217. package/dist/mcp/tools/annotation-list.js +24 -0
  218. package/dist/mcp/tools/annotation-list.js.map +7 -0
  219. package/dist/mcp/tools/annotation-reply.js +34 -0
  220. package/dist/mcp/tools/annotation-reply.js.map +7 -0
  221. package/dist/mcp/tools/annotation-shared.js +10 -0
  222. package/dist/mcp/tools/annotation-shared.js.map +7 -0
  223. package/dist/mcp/tools/annotation-update.js +39 -0
  224. package/dist/mcp/tools/annotation-update.js.map +7 -0
  225. package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
  226. package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
  227. package/dist/mcp/tools/changeset-editing.js +184 -0
  228. package/dist/mcp/tools/changeset-editing.js.map +7 -0
  229. package/dist/mcp/tools/changeset-lifecycle.js +83 -0
  230. package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
  231. package/dist/mcp/tools/changeset-list.js +33 -0
  232. package/dist/mcp/tools/changeset-list.js.map +7 -0
  233. package/dist/mcp/tools/changeset-shared.js +31 -0
  234. package/dist/mcp/tools/changeset-shared.js.map +7 -0
  235. package/dist/mcp/tools/changeset-show.js +55 -0
  236. package/dist/mcp/tools/changeset-show.js.map +7 -0
  237. package/dist/mcp/tools/changeset-verify.js +43 -0
  238. package/dist/mcp/tools/changeset-verify.js.map +7 -0
  239. package/dist/mcp/tools/chat-launch.js +50 -0
  240. package/dist/mcp/tools/chat-launch.js.map +7 -0
  241. package/dist/mcp/tools/chat-status.js +26 -0
  242. package/dist/mcp/tools/chat-status.js.map +7 -0
  243. package/dist/mcp/tools/index.js +65 -0
  244. package/dist/mcp/tools/index.js.map +7 -0
  245. package/dist/mcp/tools/model-add.js +107 -0
  246. package/dist/mcp/tools/model-add.js.map +7 -0
  247. package/dist/mcp/tools/model-delete.js +99 -0
  248. package/dist/mcp/tools/model-delete.js.map +7 -0
  249. package/dist/mcp/tools/model-export.js +72 -0
  250. package/dist/mcp/tools/model-export.js.map +7 -0
  251. package/dist/mcp/tools/model-info.js +51 -0
  252. package/dist/mcp/tools/model-info.js.map +7 -0
  253. package/dist/mcp/tools/model-list.js +53 -0
  254. package/dist/mcp/tools/model-list.js.map +7 -0
  255. package/dist/mcp/tools/model-reload.js +34 -0
  256. package/dist/mcp/tools/model-reload.js.map +7 -0
  257. package/dist/mcp/tools/model-search.js +102 -0
  258. package/dist/mcp/tools/model-search.js.map +7 -0
  259. package/dist/mcp/tools/model-show.js +68 -0
  260. package/dist/mcp/tools/model-show.js.map +7 -0
  261. package/dist/mcp/tools/model-stats.js +22 -0
  262. package/dist/mcp/tools/model-stats.js.map +7 -0
  263. package/dist/mcp/tools/model-trace.js +118 -0
  264. package/dist/mcp/tools/model-trace.js.map +7 -0
  265. package/dist/mcp/tools/model-update.js +121 -0
  266. package/dist/mcp/tools/model-update.js.map +7 -0
  267. package/dist/mcp/tools/model-validate.js +78 -0
  268. package/dist/mcp/tools/model-validate.js.map +7 -0
  269. package/dist/mcp/tools/relationship-tools.js +98 -0
  270. package/dist/mcp/tools/relationship-tools.js.map +7 -0
  271. package/dist/mcp/tools/shared.js +126 -0
  272. package/dist/mcp/tools/shared.js.map +7 -0
  273. package/dist/mcp/tools/spec-type.js +90 -0
  274. package/dist/mcp/tools/spec-type.js.map +7 -0
  275. package/dist/reports/model-layer-report-generator.js +5 -3
  276. package/dist/reports/model-layer-report-generator.js.map +2 -2
  277. package/dist/reports/model-readme-generator.js +116 -0
  278. package/dist/reports/model-readme-generator.js.map +7 -0
  279. package/dist/reports/model-report-data.js +37 -1
  280. package/dist/reports/model-report-data.js.map +2 -2
  281. package/dist/reports/model-report-orchestrator.js +29 -6
  282. package/dist/reports/model-report-orchestrator.js.map +2 -2
  283. package/dist/reports/model-report-utils.js +9 -0
  284. package/dist/reports/model-report-utils.js.map +7 -0
  285. package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
  286. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  287. package/dist/schemas/bundled/api.json +1029 -1658
  288. package/dist/schemas/bundled/apm.json +499 -734
  289. package/dist/schemas/bundled/application.json +745 -370
  290. package/dist/schemas/bundled/base.json +3 -220
  291. package/dist/schemas/bundled/business.json +452 -366
  292. package/dist/schemas/bundled/data-model.json +403 -383
  293. package/dist/schemas/bundled/data-store.json +334 -558
  294. package/dist/schemas/bundled/manifest.json +18 -18
  295. package/dist/schemas/bundled/motivation.json +349 -368
  296. package/dist/schemas/bundled/navigation.json +415 -731
  297. package/dist/schemas/bundled/product.json +325 -159
  298. package/dist/schemas/bundled/security.json +864 -1232
  299. package/dist/schemas/bundled/technology.json +574 -703
  300. package/dist/schemas/bundled/testing.json +718 -1643
  301. package/dist/schemas/bundled/ux.json +739 -1352
  302. package/dist/server/schemas.js +8 -0
  303. package/dist/server/schemas.js.map +2 -2
  304. package/dist/server/server.js +234 -31
  305. package/dist/server/server.js.map +2 -2
  306. package/dist/server-entry.js +2 -1
  307. package/dist/server-entry.js.map +2 -2
  308. package/dist/telemetry/config.js +2 -40
  309. package/dist/telemetry/config.js.map +2 -2
  310. package/dist/telemetry/index.js +1 -1
  311. package/dist/types/index.js.map +2 -2
  312. package/dist/utils/ansi-suppressor.js +23 -0
  313. package/dist/utils/ansi-suppressor.js.map +7 -0
  314. package/dist/utils/attribute-patch.js +41 -0
  315. package/dist/utils/attribute-patch.js.map +7 -0
  316. package/dist/utils/color-utils.js +34 -0
  317. package/dist/utils/color-utils.js.map +7 -0
  318. package/dist/utils/element-reference-shape.js +15 -1
  319. package/dist/utils/element-reference-shape.js.map +3 -3
  320. package/dist/utils/error-hints.js +29 -0
  321. package/dist/utils/error-hints.js.map +7 -0
  322. package/dist/utils/errors.js +75 -10
  323. package/dist/utils/errors.js.map +2 -2
  324. package/dist/utils/globals.js +17 -1
  325. package/dist/utils/globals.js.map +2 -2
  326. package/dist/utils/project-paths.js +50 -3
  327. package/dist/utils/project-paths.js.map +2 -2
  328. package/dist/utils/reference-path-parser.js +49 -0
  329. package/dist/utils/reference-path-parser.js.map +7 -0
  330. package/dist/utils/source-reference.js +17 -10
  331. package/dist/utils/source-reference.js.map +2 -2
  332. package/dist/utils/spec-version.js +10 -2
  333. package/dist/utils/spec-version.js.map +2 -2
  334. package/dist/validators/composed-reference-validator.js +240 -0
  335. package/dist/validators/composed-reference-validator.js.map +7 -0
  336. package/dist/validators/reference-validator.js +57 -13
  337. package/dist/validators/reference-validator.js.map +3 -3
  338. package/dist/validators/relationship-schema-validator.js +33 -5
  339. package/dist/validators/relationship-schema-validator.js.map +3 -3
  340. package/dist/validators/schema-validator.js +27 -9
  341. package/dist/validators/schema-validator.js.map +2 -2
  342. package/dist/validators/types.js.map +1 -1
  343. package/dist/viewer/README.md +6 -6
  344. package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
  345. package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
  346. package/dist/viewer/index.html +2 -2
  347. package/dist/viewer/manifest.json +10 -10
  348. package/dist/viewer/version.json +1 -1
  349. package/package.json +7 -3
  350. package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
  351. package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
@@ -4,11 +4,14 @@ import { StagingAreaManager } from "../core/staging-area.js";
4
4
  import { ChangesetExporter } from "../core/changeset-exporter.js";
5
5
  import { StagedChangesetStorage } from "../core/staged-changeset-storage.js";
6
6
  import { ValidationFormatter } from "../validators/validation-formatter.js";
7
+ import { Option } from "commander";
7
8
  import * as prompts from "@clack/prompts";
8
9
  import path from "path";
9
10
  import { isTelemetryEnabled, startSpan, endSpan, startActiveSpan } from "../telemetry/index.js";
10
- import { getErrorMessage } from "../utils/errors.js";
11
+ import { getErrorMessage, handleSuccess, handleInfo, handleWarning } from "../utils/errors.js";
12
+ import { isJson } from "../utils/globals.js";
11
13
  import { findElementLayer } from "../utils/element-utils.js";
14
+ import { changesetVerifyCommand } from "./changeset-verify.js";
12
15
  function generateImportedChangesetId() {
13
16
  return `imported-${Date.now()}`;
14
17
  }
@@ -18,8 +21,8 @@ async function changesetCreateCommand(name, options) {
18
21
  "changeset.hasDescription": !!options.description
19
22
  }) : null;
20
23
  try {
21
- const model = await Model.load(process.cwd(), { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model);
22
- await manager.load(name) && (console.error(ansis.red(`Error: Changeset '${name}' already exists`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset already exists" }), endSpan(span), process.exit(1));
24
+ const model = await Model.load(options.model, { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model);
25
+ await manager.load(name) && (isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: `Changeset '${name}' already exists` })) : console.error(ansis.red(`Error: Changeset '${name}' already exists`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset already exists" }), endSpan(span), process.exit(1));
23
26
  let description = options.description;
24
27
  if (!description) {
25
28
  const isInteractive = process.stdin.isTTY;
@@ -31,24 +34,30 @@ async function changesetCreateCommand(name, options) {
31
34
  }
32
35
  }
33
36
  const changeset = await manager.create(name, description || void 0), previouslyActive = await manager.getActiveId();
34
- if (previouslyActive) {
37
+ if (previouslyActive && !isJson()) {
35
38
  const previous = await manager.load(previouslyActive);
36
39
  console.log(ansis.yellow(` Deactivating changeset: ${ansis.bold(previous?.name ?? previouslyActive)}`));
37
40
  }
38
- await manager.setActive(changeset.id), isTelemetryEnabled && span && (span.setAttribute("changeset.id", changeset.id), span.setStatus({ code: 0 })), console.log(ansis.green(`\u2713 Created and activated changeset: ${ansis.bold(name)}`)), changeset.description && console.log(ansis.dim(` ${changeset.description}`)), console.log(ansis.dim(` Path: documentation-robotics/changesets/${changeset.id}/`)), console.log();
41
+ await manager.setActive(changeset.id), isTelemetryEnabled && span && (span.setAttribute("changeset.id", changeset.id), span.setStatus({ code: 0 }));
42
+ const createDetails = {
43
+ changesetId: changeset.id,
44
+ changesetName: name,
45
+ path: `documentation-robotics/changesets/${changeset.id}/`
46
+ };
47
+ changeset.description && (createDetails.description = changeset.description), handleSuccess(`Created and activated changeset: ${ansis.bold(name)}`, createDetails, { verbose: !0 });
39
48
  } catch (error) {
40
49
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
41
50
  code: 2,
42
51
  message: getErrorMessage(error)
43
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
52
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
44
53
  } finally {
45
54
  endSpan(span);
46
55
  }
47
56
  }
48
- async function changesetListCommand() {
57
+ async function changesetListCommand(options = {}) {
49
58
  const span = isTelemetryEnabled ? startSpan("changeset.list") : null;
50
59
  try {
51
- const model = await Model.load(process.cwd(), { lazyLoad: !0 }), changesets = await new StagingAreaManager(model.rootPath, model).list();
60
+ const model = await Model.load(options.model, { lazyLoad: !0 }), changesets = await new StagingAreaManager(model.rootPath, model).list();
52
61
  if (isTelemetryEnabled && span && span.setAttribute("changeset.count", changesets.length), changesets.length === 0) {
53
62
  console.log(ansis.yellow("No changesets found")), console.log(), isTelemetryEnabled && span && span.setStatus({ code: 0 }), endSpan(span);
54
63
  return;
@@ -71,11 +80,19 @@ Changesets (${changesets.length}):
71
80
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
72
81
  code: 2,
73
82
  message: getErrorMessage(error)
74
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
83
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
75
84
  } finally {
76
85
  endSpan(span);
77
86
  }
78
87
  }
88
+ function reportCommitValidation(result, details) {
89
+ if (result.validation.ran === !1) {
90
+ delete details.validationPassed, details.validationSkipped = !0, isJson() || console.log(ansis.yellow("\u26A0 Validation skipped (--no-validate); run 'dr validate' to check the result"));
91
+ return;
92
+ }
93
+ const preExisting = result.validation.preExistingErrors ?? 0;
94
+ details.preExistingErrors = preExisting, preExisting > 0 && (details.preExistingSample = result.validation.preExistingSample ?? [], isJson() || console.log(ansis.yellow(`\u26A0 ${preExisting} validation error(s) already in the base model were ignored; the changeset introduced none. Run 'dr validate' to see them.`)));
95
+ }
79
96
  async function changesetApplyCommand(name, options) {
80
97
  const span = isTelemetryEnabled ? startSpan("changeset.apply", {
81
98
  "changeset.name": name,
@@ -83,92 +100,122 @@ async function changesetApplyCommand(name, options) {
83
100
  "apply.force": options?.force === !0
84
101
  }) : null;
85
102
  try {
86
- const model = await Model.load(process.cwd(), { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), changeset = await manager.load(name);
87
- changeset || (console.error(ansis.red(`Error: Changeset '${name}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1)), isTelemetryEnabled && span && (span.setAttribute("changeset.id", changeset.id), span.setAttribute("changeset.changeCount", changeset.changes.length)), console.log(ansis.bold(`
103
+ const model = await Model.load(options?.model, { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), changeset = await manager.load(name);
104
+ if (changeset || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Changeset '${name}' not found` })) : console.error(ansis.red(`Error: Changeset '${name}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1)), isTelemetryEnabled && span && (span.setAttribute("changeset.id", changeset.id), span.setAttribute("changeset.changeCount", changeset.changes.length)), !isJson()) {
105
+ console.log(ansis.bold(`
88
106
  Applying changeset: ${ansis.cyan(name)}
89
107
  `)), console.log(ansis.dim(`Changes: ${changeset.changes.length}`));
90
- const preApplyStats = ValidationFormatter.calculateStats(model);
91
- preApplyStats.orphanedElements.length > 0 && console.log(ansis.dim(`Model health before apply: ${preApplyStats.orphanedElements.length} orphaned element(s) \u2014 run 'dr validate --orphans' for details`));
108
+ const preApplyStats = ValidationFormatter.calculateStats(model);
109
+ preApplyStats.orphanedElements.length > 0 && console.log(ansis.dim(`Model health before apply: ${preApplyStats.orphanedElements.length} orphaned element(s) \u2014 run 'dr validate --orphans' for details`));
110
+ }
92
111
  const changesetId = changeset.id || name;
93
- changesetId || (console.error(ansis.red("Error: Changeset ID could not be determined") + `
112
+ if (!changesetId) {
113
+ const errMsg = "Changeset ID could not be determined";
114
+ isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: errMsg })) : console.error(ansis.red("Error: " + errMsg) + `
94
115
  ` + ansis.dim("This indicates a corrupted changeset. The changeset exists but has no ID field.") + `
95
116
  ` + ansis.dim(`Try:
96
117
  `) + ansis.dim(` 1. Run 'dr changeset list' to see available changesets
97
118
  `) + ansis.dim(` 2. Delete and recreate the changeset if possible
98
- `) + ansis.dim(" 3. Contact support if this persists")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset ID missing" }), endSpan(span), process.exit(1));
119
+ `) + ansis.dim(" 3. Contact support if this persists")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset ID missing" }), endSpan(span), process.exit(1);
120
+ }
99
121
  const result = await manager.apply(model, changesetId, {
100
122
  validate: options?.validate,
101
123
  force: options?.force
102
124
  });
103
- if (console.log(), console.log(ansis.green(`\u2713 Applied ${result.committed} change(s) from changeset`)), isTelemetryEnabled && span && (span.setAttribute("apply.committed", result.committed), span.setAttribute("apply.failed", result.failed), span.setAttribute("apply.validationPassed", result.validation.passed)), result.failed > 0) {
104
- console.log(ansis.red(`\u2717 Failed to apply ${result.failed} change(s):`));
125
+ handleInfo(""), isTelemetryEnabled && span && (span.setAttribute("apply.committed", result.committed), span.setAttribute("apply.failed", result.failed), span.setAttribute("apply.validationPassed", result.validation.passed));
126
+ const applyDetails = {
127
+ changesetName: name,
128
+ changesetId: changeset.id,
129
+ committed: result.committed,
130
+ failed: result.failed,
131
+ validationPassed: result.validation.passed
132
+ };
133
+ if (reportCommitValidation(result, applyDetails), result.failed > 0 && (applyDetails.errors = result.validation.errors, !isJson())) {
134
+ handleInfo(ansis.red(`\u2717 Failed to apply ${result.failed} change(s):`));
105
135
  for (const error of result.validation.errors)
106
- console.log(ansis.dim(` - ${error}`));
136
+ handleInfo(ansis.dim(` - ${error}`));
107
137
  }
108
- model.manifest.changeset_history || (model.manifest.changeset_history = []), model.manifest.changeset_history.push({
138
+ handleSuccess(`Applied ${result.committed} change(s) from changeset`, applyDetails), model.manifest.changeset_history || (model.manifest.changeset_history = []), model.manifest.changeset_history.push({
109
139
  name,
110
140
  committed_at: (/* @__PURE__ */ new Date()).toISOString(),
111
141
  action: "committed"
112
- }), await model.saveDirtyLayers(), await model.saveManifest(), result.failed === 0 && console.log(ansis.dim("Changeset marked as committed")), isTelemetryEnabled && span && span.setStatus({ code: 0 }), console.log();
142
+ }), await model.saveDirtyLayers(), await model.saveManifest(), result.failed === 0 && handleInfo(ansis.dim("Changeset marked as committed")), isTelemetryEnabled && span && span.setStatus({ code: 0 }), handleInfo("");
113
143
  } catch (error) {
114
144
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
115
145
  code: 2,
116
146
  message: getErrorMessage(error)
117
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
147
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
118
148
  } finally {
119
149
  endSpan(span);
120
150
  }
121
151
  }
122
- async function changesetRevertCommand(name) {
152
+ async function changesetRevertCommand(name, options = {}) {
123
153
  const span = isTelemetryEnabled ? startSpan("changeset.revert", {
124
154
  "changeset.name": name
125
155
  }) : null;
126
156
  try {
127
- const model = await Model.load(process.cwd(), { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), changeset = await manager.load(name);
128
- changeset || (console.error(ansis.red(`Error: Changeset '${name}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1)), isTelemetryEnabled && span && (span.setAttribute("changeset.id", changeset.id), span.setAttribute("changeset.changeCount", changeset.changes.length)), console.log(ansis.bold(`
157
+ const model = await Model.load(options.model, { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), changeset = await manager.load(name);
158
+ changeset || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Changeset '${name}' not found` })) : console.error(ansis.red(`Error: Changeset '${name}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1)), isTelemetryEnabled && span && (span.setAttribute("changeset.id", changeset.id), span.setAttribute("changeset.changeCount", changeset.changes.length)), isJson() || (console.log(ansis.bold(`
129
159
  Reverting changeset: ${ansis.cyan(name)}
130
- `)), console.log(ansis.dim(`Changes to discard: ${changeset.changes.length}`));
160
+ `)), console.log(ansis.dim(`Changes to discard: ${changeset.changes.length}`)));
131
161
  const changesetId = changeset.id || name;
132
- changesetId || (console.error(ansis.red("Error: Changeset ID could not be determined")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset ID missing" }), endSpan(span), process.exit(1)), await manager.revert(changesetId), console.log(), console.log(ansis.green(`\u2713 Reverted changeset: ${name}`)), console.log(ansis.dim("Changeset marked as discarded")), isTelemetryEnabled && span && span.setStatus({ code: 0 }), console.log();
162
+ if (!changesetId) {
163
+ const errMsg = "Changeset ID could not be determined";
164
+ isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: errMsg })) : console.error(ansis.red("Error: " + errMsg) + `
165
+ ` + ansis.dim("The changeset must have an ID to proceed with revert.")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset ID missing" }), endSpan(span), process.exit(1);
166
+ }
167
+ await manager.revert(changesetId), handleInfo(""), handleSuccess(`Reverted changeset: ${name}`, {
168
+ changesetName: name,
169
+ changesetId,
170
+ changesetStatus: "discarded"
171
+ }), isTelemetryEnabled && span && span.setStatus({ code: 0 }), handleInfo("");
133
172
  } catch (error) {
134
173
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
135
174
  code: 2,
136
175
  message: getErrorMessage(error)
137
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
176
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
138
177
  } finally {
139
178
  endSpan(span);
140
179
  }
141
180
  }
142
- async function changesetActivateCommand(name) {
181
+ async function changesetActivateCommand(name, options = {}) {
143
182
  const span = isTelemetryEnabled ? startSpan("changeset.activate", {
144
183
  "changeset.name": name
145
184
  }) : null;
146
185
  try {
147
- const model = await Model.load(process.cwd(), { lazyLoad: !0 });
148
- await new StagingAreaManager(model.rootPath, model).setActive(name), console.log(ansis.green(`\u2713 Activated changeset: ${ansis.bold(name)}`)), console.log(ansis.dim(" All model changes will now be tracked in this changeset")), isTelemetryEnabled && span && span.setStatus({ code: 0 });
186
+ const model = await Model.load(options.model, { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), changeset = await manager.load(name);
187
+ changeset || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Changeset '${name}' not found` })) : console.error(ansis.red(`Error: Changeset '${name}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1)), await manager.setActive(name), handleSuccess(`Activated changeset: ${ansis.bold(name)}`, {
188
+ changesetName: name,
189
+ changesetId: changeset.id
190
+ }), isTelemetryEnabled && span && span.setStatus({ code: 0 });
149
191
  } catch (error) {
150
192
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
151
193
  code: 2,
152
194
  message: getErrorMessage(error)
153
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
195
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
154
196
  } finally {
155
197
  endSpan(span);
156
198
  }
157
199
  }
158
- async function changesetDeactivateCommand() {
200
+ async function changesetDeactivateCommand(options = {}) {
159
201
  const span = isTelemetryEnabled ? startSpan("changeset.deactivate") : null;
160
202
  try {
161
- const model = await Model.load(process.cwd(), { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), active = await manager.getActiveId();
203
+ const model = await Model.load(options.model, { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), active = await manager.getActiveId();
162
204
  if (!active) {
163
- console.log(ansis.yellow("No active changeset")), isTelemetryEnabled && span && (span.setAttribute("changeset.wasActive", !1), span.setStatus({ code: 0 })), endSpan(span);
205
+ handleInfo(ansis.yellow("No active changeset")), isTelemetryEnabled && span && (span.setAttribute("changeset.wasActive", !1), span.setStatus({ code: 0 })), endSpan(span);
164
206
  return;
165
207
  }
166
- isTelemetryEnabled && span && (span.setAttribute("changeset.name", active), span.setAttribute("changeset.wasActive", !0)), await manager.clearActive(), console.log(ansis.green(`\u2713 Deactivated changeset: ${ansis.bold(active)}`)), isTelemetryEnabled && span && span.setStatus({ code: 0 });
208
+ isTelemetryEnabled && span && (span.setAttribute("changeset.name", active), span.setAttribute("changeset.wasActive", !0));
209
+ const changeset = await manager.load(active);
210
+ await manager.clearActive(), handleSuccess(`Deactivated changeset: ${ansis.bold(active)}`, {
211
+ changesetId: active,
212
+ name: changeset?.name || active
213
+ }), isTelemetryEnabled && span && span.setStatus({ code: 0 });
167
214
  } catch (error) {
168
215
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
169
216
  code: 2,
170
217
  message: getErrorMessage(error)
171
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
218
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
172
219
  } finally {
173
220
  endSpan(span);
174
221
  }
@@ -179,34 +226,39 @@ async function changesetDeleteCommand(name, options) {
179
226
  "delete.force": options.force === !0
180
227
  }) : null;
181
228
  try {
182
- const model = await Model.load(process.cwd(), { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), changeset = await manager.load(name);
183
- if (changeset || (console.error(ansis.red(`Error: Changeset '${name}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1)), await manager.getActiveId() === changeset.id && (console.error(ansis.red(`Error: Cannot delete active changeset '${name}'`)), console.log(ansis.dim(" Run `dr changeset deactivate` first")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Cannot delete active changeset" }), endSpan(span), process.exit(1)), !options.force) {
229
+ const model = await Model.load(options.model, { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), changeset = await manager.load(name);
230
+ if (changeset || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Changeset '${name}' not found` })) : console.error(ansis.red(`Error: Changeset '${name}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1)), await manager.getActiveId() === changeset.id && (isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: `Cannot delete active changeset '${name}'` })) : (console.error(ansis.red(`Error: Cannot delete active changeset '${name}'`)), console.log(ansis.dim(" Run `dr changeset deactivate` first"))), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Cannot delete active changeset" }), endSpan(span), process.exit(1)), !options.force) {
184
231
  const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
185
232
  if (isTelemetryEnabled && span && span.setAttribute("cli.interactive", isInteractive), isInteractive) {
186
233
  const confirm = await prompts.confirm({
187
234
  message: `Delete changeset '${name}'? This cannot be undone.`
188
235
  });
189
236
  if (!confirm || typeof confirm != "boolean") {
190
- console.log(ansis.yellow("Deletion cancelled")), isTelemetryEnabled && span && (span.setAttribute("delete.cancelled", !0), span.setStatus({ code: 0 })), endSpan(span);
237
+ handleSuccess("Deletion cancelled", {
238
+ status: "cancelled"
239
+ }), isTelemetryEnabled && span && (span.setAttribute("delete.cancelled", !0), span.setStatus({ code: 0 })), endSpan(span);
191
240
  return;
192
241
  }
193
242
  } else
194
- console.error(ansis.red("Error: Cannot confirm deletion in non-interactive environment")), console.log(ansis.dim(" Use --force flag to confirm deletion")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Interactive confirmation required" }), endSpan(span), process.exit(1);
243
+ isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: "Cannot confirm deletion in non-interactive environment" })) : (console.error(ansis.red("Error: Cannot confirm deletion in non-interactive environment")), console.log(ansis.dim(" Use --force flag to confirm deletion"))), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Interactive confirmation required" }), endSpan(span), process.exit(1);
195
244
  }
196
- await manager.delete(name), console.log(ansis.green(`\u2713 Deleted changeset: ${ansis.bold(name)}`)), isTelemetryEnabled && span && span.setStatus({ code: 0 });
245
+ await manager.delete(name), handleSuccess(`Deleted changeset: ${ansis.bold(name)}`, {
246
+ changesetName: name,
247
+ changesetId: changeset.id
248
+ }), isTelemetryEnabled && span && span.setStatus({ code: 0 });
197
249
  } catch (error) {
198
250
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
199
251
  code: 2,
200
252
  message: getErrorMessage(error)
201
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
253
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
202
254
  } finally {
203
255
  endSpan(span);
204
256
  }
205
257
  }
206
- async function changesetStatusCommand() {
258
+ async function changesetStatusCommand(options = {}) {
207
259
  const span = isTelemetryEnabled ? startSpan("changeset.status") : null;
208
260
  try {
209
- const model = await Model.load(process.cwd(), { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), active = await manager.getActiveId();
261
+ const model = await Model.load(options.model, { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), active = await manager.getActiveId();
210
262
  if (!active) {
211
263
  console.log(ansis.dim("No active changeset")), isTelemetryEnabled && span && (span.setAttribute("changeset.hasActive", !1), span.setStatus({ code: 0 })), endSpan(span);
212
264
  return;
@@ -225,7 +277,7 @@ async function changesetStatusCommand() {
225
277
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
226
278
  code: 2,
227
279
  message: getErrorMessage(error)
228
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
280
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
229
281
  } finally {
230
282
  endSpan(span);
231
283
  }
@@ -235,49 +287,65 @@ async function changesetStagedCommand(options) {
235
287
  "staged.layer": options.layer
236
288
  }) : null;
237
289
  try {
238
- const model = await Model.load(process.cwd(), { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), activeChangeset = await manager.getActiveId();
290
+ const model = await Model.load(options.model, { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), activeChangeset = await manager.getActiveId();
239
291
  if (!activeChangeset) {
240
- console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
292
+ isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: "No active changeset" })) : console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
241
293
  return;
242
294
  }
243
295
  const changeset = await manager.load(activeChangeset);
244
- changeset || (console.error(ansis.red(`Error: Changeset '${activeChangeset}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1));
296
+ changeset || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Changeset '${activeChangeset}' not found` })) : console.error(ansis.red(`Error: Changeset '${activeChangeset}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1));
245
297
  let changes = changeset.changes;
246
298
  if (options.layer && (changes = changes.filter((c) => c.layerName === options.layer)), isTelemetryEnabled && span && (span.setAttribute("staged.changeCount", changes.length), span.setAttribute("staged.totalChanges", changeset.changes.length)), changes.length === 0) {
247
- console.log(ansis.yellow("No staged changes")), isTelemetryEnabled && span && span.setStatus({ code: 0 }), endSpan(span);
299
+ handleSuccess("No staged changes", {
300
+ changesetId: activeChangeset,
301
+ changeCount: 0,
302
+ changes: []
303
+ }, { verbose: !0 }), isTelemetryEnabled && span && span.setStatus({ code: 0 }), endSpan(span);
248
304
  return;
249
305
  }
250
- console.log(ansis.bold(`
306
+ if (!isJson()) {
307
+ console.log(ansis.bold(`
251
308
  Staged Changes (${changes.length}):
252
309
  `));
253
- const tableData = changes.map((c) => ({
254
- "Element ID": c.elementId,
255
- Layer: c.layerName,
256
- Type: c.type,
257
- Timestamp: new Date(c.timestamp || Date.now()).toISOString()
258
- }));
259
- console.table(tableData), isTelemetryEnabled && span && span.setStatus({ code: 0 });
310
+ const tableData = changes.map((c) => ({
311
+ "Element ID": c.elementId,
312
+ Layer: c.layerName,
313
+ Type: c.type,
314
+ Timestamp: new Date(c.timestamp || Date.now()).toISOString()
315
+ }));
316
+ console.table(tableData);
317
+ }
318
+ handleSuccess(`Listed ${changes.length} staged change(s)`, {
319
+ changesetId: activeChangeset,
320
+ changeCount: changes.length,
321
+ changes: changes.map((c) => ({
322
+ elementId: c.elementId,
323
+ layer: c.layerName,
324
+ operation: c.type,
325
+ timestamp: new Date(c.timestamp || Date.now()).toISOString()
326
+ }))
327
+ }), isTelemetryEnabled && span && span.setStatus({ code: 0 });
260
328
  } catch (error) {
261
329
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
262
330
  code: 2,
263
331
  message: getErrorMessage(error)
264
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
332
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
265
333
  } finally {
266
334
  endSpan(span);
267
335
  }
268
336
  }
269
- async function changesetExplicitStageCommand(elementId) {
337
+ async function changesetExplicitStageCommand(elementId, options = {}) {
270
338
  const span = isTelemetryEnabled ? startSpan("changeset.stage-explicit", { "stage.elementId": elementId }) : null;
271
339
  try {
272
- const model = await Model.load(process.cwd(), { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
340
+ const model = await Model.load(options.model, { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
273
341
  if (!activeChangesetId) {
274
- console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
342
+ isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: "No active changeset" })) : console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
275
343
  return;
276
344
  }
277
345
  const layerName = await findElementLayer(model, elementId);
278
- layerName || (console.error(ansis.red(`Error: Element '${elementId}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Element not found" }), endSpan(span), process.exit(1));
346
+ layerName || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Element '${elementId}' not found` })) : console.error(ansis.red(`Error: Element '${elementId}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Element not found" }), endSpan(span), process.exit(1));
279
347
  const element = (await model.getLayer(layerName))?.getElement(elementId);
280
- if (element || (console.error(ansis.red(`Error: Element '${elementId}' not found in layer '${layerName}'`)), endSpan(span), process.exit(1)), (await manager.load(activeChangesetId))?.changes.some((c) => c.elementId === elementId)) {
348
+ if (element || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Element '${elementId}' not found in layer '${layerName}'` })) : console.error(ansis.red(`Error: Element '${elementId}' not found in layer '${layerName}'`)), endSpan(span), process.exit(1)), (await manager.load(activeChangesetId))?.changes.some((c) => c.elementId === elementId)) {
281
349
  console.warn(ansis.yellow(`Warning: Element '${elementId}' is already staged in the active changeset. Skipping.`)), endSpan(span);
282
350
  return;
283
351
  }
@@ -286,63 +354,74 @@ async function changesetExplicitStageCommand(elementId) {
286
354
  elementId,
287
355
  layerName,
288
356
  after: element.toJSON()
289
- }), isTelemetryEnabled && span && (span.setAttribute("stage.layerName", layerName), span.setStatus({ code: 0 })), console.log(ansis.green(`\u2713 Staged element: ${ansis.bold(elementId)}`)), console.log(ansis.dim(` Layer: ${layerName} | Changeset: ${activeChangesetId}`));
357
+ }), isTelemetryEnabled && span && (span.setAttribute("stage.layerName", layerName), span.setStatus({ code: 0 })), handleSuccess(`Staged element: ${elementId}`, {
358
+ elementId,
359
+ layer: layerName,
360
+ changesetId: activeChangesetId,
361
+ operation: "add"
362
+ }, { verbose: !0 });
290
363
  } catch (error) {
291
- isTelemetryEnabled && span && (span.recordException(error), span.setStatus({ code: 2, message: getErrorMessage(error) })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
364
+ isTelemetryEnabled && span && (span.recordException(error), span.setStatus({ code: 2, message: getErrorMessage(error) })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
292
365
  } finally {
293
366
  endSpan(span);
294
367
  }
295
368
  }
296
- async function changesetUnstageCommand(elementId) {
369
+ async function changesetUnstageCommand(elementId, options = {}) {
297
370
  const span = isTelemetryEnabled ? startSpan("changeset.unstage", {
298
371
  "unstage.elementId": elementId
299
372
  }) : null;
300
373
  try {
301
- const model = await Model.load(process.cwd(), { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
374
+ const model = await Model.load(options.model, { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
302
375
  if (!activeChangesetId) {
303
- console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
376
+ isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: "No active changeset" })) : console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
304
377
  return;
305
378
  }
306
379
  const changeset = await manager.load(activeChangesetId);
307
- changeset || (console.error(ansis.red(`Error: Changeset '${activeChangesetId}' not found`)), process.exit(1));
380
+ changeset || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Changeset '${activeChangesetId}' not found` })) : console.error(ansis.red(`Error: Changeset '${activeChangesetId}' not found`)), process.exit(1));
308
381
  const initialCount = changeset.changes.length;
309
382
  await manager.unstage(activeChangesetId, elementId);
310
383
  const updated = await manager.load(activeChangesetId);
311
384
  if (updated && updated.changes.length === initialCount) {
312
- console.error(ansis.yellow(`Warning: Element '${elementId}' not found in staged changes`)), isTelemetryEnabled && span && (span.setAttribute("unstage.found", !1), span.setStatus({ code: 0 })), endSpan(span);
385
+ handleWarning(`Element '${elementId}' not found in staged changes`), isTelemetryEnabled && span && (span.setAttribute("unstage.found", !1), span.setStatus({ code: 0 })), endSpan(span);
313
386
  return;
314
387
  }
315
- isTelemetryEnabled && span && (span.setAttribute("unstage.found", !0), span.setAttribute("unstage.remainingChanges", updated?.getChangeCount() || 0)), console.log(ansis.green(`\u2713 Unstaged element: ${ansis.bold(elementId)}`)), console.log(ansis.dim(` Remaining staged changes: ${updated?.getChangeCount() || 0}`)), isTelemetryEnabled && span && span.setStatus({ code: 0 });
388
+ isTelemetryEnabled && span && (span.setAttribute("unstage.found", !0), span.setAttribute("unstage.remainingChanges", updated?.getChangeCount() || 0)), handleSuccess(`Unstaged element: ${elementId}`, {
389
+ changesetId: activeChangesetId,
390
+ elementId,
391
+ remainingChanges: updated?.getChangeCount() || 0
392
+ }, { verbose: !0 }), isTelemetryEnabled && span && span.setStatus({ code: 0 });
316
393
  } catch (error) {
317
394
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
318
395
  code: 2,
319
396
  message: getErrorMessage(error)
320
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
397
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
321
398
  } finally {
322
399
  endSpan(span);
323
400
  }
324
401
  }
325
- async function changesetDiscardCommand(elementId) {
402
+ async function changesetDiscardCommand(elementId, options = {}) {
326
403
  const span = isTelemetryEnabled ? startSpan("changeset.discard", {
327
404
  "discard.elementId": elementId,
328
405
  "discard.all": !elementId
329
406
  }) : null;
330
407
  try {
331
- const model = await Model.load(process.cwd(), { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
408
+ const model = await Model.load(options.model, { lazyLoad: !0 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
332
409
  if (!activeChangesetId) {
333
- console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
410
+ isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: "No active changeset" })) : console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
334
411
  return;
335
412
  }
336
413
  const changeset = await manager.load(activeChangesetId);
337
- if (changeset || (console.error(ansis.red(`Error: Changeset '${activeChangesetId}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1)), isTelemetryEnabled && span && span.setAttribute("discard.changeCount", changeset.changes.length), elementId) {
414
+ if (changeset || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Changeset '${activeChangesetId}' not found` })) : console.error(ansis.red(`Error: Changeset '${activeChangesetId}' not found`)), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset not found" }), endSpan(span), process.exit(1)), isTelemetryEnabled && span && span.setAttribute("discard.changeCount", changeset.changes.length), elementId) {
338
415
  const initialCount = changeset.changes.length;
339
416
  await manager.unstage(activeChangesetId, elementId);
340
417
  const updated = await manager.load(activeChangesetId);
341
418
  if (updated && updated.changes.length === initialCount) {
342
- console.error(ansis.yellow(`Warning: Element '${elementId}' not found in staged changes`)), isTelemetryEnabled && span && (span.setAttribute("discard.found", !1), span.setStatus({ code: 0 })), endSpan(span);
419
+ handleWarning(`Element '${elementId}' not found in staged changes`), isTelemetryEnabled && span && (span.setAttribute("discard.found", !1), span.setStatus({ code: 0 })), endSpan(span);
343
420
  return;
344
421
  }
345
- isTelemetryEnabled && span && span.setAttribute("discard.found", !0), console.log(ansis.green(`\u2713 Discarded changes for element: ${ansis.bold(elementId)}`));
422
+ isTelemetryEnabled && span && span.setAttribute("discard.found", !0), handleSuccess(`Discarded changes for element: ${elementId}`, {
423
+ elementId
424
+ }, { verbose: !0 });
346
425
  } else {
347
426
  const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
348
427
  if (isTelemetryEnabled && span && span.setAttribute("cli.interactive", isInteractive), isInteractive) {
@@ -350,19 +429,23 @@ async function changesetDiscardCommand(elementId) {
350
429
  message: `Discard all ${changeset.changes.length} staged changes? This cannot be undone.`
351
430
  });
352
431
  if (!confirmed || typeof confirmed != "boolean") {
353
- console.log(ansis.dim("Cancelled")), isTelemetryEnabled && span && (span.setAttribute("discard.cancelled", !0), span.setStatus({ code: 0 })), endSpan(span);
432
+ handleSuccess("Discard cancelled", {
433
+ status: "cancelled"
434
+ }), isTelemetryEnabled && span && (span.setAttribute("discard.cancelled", !0), span.setStatus({ code: 0 })), endSpan(span);
354
435
  return;
355
436
  }
356
437
  } else
357
438
  console.error(ansis.red("Error: Cannot confirm discard of all changes in non-interactive environment")), console.log(ansis.dim(" Specify an element ID to discard only that element")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Interactive confirmation required" }), endSpan(span), process.exit(1);
358
- await manager.discard(activeChangesetId), console.log(ansis.green("\u2713 Discarded all staged changes")), console.log(ansis.dim(" Changeset status: discarded"));
439
+ await manager.discard(activeChangesetId), handleSuccess("Discarded all staged changes", {
440
+ changesetStatus: "discarded"
441
+ }, { verbose: !0 });
359
442
  }
360
- isTelemetryEnabled && span && span.setStatus({ code: 0 }), console.log();
443
+ isTelemetryEnabled && span && span.setStatus({ code: 0 }), isJson() || console.log();
361
444
  } catch (error) {
362
445
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
363
446
  code: 2,
364
447
  message: getErrorMessage(error)
365
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
448
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
366
449
  } finally {
367
450
  endSpan(span);
368
451
  }
@@ -372,7 +455,7 @@ async function changesetPreviewCommand(options) {
372
455
  "preview.layer": options.layer
373
456
  }) : null;
374
457
  try {
375
- const model = await Model.load(process.cwd(), { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
458
+ const model = await Model.load(options.model, { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
376
459
  if (!activeChangesetId) {
377
460
  console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
378
461
  return;
@@ -425,7 +508,7 @@ async function changesetDiffCommand(options) {
425
508
  "diff.layer": options.layer
426
509
  }) : null;
427
510
  try {
428
- const model = await Model.load(process.cwd(), { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
511
+ const model = await Model.load(options.model, { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await manager.getActiveId();
429
512
  if (!activeChangesetId) {
430
513
  console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "No active changeset" }), endSpan(span);
431
514
  return;
@@ -463,48 +546,52 @@ Diff: Base Model vs Staged Changes
463
546
  async function changesetCommitCommand(options) {
464
547
  await startActiveSpan("changeset.commit", async (span) => {
465
548
  try {
466
- const model = await Model.load(process.cwd(), { lazyLoad: !1 }), stagingManager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await stagingManager.getActiveId();
549
+ const model = await Model.load(options?.model, { lazyLoad: !1 }), stagingManager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await stagingManager.getActiveId();
467
550
  if (!activeChangesetId) {
468
- console.error(ansis.red("Error: No active changeset")), isTelemetryEnabled && span.setStatus({ code: 2, message: "No active changeset" });
551
+ 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" });
469
552
  return;
470
553
  }
471
554
  const changeset = await stagingManager.load(activeChangesetId);
472
- changeset || (console.error(ansis.red(`Error: Changeset '${activeChangesetId}' not found`)), isTelemetryEnabled && span.setStatus({ code: 2, message: "Changeset not found" }), span.end(), process.exit(1));
555
+ changeset || (isJson() ? console.log(JSON.stringify({ status: "error", code: 2, message: `Changeset '${activeChangesetId}' not found` })) : console.error(ansis.red(`Error: Changeset '${activeChangesetId}' not found`)), isTelemetryEnabled && span.setStatus({ code: 2, message: "Changeset not found" }), span.end(), process.exit(1));
473
556
  const changeCount = changeset.changes.length;
474
557
  if (isTelemetryEnabled && (span.setAttribute("changeset.id", activeChangesetId), span.setAttribute("changeset.name", changeset.name), span.setAttribute("changeset.changeCount", changeCount)), changeCount === 0) {
475
- console.log(ansis.yellow("No staged changes to commit")), isTelemetryEnabled && span.setStatus({ code: 0 });
558
+ handleSuccess("No staged changes to commit", {
559
+ name: changeset.name,
560
+ changesetId: activeChangesetId,
561
+ stagedChanges: 0
562
+ }, { verbose: !0 }), isTelemetryEnabled && span.setStatus({ code: 0 });
476
563
  return;
477
564
  }
478
- console.log(ansis.bold(`
479
- Committing changeset: ${ansis.cyan(changeset.name)}`)), console.log(ansis.dim(`Staged changes: ${changeCount}`)), console.log();
565
+ isJson() || (console.log(ansis.bold(`
566
+ Committing changeset: ${ansis.cyan(changeset.name)}`)), console.log(ansis.dim(`Staged changes: ${changeCount}`)), console.log());
480
567
  try {
481
568
  const result = await stagingManager.commit(model, activeChangesetId, {
482
569
  validate: options?.validate !== !1,
483
570
  force: options?.force === !0
484
571
  });
485
- if (await stagingManager.clearActive(), console.log(ansis.green(`\u2713 Committed ${result.committed} change(s)`)), (result.skipped ?? 0) > 0) {
486
- const skipped = result.skipped, details = result.skippedDetails ?? [];
487
- console.log(ansis.yellow(`\u26A0 Skipped ${skipped} duplicate element(s):`));
488
- const shown = details.slice(0, 10);
489
- for (const detail of shown)
490
- console.log(ansis.dim(` ${detail}`));
491
- details.length > 10 && console.log(ansis.dim(` ... and ${details.length - 10} more`));
492
- }
493
- isTelemetryEnabled && (span.setAttribute("commit.committed", result.committed), span.setAttribute("commit.skipped", result.skipped), span.setAttribute("commit.failed", result.failed), span.setAttribute("commit.validationPassed", result.validation.passed));
572
+ await stagingManager.clearActive(), isTelemetryEnabled && (span.setAttribute("commit.committed", result.committed), span.setAttribute("commit.skipped", result.skipped), span.setAttribute("commit.failed", result.failed), span.setAttribute("commit.validationPassed", result.validation.passed));
573
+ const commitDetails = {
574
+ name: changeset.name,
575
+ changesetId: activeChangesetId,
576
+ committedCount: result.committed,
577
+ stagedChanges: changeCount,
578
+ validationPassed: result.validation.passed
579
+ };
580
+ reportCommitValidation(result, commitDetails), (result.skipped ?? 0) > 0 && (commitDetails.skipped = result.skipped, commitDetails.skippedDetails = (result.skippedDetails ?? []).slice(0, 10)), result.failed > 0 && (commitDetails.failed = result.failed);
494
581
  const accountedFor = result.committed + (result.skipped ?? 0) + result.failed;
495
582
  if (accountedFor < changeCount) {
496
583
  const unaccounted = changeCount - accountedFor;
497
- 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.`));
584
+ 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.`));
498
585
  }
499
- result.driftWarning && (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 }), console.log();
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();
500
587
  } catch (error) {
501
- throw console.log(ansis.red(`\u2717 Commit failed and rolled back: ${getErrorMessage(error)}`)), isTelemetryEnabled && span.setAttribute("commit.rolledBack", !0), error;
588
+ 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;
502
589
  }
503
590
  } catch (error) {
504
591
  isTelemetryEnabled && (span.recordException(error), span.setStatus({
505
592
  code: 2,
506
593
  message: getErrorMessage(error)
507
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span.end(), process.exit(1);
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);
508
595
  }
509
596
  }, {
510
597
  "commit.validate": options?.validate !== !1,
@@ -517,13 +604,17 @@ async function changesetExportCommand(changesetId, options) {
517
604
  "export.format": format
518
605
  }) : null;
519
606
  try {
520
- const model = await Model.load(process.cwd(), { lazyLoad: !0 }), exporter = new ChangesetExporter(model.rootPath), ext = format === "patch" ? "patch" : format, outputPath = options.output || `${changesetId}.${ext}`, absolutePath = path.isAbsolute(outputPath) ? outputPath : path.join(process.cwd(), outputPath);
521
- isTelemetryEnabled && span && span.setAttribute("export.outputPath", outputPath), await exporter.exportToFile(changesetId, absolutePath, format), console.log(ansis.green(`\u2713 Exported changeset to ${ansis.cyan(outputPath)}`)), console.log(ansis.dim(` Format: ${format}`)), isTelemetryEnabled && span && span.setStatus({ code: 0 });
607
+ const model = await Model.load(options.model, { lazyLoad: !0 }), exporter = new ChangesetExporter(model.rootPath), ext = format === "patch" ? "patch" : format, outputPath = options.output || `${changesetId}.${ext}`, absolutePath = path.isAbsolute(outputPath) ? outputPath : path.join(process.cwd(), outputPath);
608
+ isTelemetryEnabled && span && span.setAttribute("export.outputPath", outputPath), await exporter.exportToFile(changesetId, absolutePath, format), handleSuccess(`Exported changeset to ${outputPath}`, {
609
+ changesetId,
610
+ outputPath,
611
+ format
612
+ }, { verbose: !0 }), isTelemetryEnabled && span && span.setStatus({ code: 0 });
522
613
  } catch (error) {
523
614
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
524
615
  code: 2,
525
616
  message: getErrorMessage(error)
526
- })), console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
617
+ })), isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: getErrorMessage(error) })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), endSpan(span), process.exit(1);
527
618
  } finally {
528
619
  endSpan(span);
529
620
  }
@@ -534,23 +625,35 @@ async function changesetImportCommand(file, options = {}) {
534
625
  "import.force": options.force === !0
535
626
  }) : null;
536
627
  try {
537
- const model = await Model.load(process.cwd(), { lazyLoad: !1 }), exporter = new ChangesetExporter(model.rootPath), absolutePath = path.isAbsolute(file) ? file : path.join(process.cwd(), file), imported = await exporter.importFromFile(absolutePath);
628
+ const model = await Model.load(options.model, { lazyLoad: !1 }), exporter = new ChangesetExporter(model.rootPath), absolutePath = path.isAbsolute(file) ? file : path.join(process.cwd(), file), imported = await exporter.importFromFile(absolutePath);
538
629
  isTelemetryEnabled && span && (span.setAttribute("changeset.name", imported.name), span.setAttribute("changeset.changeCount", imported.changes.length));
539
630
  const compatibility = await exporter.validateCompatibility(imported, model);
540
631
  if (isTelemetryEnabled && span && (span.setAttribute("import.compatible", compatibility.compatible), span.setAttribute("import.baseSnapshotMatch", compatibility.baseSnapshotMatch)), !compatibility.compatible) {
541
- console.error(ansis.red("\u2717 Import failed: Changeset is incompatible")), console.error(ansis.dim(" Issues:"));
542
- for (const warning of compatibility.warnings)
543
- console.error(ansis.dim(` - ${warning}`));
632
+ if (isJson())
633
+ console.log(JSON.stringify({ status: "error", code: 1, message: "Changeset is incompatible", warnings: compatibility.warnings }));
634
+ else {
635
+ console.error(ansis.red("\u2717 Import failed: Changeset is incompatible")), console.error(ansis.dim(" Issues:"));
636
+ for (const warning of compatibility.warnings)
637
+ console.error(ansis.dim(` - ${warning}`));
638
+ }
544
639
  isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Changeset incompatible" }), endSpan(span), process.exit(1);
545
640
  }
546
- compatibility.baseSnapshotMatch || (options.force || (console.error(ansis.red("Error: Imported changeset has base model drift") + `
641
+ compatibility.baseSnapshotMatch || (options.force || (isJson() ? console.log(JSON.stringify({ status: "error", code: 1, message: "Imported changeset has base model drift" })) : console.error(ansis.red("Error: Imported changeset has base model drift") + `
547
642
  ` + ansis.dim("The model has been modified since this changeset was created.") + `
548
643
  ` + ansis.dim("This may cause conflicts or unexpected behavior when committing.") + `
549
644
 
550
645
  ` + ansis.dim("To import anyway, use: --force")), isTelemetryEnabled && span && span.setStatus({ code: 2, message: "Drift detected, force required" }), endSpan(span), process.exit(1)), console.warn(ansis.yellow("\u26A0 Warning: Base model drift detected (--force used)") + `
551
646
  ` + ansis.dim("Review changes carefully before committing.")), console.log(), isTelemetryEnabled && span && span.setAttribute("import.driftOverridden", !0));
552
647
  const newId = generateImportedChangesetId();
553
- imported.id = newId, await new StagedChangesetStorage(model.rootPath).save(imported), console.log(ansis.green(`\u2713 Imported changeset: ${ansis.cyan(imported.name)}`)), console.log(ansis.dim(` ID: ${newId}`)), console.log(ansis.dim(` Changes: +${imported.stats?.additions || 0} ~${imported.stats?.modifications || 0} -${imported.stats?.deletions || 0}`)), compatibility.baseSnapshotMatch || console.log(ansis.yellow(" \u26A0 Base model drift detected - review before committing")), isTelemetryEnabled && span && (span.setAttribute("changeset.id", newId), span.setStatus({ code: 0 })), console.log();
648
+ imported.id = newId, await new StagedChangesetStorage(model.rootPath).save(imported);
649
+ const importDetails = {
650
+ changesetName: imported.name,
651
+ changesetId: newId,
652
+ additions: imported.stats?.additions || 0,
653
+ modifications: imported.stats?.modifications || 0,
654
+ deletions: imported.stats?.deletions || 0
655
+ };
656
+ compatibility.baseSnapshotMatch || (importDetails.driftDetected = !0), handleSuccess(`Imported changeset: ${imported.name}`, importDetails, { verbose: !0 }), isTelemetryEnabled && span && (span.setAttribute("changeset.id", newId), span.setStatus({ code: 0 }));
554
657
  } catch (error) {
555
658
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({
556
659
  code: 2,
@@ -562,37 +665,37 @@ async function changesetImportCommand(file, options = {}) {
562
665
  }
563
666
  function changesetCommands(program) {
564
667
  const changesetGroup = program.command("changeset").description("Manage changesets");
565
- changesetGroup.command("create <name>").description("Create a new changeset").option("--description <desc>", "Changeset description").addHelpText("after", `
668
+ changesetGroup.command("create <name>").description("Create a new changeset").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("--description <desc>", "Changeset description").addHelpText("after", `
566
669
  Examples:
567
670
  $ dr changeset create "v1.1 migration"
568
671
  $ dr changeset create "api-refactoring" --description "Refactor API layer endpoints"`).action(async (name, options) => {
569
672
  await changesetCreateCommand(name, options);
570
- }), changesetGroup.command("list").description("List all changesets").addHelpText("after", `
673
+ }), changesetGroup.command("list").description("List all changesets").option("--model <path>", "Path to model root (contains model/manifest.yaml)").addHelpText("after", `
571
674
  Examples:
572
- $ dr changeset list`).action(async () => {
573
- await changesetListCommand();
574
- }), changesetGroup.command("apply <name>").description("Apply a changeset to the model").option("--no-validate", "Skip validation before applying").option("--force", "Force apply even if base model has drifted").addHelpText("after", `
675
+ $ dr changeset list`).action(async (options) => {
676
+ await changesetListCommand(options);
677
+ }), changesetGroup.command("apply <name>").description("Apply a changeset to the model").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("--no-validate", "Skip validation before applying").option("--force", "Force apply even if base model has drifted").addHelpText("after", `
575
678
  Examples:
576
679
  $ dr changeset apply "v1.1 migration"
577
680
  $ dr changeset apply "v1.1 migration" --no-validate
578
681
  $ dr changeset apply "v1.1 migration" --force`).action(async (name, options) => {
579
682
  await changesetApplyCommand(name, options);
580
- }), changesetGroup.command("revert <name>").description("Revert a changeset from the model").addHelpText("after", `
683
+ }), changesetGroup.command("revert <name>").description("Revert a changeset from the model").option("--model <path>", "Path to model root (contains model/manifest.yaml)").addHelpText("after", `
581
684
  Examples:
582
- $ dr changeset revert "v1.1 migration"`).action(async (name) => {
583
- await changesetRevertCommand(name);
584
- }), changesetGroup.command("activate <name>").description("Activate a changeset for automatic change tracking").addHelpText("after", `
685
+ $ dr changeset revert "v1.1 migration"`).action(async (name, options) => {
686
+ await changesetRevertCommand(name, options);
687
+ }), changesetGroup.command("activate <name>").description("Activate a changeset for automatic change tracking").option("--model <path>", "Path to model root (contains model/manifest.yaml)").addHelpText("after", `
585
688
  Examples:
586
- $ dr changeset activate "v1.1 migration"`).action(async (name) => {
587
- await changesetActivateCommand(name);
588
- }), changesetGroup.command("deactivate").description("Deactivate the currently active changeset").addHelpText("after", `
689
+ $ dr changeset activate "v1.1 migration"`).action(async (name, options) => {
690
+ await changesetActivateCommand(name, options);
691
+ }), changesetGroup.command("deactivate").description("Deactivate the currently active changeset").option("--model <path>", "Path to model root (contains model/manifest.yaml)").addHelpText("after", `
589
692
  Examples:
590
- $ dr changeset deactivate`).action(async () => {
591
- await changesetDeactivateCommand();
592
- }), changesetGroup.command("status").description("Show the currently active changeset").addHelpText("after", `
693
+ $ dr changeset deactivate`).action(async (options) => {
694
+ await changesetDeactivateCommand(options);
695
+ }), changesetGroup.command("status").description("Show the currently active changeset").option("--model <path>", "Path to model root (contains model/manifest.yaml)").addHelpText("after", `
593
696
  Examples:
594
- $ dr changeset status`).action(async () => {
595
- await changesetStatusCommand();
697
+ $ dr changeset status`).action(async (options) => {
698
+ await changesetStatusCommand(options);
596
699
  }), changesetGroup.command("show [name]").description("Show changeset details (use: status, staged, diff, or preview)").addHelpText("after", `
597
700
  To inspect a changeset, use one of:
598
701
  $ dr changeset status # Show the active changeset
@@ -607,59 +710,70 @@ To inspect a changeset, use one of:
607
710
  dr changeset staged \u2014 list all staged changes
608
711
  dr changeset diff \u2014 show a diff of staged changes
609
712
  dr changeset preview \u2014 preview what will be committed`), process.exit(1);
610
- }), changesetGroup.command("delete <name>").description("Delete a changeset permanently").option("-f, --force", "Skip confirmation prompt").addHelpText("after", `
713
+ }), changesetGroup.command("delete <name>").description("Delete a changeset permanently").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("-f, --force", "Skip confirmation prompt").addHelpText("after", `
611
714
  Examples:
612
715
  $ dr changeset delete my-feature
613
716
  $ dr changeset delete my-feature --force`).action(async (name, options) => {
614
717
  await changesetDeleteCommand(name, options);
615
- }), changesetGroup.command("staged").description("List all staged changes in the active changeset").option("-l, --layer <layer>", "Filter by layer name").addHelpText("after", `
718
+ }), changesetGroup.command("staged").description("List all staged changes in the active changeset").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("-l, --layer <layer>", "Filter by layer name").addHelpText("after", `
616
719
  Examples:
617
720
  $ dr changeset staged
618
721
  $ dr changeset staged --layer api`).action(async (options) => {
619
722
  await changesetStagedCommand(options);
620
- }), changesetGroup.command("stage <element-id>").description("Stage a specific element into the active changeset").addHelpText("after", `
723
+ }), changesetGroup.command("stage <element-id>").description("Stage a specific element into the active changeset").option("--model <path>", "Path to model root (contains model/manifest.yaml)").addHelpText("after", `
621
724
  Examples:
622
725
  $ dr changeset stage api.operation.get-users
623
- $ dr changeset stage ux.uicomponent.login-form`).action(async (elementId) => {
624
- await changesetExplicitStageCommand(elementId);
625
- }), changesetGroup.command("unstage <element-id>").description("Remove specific element from staging area").addHelpText("after", `
726
+ $ dr changeset stage ux.uicomponent.login-form`).action(async (elementId, options) => {
727
+ await changesetExplicitStageCommand(elementId, options);
728
+ }), changesetGroup.command("unstage <element-id>").description("Remove specific element from staging area").option("--model <path>", "Path to model root (contains model/manifest.yaml)").addHelpText("after", `
626
729
  Examples:
627
- $ dr changeset unstage api-endpoint-create-customer`).action(async (elementId) => {
628
- await changesetUnstageCommand(elementId);
629
- }), changesetGroup.command("discard [element-id]").description("Discard all or single staged changes").addHelpText("after", `
730
+ $ dr changeset unstage api-endpoint-create-customer`).action(async (elementId, options) => {
731
+ await changesetUnstageCommand(elementId, options);
732
+ }), changesetGroup.command("discard [element-id]").description("Discard all or single staged changes").option("--model <path>", "Path to model root (contains model/manifest.yaml)").addHelpText("after", `
630
733
  Examples:
631
734
  $ dr changeset discard
632
- $ dr changeset discard api-endpoint-create-customer`).action(async (elementId) => {
633
- await changesetDiscardCommand(elementId);
634
- }), changesetGroup.command("preview").description("Preview the merged model state with staged changes applied").option("-l, --layer <layer>", "Preview specific layer only").addHelpText("after", `
735
+ $ dr changeset discard api-endpoint-create-customer`).action(async (elementId, options) => {
736
+ await changesetDiscardCommand(elementId, options);
737
+ }), changesetGroup.command("preview").description("Preview the merged model state with staged changes applied").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("-l, --layer <layer>", "Preview specific layer only").addHelpText("after", `
635
738
  Examples:
636
739
  $ dr changeset preview
637
740
  $ dr changeset preview --layer application`).action(async (options) => {
638
741
  await changesetPreviewCommand(options);
639
- }), changesetGroup.command("diff").description("Show delta between base model and staged changes").option("-l, --layer <layer>", "Show diff for specific layer only").addHelpText("after", `
742
+ }), changesetGroup.command("diff").description("Show delta between base model and staged changes").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("-l, --layer <layer>", "Show diff for specific layer only").addHelpText("after", `
640
743
  Examples:
641
744
  $ dr changeset diff
642
745
  $ dr changeset diff --layer api`).action(async (options) => {
643
746
  await changesetDiffCommand(options);
644
- }), changesetGroup.command("commit").description("Apply staged changes to the base model").option("--validate", "Run validation before commit (default: true)", !0).option("--force", "Commit despite drift warnings", !1).addHelpText("after", `
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", `
748
+ Validation compares the model with and without the changeset. Errors the
749
+ changeset introduces block the commit; errors the base model already had are
750
+ not counted against it, and the output says how many there were.
751
+
645
752
  Examples:
646
753
  $ dr changeset commit
647
- $ dr changeset commit --validate
648
- $ dr changeset commit --force`).action(async (options) => {
754
+ $ dr changeset commit --force # commit despite drift
755
+ $ dr changeset commit --no-validate # skip validation entirely`).action(async (options) => {
649
756
  await changesetCommitCommand(options);
650
- }), changesetGroup.command("export <changeset-id>").description("Export changeset to portable file").option("-o, --output <file>", "Output file path").option("-f, --format <format>", "Export format (yaml|json|patch)", "yaml").addHelpText("after", `
757
+ }), 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", `
651
758
  Examples:
652
759
  $ dr changeset export api-updates
653
760
  $ dr changeset export api-updates --output changes.yaml
654
761
  $ dr changeset export api-updates --format json --output changes.json
655
762
  $ dr changeset export api-updates --format patch --output changes.patch`).action(async (changesetId, options) => {
656
763
  await changesetExportCommand(changesetId, options);
657
- }), changesetGroup.command("import <file>").description("Import changeset from file").option("-f, --force", "Import despite base model drift", !1).addHelpText("after", `
764
+ }), changesetGroup.command("import <file>").description("Import changeset from file").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("-f, --force", "Import despite base model drift", !1).addHelpText("after", `
658
765
  Examples:
659
766
  $ dr changeset import changes.yaml
660
767
  $ dr changeset import ../team-changes.json
661
768
  $ dr changeset import changes.yaml --force`).action(async (file, options) => {
662
769
  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", `
771
+ The planned changeset defaults to the active one.
772
+
773
+ Examples:
774
+ $ dr changeset verify export-orders --against export-orders-as-built
775
+ $ dr changeset verify export-orders --base --json`).action(async (planned, options) => {
776
+ await changesetVerifyCommand(planned, options);
663
777
  }), changesetGroup.showSuggestionAfterError();
664
778
  }
665
779
  export {