@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
@@ -3,12 +3,17 @@ import { Model } from "../core/model.js";
3
3
  import { StagingAreaManager } from "../core/staging-area.js";
4
4
  import { findElementLayer } from "../utils/element-utils.js";
5
5
  import { isValidLayerName } from "../core/layers.js";
6
- import { CLIError, ErrorCategory, handleError, getErrorMessage } from "../utils/errors.js";
6
+ import { CLIError, ErrorCategory, handleError, handleSuccess, handleInfo, getErrorMessage } from "../utils/errors.js";
7
7
  import { getValidRelationships, getValidPredicatesForSource, getValidDestinationsForSourceAndPredicate } from "../generated/relationship-index.js";
8
8
  import { normalizeNodeType } from "../generated/node-types.js";
9
9
  import { ModelReportOrchestrator } from "../reports/model-report-orchestrator.js";
10
10
  import { emitLog, SeverityNumber } from "../telemetry/index.js";
11
+ import { elementIdentifiers } from "../core/relationships.js";
11
12
  import { RelationshipCatalog } from "../core/relationship-catalog.js";
13
+ import { collectKnownIdentifiers, findDanglingRelationships } from "../core/relationship-prune.js";
14
+ import { isJson } from "../utils/globals.js";
15
+ import { ChangesetGraph } from "../core/changeset-graph.js";
16
+ import { liveRelationshipsBetween, planRelationshipAdd, stageRelationshipDeletes } from "../core/relationship-staging.js";
12
17
  function constructSpecNodeId(element, elementId) {
13
18
  const layerId = element.layer_id, type = element.type;
14
19
  if (typeof layerId != "string" || !layerId)
@@ -39,6 +44,13 @@ function getRelationshipConstraints(sourceSpecNodeId, predicate, destSpecNodeId)
39
44
  };
40
45
  }
41
46
  async function addRelationshipHandler(model, source, target, predicate, properties, preResolved) {
47
+ return await addRelationship(model, source, target, predicate, properties, preResolved) !== "duplicate";
48
+ }
49
+ async function activeStagedChangeset(manager) {
50
+ const active = await manager.getActive();
51
+ return active && active.status === "staged" ? active : null;
52
+ }
53
+ async function addRelationship(model, source, target, predicate, properties, preResolved) {
42
54
  let resolvedSourceLayerName = preResolved?.source?.layerName, resolvedSourceElement = preResolved?.source?.element, resolvedTargetLayerName = preResolved?.target?.layerName, resolvedTargetElement = preResolved?.target?.element;
43
55
  if (!resolvedSourceLayerName && (resolvedSourceLayerName = await findElementLayer(model, source), !resolvedSourceLayerName))
44
56
  throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);
@@ -68,9 +80,19 @@ async function addRelationshipHandler(model, source, target, predicate, properti
68
80
  if (relType && !relType.semantics.reflexivity && source === target)
69
81
  throw new CLIError(`Invalid relationship: predicate "${predicate}" does not allow reflexive relationships (${source})`, ErrorCategory.USER);
70
82
  }
71
- if (model.relationships.find(source, target, predicate).length > 0)
72
- return console.warn(ansis.yellow(`Warning: Relationship ${source} --[${predicate}]--> ${target} already exists. Skipping.`)), !1;
73
- const stagingManager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await stagingManager.getActiveId(), relData = {
83
+ const stagingManager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await stagingManager.getActiveId(), activeChangeset = activeChangesetId ? await activeStagedChangeset(stagingManager) : null;
84
+ if (activeChangeset) {
85
+ const graph = await ChangesetGraph.build(model, activeChangeset), plan = planRelationshipAdd(graph, activeChangeset, { source, predicate, target });
86
+ if (plan.action === "duplicate")
87
+ return "duplicate";
88
+ if (plan.action === "restore") {
89
+ for (const key of plan.keys)
90
+ await stagingManager.unstage(activeChangeset.id, key);
91
+ return "restored";
92
+ }
93
+ } else if (model.relationships.find(source, target, predicate).length > 0)
94
+ return "duplicate";
95
+ const relData = {
74
96
  source,
75
97
  target,
76
98
  predicate,
@@ -109,15 +131,35 @@ async function addRelationshipHandler(model, source, target, predicate, properti
109
131
  });
110
132
  }
111
133
  }
112
- return !0;
134
+ return activeChangesetId ? "staged" : "added";
135
+ }
136
+ async function findRelationshipsByEndpoints(model, source, target, predicate) {
137
+ const sourceIds = new Set(elementIdentifiers(model.getElementById(source) ?? {}, source)), targetIds = new Set(elementIdentifiers(model.getElementById(target) ?? {}, target));
138
+ return model.relationships.getAll().filter((rel) => sourceIds.has(rel.source) && targetIds.has(rel.target) && (!predicate || rel.predicate === predicate));
113
139
  }
114
- async function deleteRelationshipHandler(model, source, target, predicate, preResolved) {
140
+ function noMatchingRelationshipHints(surface, source, inChangeset) {
141
+ return surface === "mcp" ? [
142
+ `Use "model_show" on ${source} to see its relationships`,
143
+ inChangeset ? 'Use "changeset_brief" to see what this changeset already stages' : 'Use "model_search" to check the source and target element IDs'
144
+ ] : [
145
+ `Use "dr relationship list ${source}" to see the relationships recorded for ${source}`,
146
+ inChangeset ? 'Use "dr changeset staged" to see what the active changeset already stages' : 'Use "dr relationship prune --dry-run" to list rows whose endpoints no longer resolve'
147
+ ];
148
+ }
149
+ async function deleteRelationshipHandler(model, source, target, predicate, preResolved, surface = "cli") {
150
+ const changesetManager = new StagingAreaManager(model.rootPath, model), changeset = await changesetManager.getActiveId() ? await activeStagedChangeset(changesetManager) : null;
151
+ if (changeset) {
152
+ const graph = await ChangesetGraph.build(model, changeset), matches = liveRelationshipsBetween(graph, source, target, predicate);
153
+ if (matches.length === 0)
154
+ throw new CLIError("No matching relationships found", ErrorCategory.USER, noMatchingRelationshipHints(surface, source, !0));
155
+ const staged = await stageRelationshipDeletes(changesetManager, graph, changeset, matches);
156
+ return { deletedCount: staged.length, staged };
157
+ }
115
158
  let resolvedSourceLayerName = preResolved?.sourceLayerName;
116
- if (!resolvedSourceLayerName && (resolvedSourceLayerName = await findElementLayer(model, source), !resolvedSourceLayerName))
117
- throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);
159
+ resolvedSourceLayerName || (resolvedSourceLayerName = await findElementLayer(model, source));
118
160
  let resolvedToDelete = preResolved?.toDelete;
119
- if (resolvedToDelete || (resolvedToDelete = model.relationships.find(source, target, predicate)), resolvedToDelete.length === 0)
120
- throw new CLIError("No matching relationships found", ErrorCategory.USER);
161
+ if (resolvedToDelete || (resolvedToDelete = await findRelationshipsByEndpoints(model, source, target, predicate)), resolvedToDelete.length === 0)
162
+ throw new CLIError("No matching relationships found", ErrorCategory.USER, noMatchingRelationshipHints(surface, source, !1));
121
163
  const stagingManager = new StagingAreaManager(model.rootPath, model), activeChangesetId = await stagingManager.getActiveId();
122
164
  if (activeChangesetId)
123
165
  for (const rel of resolvedToDelete) {
@@ -137,7 +179,9 @@ async function deleteRelationshipHandler(model, source, target, predicate, preRe
137
179
  });
138
180
  }
139
181
  else {
140
- model.relationships.delete(source, target, predicate), await model.saveRelationships(), await model.saveManifest();
182
+ for (const rel of resolvedToDelete)
183
+ model.relationships.delete(rel.source, rel.target, rel.predicate);
184
+ await model.saveRelationships(), await model.saveManifest();
141
185
  try {
142
186
  const orchestrator = new ModelReportOrchestrator(model, model.rootPath), affectedLayers = /* @__PURE__ */ new Set();
143
187
  for (const rel of resolvedToDelete) {
@@ -161,15 +205,115 @@ async function deleteRelationshipHandler(model, source, target, predicate, preRe
161
205
  }
162
206
  return { deletedCount: resolvedToDelete.length };
163
207
  }
164
- function relationshipCommands(program) {
165
- program.command("add <source> <target>").description("Add a relationship between elements (intra-layer or cross-layer)").requiredOption("--predicate <predicate>", "Relationship predicate (e.g., depends-on, realizes, exposes)").option("--properties <json>", "Relationship properties (JSON)").addHelpText("after", `
208
+ async function pruneRelationshipsCommand(options) {
209
+ const format = options.format ?? "text";
210
+ if (format !== "text" && format !== "json")
211
+ throw new CLIError(`Invalid format: ${format}`, ErrorCategory.USER, [
212
+ "Use --format text or --format json"
213
+ ]);
214
+ const jsonOutput = format === "json" || isJson(), layers = options.layers?.length ? options.layers : void 0;
215
+ if (layers) {
216
+ const invalid = layers.filter((l) => !isValidLayerName(l));
217
+ if (invalid.length > 0)
218
+ throw new CLIError(`Invalid layer name(s): ${invalid.join(", ")}`, ErrorCategory.USER, ['Use canonical layer names, e.g. "api", "data-model", "data-store"']);
219
+ }
220
+ const model = await Model.load(options.model || process.cwd()), elements = [];
221
+ for (const layerName of model.getLayerNames()) {
222
+ const layer = await model.getLayer(layerName);
223
+ layer && elements.push(...layer.listElements());
224
+ }
225
+ const known = collectKnownIdentifiers(elements), analysis = findDanglingRelationships(model.relationships.getAll(), known, { layers }), stagingManager = new StagingAreaManager(model.rootPath, model), activeChangeset = await stagingManager.getActive(), staging = !!activeChangeset && activeChangeset.status === "staged", result = {
226
+ scanned: analysis.scanned,
227
+ dangling: analysis.dangling.length,
228
+ removed: 0,
229
+ dryRun: !!options.dryRun,
230
+ staged: !1,
231
+ ...staging ? { changeset: activeChangeset.name } : {},
232
+ ...layers ? { layers } : {},
233
+ relationships: analysis.dangling.map(({ relationship: rel, unresolved }) => ({
234
+ source: rel.source,
235
+ predicate: rel.predicate,
236
+ target: rel.target,
237
+ layer: rel.layer,
238
+ ...rel.targetLayer ? { targetLayer: rel.targetLayer } : {},
239
+ unresolved
240
+ }))
241
+ };
242
+ if (!options.dryRun && analysis.dangling.length > 0) {
243
+ if (staging) {
244
+ for (const { relationship: rel } of analysis.dangling)
245
+ await stagingManager.stage(activeChangeset.id, {
246
+ type: "relationship-delete",
247
+ elementId: `${rel.source}::${rel.predicate}::${rel.target}`,
248
+ layerName: rel.layer,
249
+ before: {
250
+ source: rel.source,
251
+ target: rel.target,
252
+ predicate: rel.predicate,
253
+ layer: rel.layer,
254
+ ...rel.targetLayer ? { targetLayer: rel.targetLayer } : {},
255
+ ...rel.properties ? { properties: rel.properties } : {}
256
+ }
257
+ });
258
+ result.staged = !0;
259
+ } else {
260
+ const danglingRows = new Set(analysis.dangling.map((d) => d.relationship));
261
+ model.relationships.retain((rel) => !danglingRows.has(rel)), await model.saveRelationships({ preserveHeader: !0 }), await model.saveManifest();
262
+ try {
263
+ const orchestrator = new ModelReportOrchestrator(model, model.rootPath), affectedLayers = /* @__PURE__ */ new Set();
264
+ for (const { relationship: rel } of analysis.dangling)
265
+ for (const name of [rel.layer, rel.targetLayer])
266
+ if (name && isValidLayerName(name))
267
+ for (const affected of orchestrator.computeAffectedLayers(name))
268
+ affectedLayers.add(affected);
269
+ await orchestrator.regenerate(affectedLayers);
270
+ } catch (error) {
271
+ emitLog(SeverityNumber.WARN, "Failed to regenerate layer reports after relationship prune", {
272
+ "relationship.pruned": analysis.dangling.length,
273
+ "error.message": getErrorMessage(error)
274
+ });
275
+ }
276
+ }
277
+ result.removed = analysis.dangling.length;
278
+ }
279
+ if (jsonOutput)
280
+ return console.log(JSON.stringify({ status: "ok", ...result }, null, 2)), result;
281
+ const scope = layers ? ` in layer(s) ${layers.join(", ")}` : "";
282
+ handleInfo(`Scanned ${result.scanned} relationship(s)${scope}: ${result.dangling} dangling`);
283
+ for (const row of result.relationships) {
284
+ const side = row.unresolved === "both" ? "source and target not found" : `${row.unresolved} not found`;
285
+ handleInfo(` - ${ansis.yellow(row.source)} --[${ansis.magenta(row.predicate)}]--> ${ansis.yellow(row.target)} ${ansis.dim(`(${side})`)}`);
286
+ }
287
+ return result.dangling === 0 ? handleSuccess("No dangling relationships found", {
288
+ scanned: result.scanned,
289
+ dangling: 0,
290
+ removed: 0
291
+ }) : result.dryRun ? handleSuccess(`Dry run: would remove ${result.dangling} dangling relationship(s)`, {
292
+ scanned: result.scanned,
293
+ dangling: result.dangling,
294
+ removed: 0,
295
+ dryRun: !0
296
+ }) : result.staged ? handleSuccess(`Staged removal of ${result.removed} dangling relationship(s) to ${ansis.bold(result.changeset)}`, {
297
+ scanned: result.scanned,
298
+ dangling: result.dangling,
299
+ removed: result.removed,
300
+ changesetStatus: "staged",
301
+ changeset: result.changeset
302
+ }) : handleSuccess(`Removed ${result.removed} dangling relationship(s)`, {
303
+ scanned: result.scanned,
304
+ dangling: result.dangling,
305
+ removed: result.removed
306
+ }), result;
307
+ }
308
+ function relationshipCommands(program, rootProgram) {
309
+ program.command("add <source> <target>").description("Add a relationship between elements (intra-layer or cross-layer)").option("--model <path>", "Path to model root (contains model/manifest.yaml)").requiredOption("--predicate <predicate>", "Relationship predicate (e.g., depends-on, realizes, exposes)").option("--properties <json>", "Relationship properties (JSON)").addHelpText("after", `
166
310
  Examples:
167
311
  $ dr relationship add motivation.goal.goal-a motivation.goal.goal-b --predicate aggregates
168
312
  $ dr relationship add api.operation.create-order application.service.order-service --predicate exposes
169
313
  $ dr relationship add business.businessprocess.checkout application.applicationprocess.checkout --predicate aggregates
170
314
  $ dr relationship add element-1 element-2 --predicate realizes --properties '{"strength":"high"}'`).action(async (source, target, options) => {
171
315
  try {
172
- const model = await Model.load(), sourceLayerName = await findElementLayer(model, source);
316
+ const model = await Model.load(options.model), sourceLayerName = await findElementLayer(model, source);
173
317
  if (!sourceLayerName)
174
318
  throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);
175
319
  const sourceLayer = await model.getLayer(sourceLayerName);
@@ -195,111 +339,171 @@ Examples:
195
339
  } catch {
196
340
  throw new CLIError("Invalid JSON in --properties", ErrorCategory.USER);
197
341
  }
198
- const activeChangesetId = await new StagingAreaManager(model.rootPath, model).getActiveId();
199
- if (!await addRelationshipHandler(model, source, target, options.predicate, properties, {
342
+ const activeChangesetId = await new StagingAreaManager(model.rootPath, model).getActiveId(), outcome = await addRelationship(model, source, target, options.predicate, properties, {
200
343
  source: { element: sourceElement, layerName: sourceLayerName },
201
344
  target: { element: targetElement, layerName: targetLayerName }
202
- }))
345
+ });
346
+ if (outcome === "duplicate") {
347
+ handleSuccess(`Relationship ${source} --[${options.predicate}]--> ${target} already exists` + (activeChangesetId ? " (in the model or the active changeset); nothing staged" : ""), {
348
+ source,
349
+ target,
350
+ predicate: options.predicate,
351
+ layer: sourceLayerName,
352
+ duplicate: !0,
353
+ ...activeChangesetId ? { changesetStatus: "unchanged" } : {}
354
+ }, { verbose: options.verbose });
203
355
  return;
204
- let message = `\u2713 Added relationship: ${ansis.bold(source)} ${options.predicate} ${ansis.bold(target)}`;
205
- constraints && (message += ansis.dim(` (${constraints.cardinality}, ${constraints.strength} strength)`)), activeChangesetId && (message += ansis.dim(" [staged]")), console.log(ansis.green(message));
356
+ }
357
+ if (outcome === "restored") {
358
+ handleSuccess(`Restored relationship: ${ansis.bold(source)} ${options.predicate} ${ansis.bold(target)}` + ansis.dim(" [the active changeset's staged deletion of it was unstaged]"), {
359
+ source,
360
+ target,
361
+ predicate: options.predicate,
362
+ layer: sourceLayerName,
363
+ restored: !0,
364
+ changesetStatus: "unstaged"
365
+ }, { verbose: options.verbose });
366
+ return;
367
+ }
368
+ let message = `Added relationship: ${ansis.bold(source)} ${options.predicate} ${ansis.bold(target)}`;
369
+ const details = {
370
+ source,
371
+ target,
372
+ predicate: options.predicate,
373
+ layer: sourceLayerName
374
+ };
375
+ constraints && (message += ansis.dim(` (${constraints.cardinality}, ${constraints.strength} strength)`), details.cardinality = constraints.cardinality, details.strength = constraints.strength), activeChangesetId && (message += ansis.dim(" [staged]"), details.changesetStatus = "staged"), handleSuccess(message, details, { verbose: options.verbose });
206
376
  } catch (error) {
207
377
  handleError(error);
208
378
  }
209
- }), program.command("delete <source> <target>").description("Delete a relationship").option("--predicate <predicate>", "Specific predicate to delete (optional, delete all if not specified)").option("--force", "Skip confirmation prompt").addHelpText("after", `
379
+ }), program.command("delete <source> <target>").description("Delete a relationship").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("--predicate <predicate>", "Specific predicate to delete (optional, delete all if not specified)").option("--force", "Skip confirmation prompt").addHelpText("after", `
210
380
  Examples:
211
381
  $ dr relationship delete element-1 element-2 --predicate depends-on
212
382
  $ dr relationship delete element-1 element-2 --force`).action(async (source, target, options) => {
213
383
  try {
214
- const model = await Model.load(), sourceLayerName = await findElementLayer(model, source);
215
- if (!sourceLayerName)
216
- throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);
217
- const toDelete = model.relationships.find(source, target, options.predicate);
384
+ const model = await Model.load(options.model), activeForDelete = await activeStagedChangeset(new StagingAreaManager(model.rootPath, model)), toDelete = activeForDelete ? liveRelationshipsBetween(await ChangesetGraph.build(model, activeForDelete), source, target, options.predicate) : await findRelationshipsByEndpoints(model, source, target, options.predicate);
218
385
  if (toDelete.length === 0)
219
- throw new CLIError("No matching relationships found", ErrorCategory.USER);
220
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
386
+ throw new CLIError("No matching relationships found", ErrorCategory.USER, [
387
+ `Use "dr relationship list ${source}" to see the relationships recorded for ${source}`,
388
+ 'Use "dr relationship prune --dry-run" to list rows whose endpoints no longer resolve'
389
+ ]);
390
+ const sourceLayerName = await findElementLayer(model, source) ?? toDelete[0].layer, isInteractive = process.stdin.isTTY && process.stdout.isTTY;
221
391
  if (!options.force && isInteractive) {
222
392
  const { confirm } = await import("@clack/prompts");
223
393
  if (!await confirm({
224
394
  message: `Delete ${toDelete.length} relationship(s)? This cannot be undone.`,
225
395
  initialValue: !1
226
396
  })) {
227
- console.log(ansis.dim("Cancelled"));
397
+ handleSuccess("Deletion cancelled", {
398
+ source,
399
+ target,
400
+ status: "cancelled"
401
+ });
228
402
  return;
229
403
  }
230
404
  }
231
405
  const activeChangesetId = await new StagingAreaManager(model.rootPath, model).getActiveId(), result = await deleteRelationshipHandler(model, source, target, options.predicate, {
232
406
  sourceLayerName,
233
- toDelete
234
- }), stagedSuffix = activeChangesetId ? " [staged]" : "";
235
- console.log(ansis.green(`\u2713 Deleted ${result.deletedCount} relationship(s) from ${ansis.bold(source)} to ${ansis.bold(target)}${stagedSuffix}`));
407
+ // In a changeset the handler re-reads the projected graph itself.
408
+ toDelete: activeForDelete ? void 0 : toDelete
409
+ }), stagedSuffix = activeChangesetId ? " [staged]" : "", deleteDetails = {
410
+ source,
411
+ target,
412
+ layer: sourceLayerName,
413
+ deletedCount: result.deletedCount,
414
+ ...result.staged ? { relationships: result.staged } : {}
415
+ };
416
+ options.predicate && (deleteDetails.predicate = options.predicate), activeChangesetId && (deleteDetails.changesetStatus = "staged"), handleSuccess(`Deleted ${result.deletedCount} relationship(s) from ${ansis.bold(source)} to ${ansis.bold(target)}${stagedSuffix}`, deleteDetails, { verbose: options.verbose });
417
+ } catch (error) {
418
+ handleError(error);
419
+ }
420
+ }), program.command("prune").description("Remove relationship rows whose source or target no longer resolves to an element").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("--dry-run", "List the rows that would be removed without writing").option("--layers <layers...>", "Only scan rows whose source or target layer is one of these canonical layer names").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
421
+ A row is dangling when its source or target matches neither the UUID id nor the
422
+ dotted path of any current element. Kept rows keep their order and the file's
423
+ header comment is preserved. Inside an active changeset the removals are staged
424
+ as relationship deletions instead of being written to relationships.yaml.
425
+
426
+ Examples:
427
+ $ dr relationship prune --dry-run
428
+ $ dr relationship prune
429
+ $ dr relationship prune --layers api application --format json`).action(async (options) => {
430
+ try {
431
+ await pruneRelationshipsCommand({
432
+ model: options.model,
433
+ dryRun: !!options.dryRun,
434
+ layers: options.layers,
435
+ format: options.format
436
+ });
236
437
  } catch (error) {
237
438
  handleError(error);
238
439
  }
239
- }), program.command("list <id>").description("List relationships for an element").option("--direction <dir>", "Filter by direction (incoming/outgoing/all)", "all").option("--json", "Output as JSON").addHelpText("after", `
440
+ }), program.command("list <id>").description("List relationships for an element").option("--model <path>", "Path to model root (contains model/manifest.yaml)").option("--direction <dir>", "Filter by direction (incoming/outgoing/all)", "all").option("--json", "Output as JSON").addHelpText("after", `
240
441
  Examples:
241
442
  $ dr relationship list api-endpoint-create-customer
242
443
  $ dr relationship list business-service-order --direction outgoing
243
444
  $ dr relationship list element-1 --json`).action(async (id, options) => {
244
445
  try {
245
- const model = await Model.load();
446
+ const model = await Model.load(options.model);
246
447
  if (!await findElementLayer(model, id))
247
448
  throw new CLIError(`Element ${id} not found`, ErrorCategory.USER);
248
449
  const { outgoing, incoming } = model.relationships.getForElement(id);
249
450
  let relationships = [];
250
- if ((options.direction === "outgoing" || options.direction === "all") && relationships.push(...outgoing), (options.direction === "incoming" || options.direction === "all") && relationships.push(...incoming), options.json) {
451
+ if ((options.direction === "outgoing" || options.direction === "all") && relationships.push(...outgoing), (options.direction === "incoming" || options.direction === "all") && relationships.push(...incoming), options.json ?? rootProgram?.opts().json) {
251
452
  console.log(JSON.stringify(relationships, null, 2));
252
453
  return;
253
454
  }
254
455
  if (relationships.length === 0) {
255
- console.log(ansis.yellow(`No ${options.direction} relationships for ${id}`));
456
+ handleInfo(ansis.yellow(`No ${options.direction} relationships for ${id}`));
256
457
  return;
257
458
  }
258
- console.log(""), console.log(ansis.bold(`${options.direction === "all" ? "" : options.direction + " "}relationships for ${ansis.cyan(id)}:`)), console.log(ansis.dim("\u2500".repeat(80)));
459
+ handleInfo(""), handleInfo(ansis.bold(`${options.direction === "all" ? "" : options.direction + " "}relationships for ${ansis.cyan(id)}:`)), handleInfo(ansis.dim("\u2500".repeat(80)));
259
460
  for (const rel of relationships) {
260
461
  const isOutgoing = rel.source === id, direction = isOutgoing ? "\u2192" : "\u2190", otherElement = isOutgoing ? rel.target : rel.source, crossLayer = rel.targetLayer && rel.targetLayer !== rel.layer ? ansis.dim(` [${rel.layer} \u2192 ${rel.targetLayer}]`) : "";
261
- if (console.log(` ${direction} ${ansis.magenta(rel.predicate)}: ${ansis.yellow(otherElement)}${crossLayer}`), rel.properties) {
462
+ if (handleInfo(` ${direction} ${ansis.magenta(rel.predicate)}: ${ansis.yellow(otherElement)}${crossLayer}`), rel.properties) {
262
463
  const propStr = Object.entries(rel.properties).map(([k, v]) => `${k}=${v}`).join(", ");
263
- console.log(` ${ansis.dim(propStr)}`);
464
+ handleInfo(` ${ansis.dim(propStr)}`);
264
465
  }
265
466
  }
266
- console.log(ansis.dim("\u2500".repeat(80))), console.log(ansis.dim(`Total: ${relationships.length} relationship(s)`)), console.log("");
467
+ handleInfo(ansis.dim("\u2500".repeat(80))), handleInfo(ansis.dim(`Total: ${relationships.length} relationship(s)`)), handleInfo("");
267
468
  } catch (error) {
268
469
  handleError(error);
269
470
  }
270
- }), program.command("show <source> <target>").description("Show relationship details").addHelpText("after", `
471
+ }), program.command("show <source> <target>").description("Show relationship details").option("--model <path>", "Path to model root (contains model/manifest.yaml)").addHelpText("after", `
271
472
  Examples:
272
473
  $ dr relationship show api-endpoint-create-customer business-service-customer-mgmt
273
- $ dr relationship show application-component-api motivation-goal-sales-efficiency`).action(async (source, target) => {
474
+ $ dr relationship show application-component-api motivation-goal-sales-efficiency`).action(async (source, target, options) => {
274
475
  try {
275
- const model = await Model.load();
476
+ const model = await Model.load(options.model);
276
477
  if (!await findElementLayer(model, source))
277
478
  throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);
278
479
  const relationships = model.relationships.find(source, target);
279
480
  if (relationships.length === 0)
280
481
  throw new CLIError(`No relationships from ${source} to ${target}`, ErrorCategory.USER);
281
- console.log(""), console.log(ansis.bold(`Relationship${relationships.length > 1 ? "s" : ""} from ${ansis.cyan(source)} to ${ansis.yellow(target)}:`)), console.log(ansis.dim("\u2500".repeat(60)));
482
+ handleInfo(""), handleInfo(ansis.bold(`Relationship${relationships.length > 1 ? "s" : ""} from ${ansis.cyan(source)} to ${ansis.yellow(target)}:`)), handleInfo(ansis.dim("\u2500".repeat(60)));
282
483
  for (let i = 0; i < relationships.length; i++) {
283
484
  const rel = relationships[i];
284
- if (i > 0 && console.log(""), console.log(`Predicate: ${ansis.magenta(rel.predicate)}`), console.log(`Layer: ${ansis.cyan(rel.layer)}`), rel.targetLayer && rel.targetLayer !== rel.layer && console.log(`Target Layer: ${ansis.cyan(rel.targetLayer)}`), rel.properties && Object.keys(rel.properties).length > 0) {
285
- console.log("Properties:");
485
+ if (i > 0 && handleInfo(""), handleInfo(`Predicate: ${ansis.magenta(rel.predicate)}`), handleInfo(`Layer: ${ansis.cyan(rel.layer)}`), rel.targetLayer && rel.targetLayer !== rel.layer && handleInfo(`Target Layer: ${ansis.cyan(rel.targetLayer)}`), rel.properties && Object.keys(rel.properties).length > 0) {
486
+ handleInfo("Properties:");
286
487
  for (const [key, value] of Object.entries(rel.properties)) {
287
488
  const displayValue = typeof value == "string" ? value : JSON.stringify(value, null, 2);
288
- console.log(` ${ansis.cyan(key)}: ${displayValue}`);
489
+ handleInfo(` ${ansis.cyan(key)}: ${displayValue}`);
289
490
  }
290
491
  } else
291
- console.log("Properties: none");
492
+ handleInfo("Properties: none");
292
493
  }
293
- console.log("");
494
+ handleInfo("");
294
495
  } catch (error) {
295
496
  handleError(error);
296
497
  }
297
498
  });
298
499
  }
299
500
  export {
501
+ addRelationship,
300
502
  addRelationshipHandler,
301
503
  deleteRelationshipHandler,
504
+ findRelationshipsByEndpoints,
302
505
  getRelationshipConstraints,
506
+ pruneRelationshipsCommand,
303
507
  relationshipCommands,
304
508
  validateRelationshipCombination
305
509
  };