@documentation-robotics/cli 0.1.0 → 0.1.7

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 (510) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +466 -693
  3. package/dist/analysis/data-model-analyzer.js +189 -0
  4. package/dist/analysis/data-model-analyzer.js.map +7 -0
  5. package/dist/analysis/index.js +3 -0
  6. package/dist/analysis/index.js.map +7 -0
  7. package/dist/analysis/relationship-classifier.js +181 -0
  8. package/dist/analysis/relationship-classifier.js.map +7 -0
  9. package/dist/analyzers/base-analyzer.js +1 -0
  10. package/dist/analyzers/base-analyzer.js.map +7 -0
  11. package/dist/analyzers/call-graph-utils.js +39 -0
  12. package/dist/analyzers/call-graph-utils.js.map +7 -0
  13. package/dist/analyzers/cbm-analyzer.js +1205 -0
  14. package/dist/analyzers/cbm-analyzer.js.map +7 -0
  15. package/dist/analyzers/heuristic-utils.js +55 -0
  16. package/dist/analyzers/heuristic-utils.js.map +7 -0
  17. package/dist/analyzers/mapping-loader.js +146 -0
  18. package/dist/analyzers/mapping-loader.js.map +7 -0
  19. package/dist/analyzers/registry.js +124 -0
  20. package/dist/analyzers/registry.js.map +7 -0
  21. package/dist/analyzers/session-state.js +176 -0
  22. package/dist/analyzers/session-state.js.map +7 -0
  23. package/dist/analyzers/stdio-client.js +208 -0
  24. package/dist/analyzers/stdio-client.js.map +7 -0
  25. package/dist/analyzers/types.js +15 -0
  26. package/dist/analyzers/types.js.map +7 -0
  27. package/dist/analyzers/verify-engine.js +209 -0
  28. package/dist/analyzers/verify-engine.js.map +7 -0
  29. package/dist/analyzers/verify-ignore.js +130 -0
  30. package/dist/analyzers/verify-ignore.js.map +7 -0
  31. package/dist/analyzers/version.js +8 -0
  32. package/dist/analyzers/version.js.map +7 -0
  33. package/dist/audit/ai/runner.js +41 -0
  34. package/dist/audit/ai/runner.js.map +7 -0
  35. package/dist/audit/differential-analyzer.js +183 -0
  36. package/dist/audit/differential-analyzer.js.map +7 -0
  37. package/dist/audit/index.js +32 -0
  38. package/dist/audit/index.js.map +7 -0
  39. package/dist/audit/nodes/ai/evaluator.js +49 -0
  40. package/dist/audit/nodes/ai/evaluator.js.map +7 -0
  41. package/dist/audit/nodes/ai/parser.js +55 -0
  42. package/dist/audit/nodes/ai/parser.js.map +7 -0
  43. package/dist/audit/nodes/ai/prompts.js +103 -0
  44. package/dist/audit/nodes/ai/prompts.js.map +7 -0
  45. package/dist/audit/nodes/analysis/node-completeness-checker.js +32 -0
  46. package/dist/audit/nodes/analysis/node-completeness-checker.js.map +7 -0
  47. package/dist/audit/nodes/analysis/node-definition-analyzer.js +47 -0
  48. package/dist/audit/nodes/analysis/node-definition-analyzer.js.map +7 -0
  49. package/dist/audit/nodes/analysis/node-overlap-detector.js +79 -0
  50. package/dist/audit/nodes/analysis/node-overlap-detector.js.map +7 -0
  51. package/dist/audit/nodes/model/orchestrator.js +82 -0
  52. package/dist/audit/nodes/model/orchestrator.js.map +7 -0
  53. package/dist/audit/nodes/spec/loader.js +116 -0
  54. package/dist/audit/nodes/spec/loader.js.map +7 -0
  55. package/dist/audit/nodes/spec/orchestrator.js +68 -0
  56. package/dist/audit/nodes/spec/orchestrator.js.map +7 -0
  57. package/dist/audit/nodes/types.js +1 -0
  58. package/dist/audit/nodes/types.js.map +7 -0
  59. package/dist/audit/pipeline/index.js +5 -0
  60. package/dist/audit/pipeline/index.js.map +7 -0
  61. package/dist/audit/pipeline/pipeline-orchestrator.js +218 -0
  62. package/dist/audit/pipeline/pipeline-orchestrator.js.map +7 -0
  63. package/dist/audit/relationships/ai/evaluator.js +235 -0
  64. package/dist/audit/relationships/ai/evaluator.js.map +7 -0
  65. package/dist/audit/relationships/ai/parser.js +197 -0
  66. package/dist/audit/relationships/ai/parser.js.map +7 -0
  67. package/dist/audit/relationships/ai/prompts.js +274 -0
  68. package/dist/audit/relationships/ai/prompts.js.map +7 -0
  69. package/dist/audit/relationships/analysis/balance-assessor.js +115 -0
  70. package/dist/audit/relationships/analysis/balance-assessor.js.map +7 -0
  71. package/dist/audit/relationships/analysis/coverage-analyzer.js +99 -0
  72. package/dist/audit/relationships/analysis/coverage-analyzer.js.map +7 -0
  73. package/dist/audit/relationships/analysis/duplicate-detector.js +95 -0
  74. package/dist/audit/relationships/analysis/duplicate-detector.js.map +7 -0
  75. package/dist/audit/relationships/analysis/gap-analyzer.js +99 -0
  76. package/dist/audit/relationships/analysis/gap-analyzer.js.map +7 -0
  77. package/dist/audit/relationships/analysis/layer-templates.js +294 -0
  78. package/dist/audit/relationships/analysis/layer-templates.js.map +7 -0
  79. package/dist/audit/relationships/graph/connectivity.js +129 -0
  80. package/dist/audit/relationships/graph/connectivity.js.map +7 -0
  81. package/dist/audit/relationships/graph/relationship-graph.js +179 -0
  82. package/dist/audit/relationships/graph/relationship-graph.js.map +7 -0
  83. package/dist/audit/relationships/model/balance-assessor.js +49 -0
  84. package/dist/audit/relationships/model/balance-assessor.js.map +7 -0
  85. package/dist/audit/relationships/model/coverage-analyzer.js +34 -0
  86. package/dist/audit/relationships/model/coverage-analyzer.js.map +7 -0
  87. package/dist/audit/relationships/model/duplicate-detector.js +68 -0
  88. package/dist/audit/relationships/model/duplicate-detector.js.map +7 -0
  89. package/dist/audit/relationships/model/gap-analyzer.js +41 -0
  90. package/dist/audit/relationships/model/gap-analyzer.js.map +7 -0
  91. package/dist/audit/relationships/model/orchestrator.js +126 -0
  92. package/dist/audit/relationships/model/orchestrator.js.map +7 -0
  93. package/dist/audit/relationships/spec/orchestrator.js +157 -0
  94. package/dist/audit/relationships/spec/orchestrator.js.map +7 -0
  95. package/dist/audit/reports/index.js +5 -0
  96. package/dist/audit/reports/index.js.map +7 -0
  97. package/dist/audit/reports/report-generator.js +118 -0
  98. package/dist/audit/reports/report-generator.js.map +7 -0
  99. package/dist/audit/snapshot-storage.js +163 -0
  100. package/dist/audit/snapshot-storage.js.map +7 -0
  101. package/dist/audit/types.js +21 -0
  102. package/dist/audit/types.js.map +7 -0
  103. package/dist/cli.js +223 -92
  104. package/dist/cli.js.map +3 -3
  105. package/dist/coding-agents/agents/claude-code.js +147 -0
  106. package/dist/coding-agents/agents/claude-code.js.map +7 -0
  107. package/dist/coding-agents/agents/index.js +5 -0
  108. package/dist/coding-agents/agents/index.js.map +7 -0
  109. package/dist/coding-agents/agents/types.js +1 -0
  110. package/dist/coding-agents/agents/types.js.map +7 -0
  111. package/dist/coding-agents/base-chat-client.js +45 -0
  112. package/dist/coding-agents/base-chat-client.js.map +7 -0
  113. package/dist/coding-agents/chat-utils.js +23 -0
  114. package/dist/coding-agents/chat-utils.js.map +7 -0
  115. package/dist/coding-agents/claude-code-client.js +246 -0
  116. package/dist/coding-agents/claude-code-client.js.map +7 -0
  117. package/dist/coding-agents/copilot-client.js +209 -0
  118. package/dist/coding-agents/copilot-client.js.map +7 -0
  119. package/dist/commands/add.js +95 -33
  120. package/dist/commands/add.js.map +2 -2
  121. package/dist/commands/analyzer.js +511 -0
  122. package/dist/commands/analyzer.js.map +7 -0
  123. package/dist/commands/audit-diff.js +49 -0
  124. package/dist/commands/audit-diff.js.map +7 -0
  125. package/dist/commands/audit-snapshots.js +62 -0
  126. package/dist/commands/audit-snapshots.js.map +7 -0
  127. package/dist/commands/audit.js +81 -0
  128. package/dist/commands/audit.js.map +7 -0
  129. package/dist/commands/catalog.js +248 -0
  130. package/dist/commands/catalog.js.map +7 -0
  131. package/dist/commands/changeset.js +577 -66
  132. package/dist/commands/changeset.js.map +2 -2
  133. package/dist/commands/chat.js +106 -83
  134. package/dist/commands/chat.js.map +2 -2
  135. package/dist/commands/claude.js +74 -0
  136. package/dist/commands/claude.js.map +7 -0
  137. package/dist/commands/conformance.js +71 -68
  138. package/dist/commands/conformance.js.map +2 -2
  139. package/dist/commands/copilot.js +75 -0
  140. package/dist/commands/copilot.js.map +7 -0
  141. package/dist/commands/delete.js +125 -11
  142. package/dist/commands/delete.js.map +3 -3
  143. package/dist/commands/discover-logic.js +44 -0
  144. package/dist/commands/discover-logic.js.map +7 -0
  145. package/dist/commands/docs.js +87 -0
  146. package/dist/commands/docs.js.map +7 -0
  147. package/dist/commands/element.js +14 -20
  148. package/dist/commands/element.js.map +2 -2
  149. package/dist/commands/export.js +13 -3
  150. package/dist/commands/export.js.map +2 -2
  151. package/dist/commands/import.js +51 -0
  152. package/dist/commands/import.js.map +7 -0
  153. package/dist/commands/info.js +30 -24
  154. package/dist/commands/info.js.map +2 -2
  155. package/dist/commands/init.js +27 -11
  156. package/dist/commands/init.js.map +2 -2
  157. package/dist/commands/list.js +63 -13
  158. package/dist/commands/list.js.map +3 -3
  159. package/dist/commands/relationship.js +225 -37
  160. package/dist/commands/relationship.js.map +2 -2
  161. package/dist/commands/report.js +54 -0
  162. package/dist/commands/report.js.map +7 -0
  163. package/dist/commands/reports.js +45 -0
  164. package/dist/commands/reports.js.map +7 -0
  165. package/dist/commands/schema.js +135 -0
  166. package/dist/commands/schema.js.map +7 -0
  167. package/dist/commands/search.js +40 -12
  168. package/dist/commands/search.js.map +2 -2
  169. package/dist/commands/show.js +58 -27
  170. package/dist/commands/show.js.map +2 -2
  171. package/dist/commands/stats.js +35 -0
  172. package/dist/commands/stats.js.map +7 -0
  173. package/dist/commands/trace.js +39 -16
  174. package/dist/commands/trace.js.map +2 -2
  175. package/dist/commands/update.js +141 -14
  176. package/dist/commands/update.js.map +2 -2
  177. package/dist/commands/upgrade.js +210 -46
  178. package/dist/commands/upgrade.js.map +3 -3
  179. package/dist/commands/validate.js +142 -23
  180. package/dist/commands/validate.js.map +3 -3
  181. package/dist/commands/version.js +8 -12
  182. package/dist/commands/version.js.map +2 -2
  183. package/dist/commands/visualize.js +162 -25
  184. package/dist/commands/visualize.js.map +2 -2
  185. package/dist/core/base-snapshot-manager.js +84 -0
  186. package/dist/core/base-snapshot-manager.js.map +7 -0
  187. package/dist/core/changeset-exporter.js +305 -0
  188. package/dist/core/changeset-exporter.js.map +7 -0
  189. package/dist/core/changeset-validator.js +41 -0
  190. package/dist/core/changeset-validator.js.map +7 -0
  191. package/dist/core/changeset.js +88 -173
  192. package/dist/core/changeset.js.map +2 -2
  193. package/dist/core/dependency-tracker.js +4 -2
  194. package/dist/core/dependency-tracker.js.map +2 -2
  195. package/dist/core/element.js +98 -27
  196. package/dist/core/element.js.map +2 -2
  197. package/dist/core/golden-copy-cache.js +290 -0
  198. package/dist/core/golden-copy-cache.js.map +7 -0
  199. package/dist/core/graph-model.js +266 -0
  200. package/dist/core/graph-model.js.map +7 -0
  201. package/dist/core/layer.js +159 -16
  202. package/dist/core/layer.js.map +2 -2
  203. package/dist/core/layers.js +32 -0
  204. package/dist/core/layers.js.map +7 -0
  205. package/dist/core/manifest.js +32 -11
  206. package/dist/core/manifest.js.map +2 -2
  207. package/dist/core/migration-registry.js +56 -1
  208. package/dist/core/migration-registry.js.map +2 -2
  209. package/dist/core/model.js +424 -217
  210. package/dist/core/model.js.map +3 -3
  211. package/dist/core/mutation-handler.js +179 -0
  212. package/dist/core/mutation-handler.js.map +7 -0
  213. package/dist/core/reference-registry.js +29 -21
  214. package/dist/core/reference-registry.js.map +2 -2
  215. package/dist/core/relationship-catalog.js +189 -0
  216. package/dist/core/relationship-catalog.js.map +7 -0
  217. package/dist/core/relationship-registry.js.map +2 -2
  218. package/dist/core/relationships.js.map +1 -1
  219. package/dist/core/report-data-model.js +285 -0
  220. package/dist/core/report-data-model.js.map +7 -0
  221. package/dist/core/spec-data-service.js +185 -0
  222. package/dist/core/spec-data-service.js.map +7 -0
  223. package/dist/core/spec-loader-types.js +1 -0
  224. package/dist/core/spec-loader-types.js.map +7 -0
  225. package/dist/core/spec-loader.js +234 -0
  226. package/dist/core/spec-loader.js.map +7 -0
  227. package/dist/core/staged-changeset-storage.js +282 -0
  228. package/dist/core/staged-changeset-storage.js.map +7 -0
  229. package/dist/core/staging-area.js +1002 -0
  230. package/dist/core/staging-area.js.map +7 -0
  231. package/dist/core/stats-collector.js +169 -0
  232. package/dist/core/stats-collector.js.map +7 -0
  233. package/dist/core/virtual-projection.js +443 -0
  234. package/dist/core/virtual-projection.js.map +7 -0
  235. package/dist/export/archimate-exporter.js +99 -60
  236. package/dist/export/archimate-exporter.js.map +2 -2
  237. package/dist/export/audit-formatters.js +479 -0
  238. package/dist/export/audit-formatters.js.map +7 -0
  239. package/dist/export/diff-formatters.js +99 -0
  240. package/dist/export/diff-formatters.js.map +7 -0
  241. package/dist/export/export-manager.js.map +2 -2
  242. package/dist/export/graphml-exporter.js +44 -27
  243. package/dist/export/graphml-exporter.js.map +2 -2
  244. package/dist/export/index.js +4 -0
  245. package/dist/export/index.js.map +2 -2
  246. package/dist/export/json-schema-exporter.js +55 -42
  247. package/dist/export/json-schema-exporter.js.map +2 -2
  248. package/dist/export/markdown-exporter.js +92 -70
  249. package/dist/export/markdown-exporter.js.map +2 -2
  250. package/dist/export/markdown-generator.js +248 -0
  251. package/dist/export/markdown-generator.js.map +7 -0
  252. package/dist/export/markdown-utils.js +30 -0
  253. package/dist/export/markdown-utils.js.map +7 -0
  254. package/dist/export/mermaid-markdown-exporter.js +37 -0
  255. package/dist/export/mermaid-markdown-exporter.js.map +7 -0
  256. package/dist/export/openapi-exporter.js +119 -71
  257. package/dist/export/openapi-exporter.js.map +2 -2
  258. package/dist/export/plantuml-exporter.js +104 -39
  259. package/dist/export/plantuml-exporter.js.map +2 -2
  260. package/dist/export/report-formatters.js +111 -0
  261. package/dist/export/report-formatters.js.map +7 -0
  262. package/dist/export/stats-formatters.js +98 -0
  263. package/dist/export/stats-formatters.js.map +7 -0
  264. package/dist/export/types.js +2 -14
  265. package/dist/export/types.js.map +2 -2
  266. package/dist/export/verify-formatters.js +153 -0
  267. package/dist/export/verify-formatters.js.map +7 -0
  268. package/dist/generated/compiled-validators.js +17 -0
  269. package/dist/generated/compiled-validators.js.map +7 -0
  270. package/dist/generated/index.js +31 -0
  271. package/dist/generated/index.js.map +7 -0
  272. package/dist/generated/layer-registry.js +190 -0
  273. package/dist/generated/layer-registry.js.map +7 -0
  274. package/dist/generated/layer-types.js +7 -0
  275. package/dist/generated/layer-types.js.map +7 -0
  276. package/dist/generated/node-types.js +1719 -0
  277. package/dist/generated/node-types.js.map +7 -0
  278. package/dist/generated/relationship-index.js +14552 -0
  279. package/dist/generated/relationship-index.js.map +7 -0
  280. package/dist/import/archimate-importer.js +166 -0
  281. package/dist/import/archimate-importer.js.map +7 -0
  282. package/dist/import/index.js +47 -0
  283. package/dist/import/index.js.map +7 -0
  284. package/dist/import/openapi-importer.js +143 -0
  285. package/dist/import/openapi-importer.js.map +7 -0
  286. package/dist/import/types.js +1 -0
  287. package/dist/import/types.js.map +7 -0
  288. package/dist/integrations/base-manager.js +311 -0
  289. package/dist/integrations/base-manager.js.map +7 -0
  290. package/dist/integrations/claude-manager.js +360 -0
  291. package/dist/integrations/claude-manager.js.map +7 -0
  292. package/dist/integrations/claude_code/agents/dr-advisor.md +253 -0
  293. package/dist/integrations/claude_code/agents/dr-architect.md +278 -0
  294. package/dist/integrations/claude_code/agents/dr-extractor.md +286 -0
  295. package/dist/integrations/claude_code/commands/dr-changeset.md +657 -0
  296. package/dist/integrations/claude_code/commands/dr-design.md +373 -0
  297. package/dist/integrations/claude_code/commands/dr-info.md +132 -0
  298. package/dist/integrations/claude_code/commands/dr-init.md +251 -0
  299. package/dist/integrations/claude_code/commands/dr-map.md +936 -0
  300. package/dist/integrations/claude_code/commands/dr-model.md +493 -0
  301. package/dist/integrations/claude_code/commands/dr-relate.md +814 -0
  302. package/dist/integrations/claude_code/commands/dr-sync.md +279 -0
  303. package/dist/integrations/claude_code/commands/dr-validate.md +654 -0
  304. package/dist/integrations/claude_code/commands/dr-verify.md +559 -0
  305. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +903 -0
  306. package/dist/integrations/claude_code/docs/QUICK_START.md +271 -0
  307. package/dist/integrations/claude_code/docs/README.md +347 -0
  308. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +550 -0
  309. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +620 -0
  310. package/dist/integrations/claude_code/skills/dr_03_security_layer/SKILL.md +642 -0
  311. package/dist/integrations/claude_code/skills/dr_04_application_layer/SKILL.md +911 -0
  312. package/dist/integrations/claude_code/skills/dr_05_technology_layer/SKILL.md +836 -0
  313. package/dist/integrations/claude_code/skills/dr_06_api_layer/SKILL.md +933 -0
  314. package/dist/integrations/claude_code/skills/dr_07_data_model_layer/SKILL.md +210 -0
  315. package/dist/integrations/claude_code/skills/dr_08_dataset_layer/SKILL.md +319 -0
  316. package/dist/integrations/claude_code/skills/dr_09_ux_layer/SKILL.md +319 -0
  317. package/dist/integrations/claude_code/skills/dr_10_navigation_layer/SKILL.md +318 -0
  318. package/dist/integrations/claude_code/skills/dr_11_apm_layer/SKILL.md +273 -0
  319. package/dist/integrations/claude_code/skills/dr_12_testing_layer/SKILL.md +273 -0
  320. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +187 -0
  321. package/dist/integrations/claude_code/skills/dr_codebase_memory/SKILL.md +590 -0
  322. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +295 -0
  323. package/dist/integrations/copilot-manager.js +328 -0
  324. package/dist/integrations/copilot-manager.js.map +7 -0
  325. package/dist/integrations/github_copilot/agents/dr-advisor.md +243 -0
  326. package/dist/integrations/github_copilot/agents/dr-architect.md +1567 -0
  327. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +491 -0
  328. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +539 -0
  329. package/dist/integrations/github_copilot/skills/dr_03_security_layer/SKILL.md +667 -0
  330. package/dist/integrations/github_copilot/skills/dr_04_application_layer/SKILL.md +670 -0
  331. package/dist/integrations/github_copilot/skills/dr_05_technology_layer/SKILL.md +660 -0
  332. package/dist/integrations/github_copilot/skills/dr_06_api_layer/SKILL.md +855 -0
  333. package/dist/integrations/github_copilot/skills/dr_07_data_model_layer/SKILL.md +171 -0
  334. package/dist/integrations/github_copilot/skills/dr_08_dataset_layer/SKILL.md +216 -0
  335. package/dist/integrations/github_copilot/skills/dr_09_ux_layer/SKILL.md +230 -0
  336. package/dist/integrations/github_copilot/skills/dr_10_navigation_layer/SKILL.md +228 -0
  337. package/dist/integrations/github_copilot/skills/dr_11_apm_layer/SKILL.md +234 -0
  338. package/dist/integrations/github_copilot/skills/dr_12_testing_layer/SKILL.md +230 -0
  339. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +187 -0
  340. package/dist/integrations/hash-utils.js +51 -0
  341. package/dist/integrations/hash-utils.js.map +7 -0
  342. package/dist/integrations/types.js +16 -0
  343. package/dist/integrations/types.js.map +7 -0
  344. package/dist/reports/model-layer-report-generator.js +278 -0
  345. package/dist/reports/model-layer-report-generator.js.map +7 -0
  346. package/dist/reports/model-report-data.js +57 -0
  347. package/dist/reports/model-report-data.js.map +7 -0
  348. package/dist/reports/model-report-orchestrator.js +103 -0
  349. package/dist/reports/model-report-orchestrator.js.map +7 -0
  350. package/dist/schemas/bundled/analyzers/cbm.json +595 -0
  351. package/dist/schemas/bundled/analyzers/manifest.json +9 -0
  352. package/dist/schemas/bundled/api.json +3411 -0
  353. package/dist/schemas/bundled/apm.json +2371 -0
  354. package/dist/schemas/bundled/application.json +1253 -0
  355. package/dist/schemas/bundled/base.json +1622 -0
  356. package/dist/schemas/bundled/business.json +1279 -0
  357. package/dist/schemas/bundled/data-model.json +1298 -0
  358. package/dist/schemas/bundled/data-store.json +2025 -0
  359. package/dist/schemas/bundled/manifest.json +93 -0
  360. package/dist/schemas/bundled/motivation.json +1360 -0
  361. package/dist/schemas/bundled/navigation.json +1874 -0
  362. package/dist/schemas/bundled/security.json +3488 -0
  363. package/dist/schemas/bundled/technology.json +2067 -0
  364. package/dist/schemas/bundled/testing.json +3120 -0
  365. package/dist/schemas/bundled/ux.json +3592 -0
  366. package/dist/server/schemas.js +202 -0
  367. package/dist/server/schemas.js.map +7 -0
  368. package/dist/server/server.js +1336 -252
  369. package/dist/server/server.js.map +3 -3
  370. package/dist/server/telemetry-middleware.js +22 -15
  371. package/dist/server/telemetry-middleware.js.map +2 -2
  372. package/dist/server-entry.js +65 -0
  373. package/dist/server-entry.js.map +7 -0
  374. package/dist/telemetry/config.js +89 -0
  375. package/dist/telemetry/config.js.map +7 -0
  376. package/dist/telemetry/console-interceptor.js.map +1 -1
  377. package/dist/telemetry/detect.js +24 -0
  378. package/dist/telemetry/detect.js.map +7 -0
  379. package/dist/telemetry/fetch-otlp-exporter.js +112 -0
  380. package/dist/telemetry/fetch-otlp-exporter.js.map +7 -0
  381. package/dist/telemetry/index.js +153 -3
  382. package/dist/telemetry/index.js.map +2 -2
  383. package/dist/telemetry/resilient-exporter.js +22 -8
  384. package/dist/telemetry/resilient-exporter.js.map +2 -2
  385. package/dist/telemetry/resilient-log-exporter.js +17 -8
  386. package/dist/telemetry/resilient-log-exporter.js.map +2 -2
  387. package/dist/telemetry/test-instrumentation.js.map +2 -2
  388. package/dist/types/index.js +84 -0
  389. package/dist/types/index.js.map +3 -3
  390. package/dist/types/source-reference.js +1 -0
  391. package/dist/types/source-reference.js.map +7 -0
  392. package/dist/utils/chat-logger.js +292 -0
  393. package/dist/utils/chat-logger.js.map +7 -0
  394. package/dist/utils/claude-stream.js +8 -0
  395. package/dist/utils/claude-stream.js.map +7 -0
  396. package/dist/utils/date-utils.js +7 -0
  397. package/dist/utils/date-utils.js.map +7 -0
  398. package/dist/utils/element-utils.js.map +1 -1
  399. package/dist/utils/errors.js +88 -15
  400. package/dist/utils/errors.js.map +3 -3
  401. package/dist/utils/export-python-annotations.js +82 -0
  402. package/dist/utils/export-python-annotations.js.map +7 -0
  403. package/dist/utils/file-io.js +7 -2
  404. package/dist/utils/file-io.js.map +2 -2
  405. package/dist/utils/file-lock.js +156 -0
  406. package/dist/utils/file-lock.js.map +7 -0
  407. package/dist/utils/id-generator.js +18 -0
  408. package/dist/utils/id-generator.js.map +7 -0
  409. package/dist/utils/layer-name-formatter.js +16 -0
  410. package/dist/utils/layer-name-formatter.js.map +7 -0
  411. package/dist/utils/markdown-anchor.js +7 -0
  412. package/dist/utils/markdown-anchor.js.map +7 -0
  413. package/dist/utils/markdown-link.js +12 -0
  414. package/dist/utils/markdown-link.js.map +7 -0
  415. package/dist/utils/markdown-table.js +17 -0
  416. package/dist/utils/markdown-table.js.map +7 -0
  417. package/dist/utils/mermaid-utils.js +11 -0
  418. package/dist/utils/mermaid-utils.js.map +7 -0
  419. package/dist/utils/project-paths.js.map +2 -2
  420. package/dist/utils/source-reference.js +52 -0
  421. package/dist/utils/source-reference.js.map +7 -0
  422. package/dist/utils/spec-installer.js +40 -53
  423. package/dist/utils/spec-installer.js.map +2 -2
  424. package/dist/utils/spec-version.js +5 -1
  425. package/dist/utils/spec-version.js.map +2 -2
  426. package/dist/utils/string-utils.js +12 -0
  427. package/dist/utils/string-utils.js.map +7 -0
  428. package/dist/utils/table-formatting.js +19 -0
  429. package/dist/utils/table-formatting.js.map +7 -0
  430. package/dist/validators/index.js +4 -0
  431. package/dist/validators/index.js.map +2 -2
  432. package/dist/validators/naming-validator.js +20 -23
  433. package/dist/validators/naming-validator.js.map +2 -2
  434. package/dist/validators/reference-validator.js +23 -38
  435. package/dist/validators/reference-validator.js.map +2 -2
  436. package/dist/validators/relationship-schema-validator.js +243 -0
  437. package/dist/validators/relationship-schema-validator.js.map +7 -0
  438. package/dist/validators/schema-validator.js +168 -68
  439. package/dist/validators/schema-validator.js.map +2 -2
  440. package/dist/validators/semantic-validator.js +36 -60
  441. package/dist/validators/semantic-validator.js.map +2 -2
  442. package/dist/validators/types.js.map +2 -2
  443. package/dist/validators/validation-formatter.js +159 -0
  444. package/dist/validators/validation-formatter.js.map +7 -0
  445. package/dist/validators/validator.js +24 -38
  446. package/dist/validators/validator.js.map +2 -2
  447. package/dist/viewer/README.md +139 -0
  448. package/dist/viewer/assets/main-CY0Xufuj.css +1 -0
  449. package/dist/viewer/assets/main-DtgErnwF.js +81 -0
  450. package/dist/viewer/assets/vendor-CxKvDp2x.js +17 -0
  451. package/dist/viewer/favicon.svg +11 -0
  452. package/dist/viewer/fonts/inter/Inter-Black.woff2 +0 -0
  453. package/dist/viewer/fonts/inter/Inter-Bold.woff2 +0 -0
  454. package/dist/viewer/fonts/inter/Inter-ExtraBold.woff2 +0 -0
  455. package/dist/viewer/fonts/inter/Inter-Light.woff2 +0 -0
  456. package/dist/viewer/fonts/inter/Inter-Medium.woff2 +0 -0
  457. package/dist/viewer/fonts/inter/Inter-Regular.woff2 +0 -0
  458. package/dist/viewer/fonts/inter/Inter-SemiBold.woff2 +0 -0
  459. package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-Medium.woff2 +0 -0
  460. package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
  461. package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-SemiBold.woff2 +0 -0
  462. package/dist/viewer/index.html +15 -0
  463. package/dist/viewer/manifest.json +99 -0
  464. package/dist/viewer/version.json +1 -0
  465. package/package.json +42 -16
  466. package/PYTHON_CLI_PARITY.md +0 -265
  467. package/PYTHON_CLI_STRUCTURE_UPDATE.md +0 -246
  468. package/biome.json +0 -42
  469. package/bunfig.toml +0 -3
  470. package/dist/ai/claude-client.js +0 -71
  471. package/dist/ai/claude-client.js.map +0 -7
  472. package/dist/ai/context-provider.js +0 -129
  473. package/dist/ai/context-provider.js.map +0 -7
  474. package/dist/ai/tools.js +0 -231
  475. package/dist/ai/tools.js.map +0 -7
  476. package/dist/commands/migrate.js +0 -36
  477. package/dist/commands/migrate.js.map +0 -7
  478. package/dist/commands/project.js +0 -74
  479. package/dist/commands/project.js.map +0 -7
  480. package/dist/core/active-changeset.js +0 -52
  481. package/dist/core/active-changeset.js.map +0 -7
  482. package/dist/core/projection-engine.js +0 -288
  483. package/dist/core/projection-engine.js.map +0 -7
  484. package/dist/schemas/bundled/01-motivation-layer.schema.json +0 -942
  485. package/dist/schemas/bundled/02-business-layer.schema.json +0 -1293
  486. package/dist/schemas/bundled/03-security-layer.schema.json +0 -2177
  487. package/dist/schemas/bundled/04-application-layer.schema.json +0 -1328
  488. package/dist/schemas/bundled/05-technology-layer.schema.json +0 -1465
  489. package/dist/schemas/bundled/06-api-layer.schema.json +0 -1743
  490. package/dist/schemas/bundled/07-data-model-layer.schema.json +0 -1253
  491. package/dist/schemas/bundled/08-datastore-layer.schema.json +0 -892
  492. package/dist/schemas/bundled/09-ux-layer.schema.json +0 -1506
  493. package/dist/schemas/bundled/10-navigation-layer.schema.json +0 -843
  494. package/dist/schemas/bundled/11-apm-observability-layer.schema.json +0 -1430
  495. package/dist/schemas/bundled/12-testing-layer.schema.json +0 -1091
  496. package/dist/schemas/bundled/common/layer-extensions.schema.json +0 -266
  497. package/dist/schemas/bundled/common/predicates.schema.json +0 -493
  498. package/dist/schemas/bundled/common/relationships.schema.json +0 -791
  499. package/dist/schemas/bundled/common/source-references.schema.json +0 -71
  500. package/dist/schemas/bundled/link-registry.json +0 -1839
  501. package/dist/schemas/bundled/link-registry.schema.json +0 -339
  502. package/dist/schemas/bundled/relationship-catalog.json +0 -1302
  503. package/dist/schemas/bundled/relationship-type.schema.json +0 -221
  504. package/dist/utils/model-path.js +0 -22
  505. package/dist/utils/model-path.js.map +0 -7
  506. package/docs/TEST-INSTRUMENTATION.md +0 -405
  507. package/docs/telemetry.md +0 -427
  508. package/test-debug.js +0 -33
  509. package/test-special.mjs +0 -46
  510. package/test_runner.ts +0 -7
@@ -1,138 +1,209 @@
1
1
  import { Layer } from "./layer.js";
2
2
  import { Manifest } from "./manifest.js";
3
3
  import { Element } from "./element.js";
4
- import { ProjectionEngine } from "./projection-engine.js";
4
+ import { GraphModel } from "./graph-model.js";
5
+ import { VirtualProjectionEngine } from "./virtual-projection.js";
6
+ import { StagedChangesetStorage } from "./staged-changeset-storage.js";
5
7
  import { Relationships } from "./relationships.js";
6
- import { ActiveChangesetContext } from "./active-changeset.js";
8
+ import { CANONICAL_LAYER_NAMES } from "./layers.js";
7
9
  import { ensureDir, writeFile } from "../utils/file-io.js";
8
- import { startSpan, endSpan } from "../telemetry/index.js";
10
+ import { getCliVersion } from "../utils/spec-version.js";
11
+ import { startSpan, endSpan, startActiveSpan } from "../telemetry/index.js";
9
12
  import { findProjectRoot } from "../utils/project-paths.js";
13
+ import { getNodeType } from "../generated/node-types.js";
10
14
  import path from "node:path";
11
15
  import fs from "node:fs/promises";
16
+ import crypto from "node:crypto";
17
+ function deterministicUUID(path2) {
18
+ const hash = crypto.createHash("sha256").update(path2).digest("hex");
19
+ return [
20
+ hash.slice(0, 8),
21
+ hash.slice(8, 12),
22
+ "4" + hash.slice(13, 16),
23
+ // version 4 marker
24
+ (parseInt(hash.slice(16, 18), 16) & 63 | 128).toString(16) + hash.slice(18, 20),
25
+ // variant
26
+ hash.slice(20, 32)
27
+ ].join("-");
28
+ }
29
+ function titleToSnakeCase(title) {
30
+ return title.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").toLowerCase();
31
+ }
32
+ function typeToSnakeStem(specNodeId, type) {
33
+ if (specNodeId) {
34
+ const nodeType = getNodeType(specNodeId);
35
+ if (nodeType?.title)
36
+ return titleToSnakeCase(nodeType.title);
37
+ }
38
+ return type;
39
+ }
12
40
  const isTelemetryEnabled = !1;
13
41
  class Model {
14
42
  rootPath;
15
43
  manifest;
44
+ graph;
16
45
  layers;
17
46
  relationships;
18
47
  lazyLoad;
19
48
  loadedLayers;
20
- projectionEngine;
21
- activeChangesetContext;
49
+ virtualProjectionEngine;
50
+ /** ID of the currently active changeset, or null if none is active. Set during load(). */
51
+ _activeChangesetId = null;
52
+ /** Layer names that exist only in staged changes (not yet committed to base model). */
53
+ _stagedLayerNames = /* @__PURE__ */ new Set();
22
54
  constructor(rootPath, manifest, options = {}) {
23
- this.rootPath = rootPath, this.manifest = manifest, this.layers = /* @__PURE__ */ new Map(), this.relationships = new Relationships(), this.lazyLoad = options.lazyLoad ?? !1, this.loadedLayers = /* @__PURE__ */ new Set();
55
+ this.rootPath = rootPath, this.manifest = manifest, this.graph = new GraphModel(), this.layers = /* @__PURE__ */ new Map(), this.relationships = new Relationships(), this.lazyLoad = options.lazyLoad ?? !1, this.loadedLayers = /* @__PURE__ */ new Set();
24
56
  }
25
57
  /**
26
- * Get a layer, loading it if lazy loading is enabled
58
+ * Get the committed base layer (no staging projection).
59
+ * Used internally by VirtualProjectionEngine and commit() to access the raw base model
60
+ * without triggering infinite recursion or double-projection.
27
61
  */
28
- async getLayer(name) {
62
+ async getBaseLayer(name) {
29
63
  return this.lazyLoad && !this.loadedLayers.has(name) && await this.loadLayer(name), this.layers.get(name);
30
64
  }
65
+ /**
66
+ * Get a layer, transparently merging staged changes when an active changeset exists.
67
+ * Falls back to the committed base layer if no changeset is active or projection fails.
68
+ */
69
+ async getLayer(name) {
70
+ if (this.lazyLoad && !this.loadedLayers.has(name) && await this.loadLayer(name), this._activeChangesetId)
71
+ try {
72
+ return await this.getVirtualProjectionEngine().projectLayer(this, this._activeChangesetId, name);
73
+ } catch {
74
+ return this.layers.get(name);
75
+ }
76
+ return this.layers.get(name);
77
+ }
31
78
  /**
32
79
  * Get an element by ID across all layers
80
+ * Supports both UUID and semantic ID (layer.type.kebab-name) lookup
33
81
  */
34
82
  getElementById(id) {
35
83
  for (const layer of this.layers.values()) {
36
- const element = layer.elements.get(id);
84
+ const element = layer.getElement(id);
37
85
  if (element)
38
86
  return element;
39
87
  }
40
88
  }
41
89
  /**
42
- * Get projection engine (lazily initialized)
90
+ * Get virtual projection engine (lazily initialized)
43
91
  */
44
- getProjectionEngine() {
45
- return this.projectionEngine || (this.projectionEngine = new ProjectionEngine(this)), this.projectionEngine;
92
+ getVirtualProjectionEngine() {
93
+ return this.virtualProjectionEngine || (this.virtualProjectionEngine = new VirtualProjectionEngine(this.rootPath)), this.virtualProjectionEngine;
46
94
  }
47
95
  /**
48
- * Get active changeset context (lazily initialized)
96
+ * Returns the active changeset ID, or null if no changeset is active.
97
+ * Used by commands that need to project the full model (e.g. validate).
49
98
  */
50
- getActiveChangesetContext() {
51
- return this.activeChangesetContext || (this.activeChangesetContext = new ActiveChangesetContext(this.rootPath)), this.activeChangesetContext;
99
+ getActiveChangesetId() {
100
+ return this._activeChangesetId;
52
101
  }
53
102
  /**
54
- * Load a layer from disk (legacy format: model/XX_layername/*.yaml files)
55
- * Supports reading layer paths from manifest for Python CLI compatibility
103
+ * Load a layer from disk (model/XX_layername/*.yaml files)
56
104
  */
57
105
  async loadLayer(name) {
58
- const layerSpan = isTelemetryEnabled ? startSpan("layer.load", {
59
- "layer.name": name
60
- }) : null;
61
- try {
106
+ await startActiveSpan("layer.load", async (layerSpan) => {
62
107
  const fs2 = await import("fs/promises"), yaml = await import("yaml");
63
108
  let layerPath = null;
64
- if (this.manifest.layers && this.manifest.layers[name]) {
65
- const layerConfig = this.manifest.layers[name];
66
- if (layerConfig.path) {
67
- const fullPath = `${this.rootPath}/${layerConfig.path.replace(/\/$/, "")}`;
68
- try {
69
- await fs2.access(fullPath), layerPath = fullPath;
70
- } catch {
71
- process.env.DEBUG && console.debug(`Manifest specifies path ${layerConfig.path} but it doesn't exist`);
72
- }
73
- }
74
- }
75
- if (!layerPath) {
76
- const modelDir = `${this.rootPath}/documentation-robotics/model`;
77
- try {
78
- const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
79
- layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
80
- } catch {
81
- }
109
+ const modelDir = `${this.rootPath}/documentation-robotics/model`;
110
+ try {
111
+ const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
112
+ layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
113
+ } catch {
82
114
  }
83
115
  if (!layerPath)
84
116
  return;
85
- const layer = new Layer(name), files = await fs2.readdir(layerPath);
117
+ const layer = new Layer(name, this.graph);
118
+ let parseErrorCount = 0;
119
+ const files = await fs2.readdir(layerPath);
86
120
  for (const file of files)
87
121
  if (file.endsWith(".yaml") || file.endsWith(".yml")) {
88
- const filePath = `${layerPath}/${file}`, yamlContent = await fs2.readFile(filePath, "utf-8"), elements = yaml.parse(yamlContent);
89
- if (elements && typeof elements == "object") {
90
- for (const [key, element] of Object.entries(elements))
91
- if (element && typeof element == "object") {
92
- const el = element, elementId = el.id || `${name}.${key}`;
93
- let elementType = el.type;
94
- if (!elementType) {
95
- const stem = file.replace(/\.ya?ml$/, "");
96
- elementType = stem.endsWith("s") ? stem.slice(0, -1) : stem;
122
+ const filePath = `${layerPath}/${file}`;
123
+ try {
124
+ const yamlContent = await fs2.readFile(filePath, "utf-8"), elements = yaml.parse(yamlContent);
125
+ if (elements && typeof elements == "object") {
126
+ for (const [key, element] of Object.entries(elements))
127
+ if (element && typeof element == "object") {
128
+ const el = element, isSlugPattern = /^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$/.test(el.id || ""), isUUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(el.id || "");
129
+ let elementId, elementPath, extractedType;
130
+ if (el.path && isUUIDPattern) {
131
+ elementId = el.id, elementPath = el.path;
132
+ const pathParts = el.path.split(".");
133
+ pathParts.length >= 3 && (extractedType = pathParts[1]);
134
+ } else if (el.path && !isUUIDPattern) {
135
+ elementPath = el.path, elementId = deterministicUUID(el.path);
136
+ const pathParts = el.path.split(".");
137
+ pathParts.length >= 3 && (extractedType = pathParts[1]);
138
+ } else if (isSlugPattern) {
139
+ elementPath = el.id, elementId = deterministicUUID(el.id);
140
+ const parts = el.id.split(".");
141
+ parts.length >= 3 && (extractedType = parts[1]);
142
+ } else if (isUUIDPattern) {
143
+ elementId = el.id;
144
+ const layerId2 = el.layer_id || name, rawType = el.type || "", kebabName = (el.name || key).toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
145
+ elementPath = `${layerId2}.${rawType}.${kebabName}`, extractedType = rawType || void 0;
146
+ } else {
147
+ const layerId2 = el.layer_id || name, rawType = el.type || (typeof el.id == "string" && el.id.includes(".") ? el.id.split(".")[1] : "") || "", kebabName = (el.name || key).toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
148
+ if (elementPath = `${layerId2}.${rawType || "element"}.${kebabName}`, elementId = deterministicUUID(elementPath), rawType)
149
+ extractedType = rawType;
150
+ else if (typeof el.id == "string" && el.id.includes(".")) {
151
+ const parts = el.id.split(".");
152
+ parts.length >= 3 && (extractedType = parts[1]);
153
+ }
154
+ }
155
+ const elementName = el.name || key, elementType = el.type || extractedType, layerId = el.layer_id || name, newElement = new Element({
156
+ id: elementId,
157
+ path: elementPath,
158
+ spec_node_id: el.spec_node_id,
159
+ layer_id: layerId,
160
+ attributes: el.attributes,
161
+ source_reference: el.source_reference,
162
+ metadata: el.metadata,
163
+ name: elementName,
164
+ type: elementType,
165
+ description: el.description || "",
166
+ relationships: el.relationships || [],
167
+ references: el.references || [],
168
+ layer: layerId
169
+ });
170
+ layer.addElement(newElement);
97
171
  }
98
- const newElement = new Element({
99
- id: elementId,
100
- name: el.name || key,
101
- type: elementType,
102
- description: el.description || el.documentation || "",
103
- properties: el.properties || {},
104
- relationships: el.relationships || [],
105
- references: el.references || [],
106
- layer: name,
107
- // Track layer (Python CLI compatibility)
108
- filePath,
109
- // Track source file (Python CLI compatibility)
110
- rawData: el
111
- // Preserve raw YAML data (Python CLI compatibility)
112
- });
113
- layer.addElement(newElement);
114
- }
172
+ }
173
+ } catch (fileError) {
174
+ const errorMsg = fileError instanceof Error ? fileError.message : String(fileError);
175
+ console.error(`Warning: Failed to parse ${filePath}: ${errorMsg}`), parseErrorCount++;
115
176
  }
116
177
  }
117
- this.layers.set(name, layer), this.loadedLayers.add(name), isTelemetryEnabled && layerSpan && layerSpan.setAttribute("layer.element_count", layer.elements.size);
118
- } finally {
119
- isTelemetryEnabled && endSpan(layerSpan);
120
- }
178
+ parseErrorCount > 0 && console.error(`Warning: Layer '${name}' loaded with ${parseErrorCount} YAML parse error(s)`), this.layers.set(name, layer), this.loadedLayers.add(name), layerSpan.setAttribute("layer.element_count", layer.elements.size);
179
+ }, { "layer.name": name });
121
180
  }
122
181
  /**
123
182
  * Add a layer to the model
124
183
  */
125
184
  addLayer(layer) {
126
- this.layers.set(layer.name, layer), this.loadedLayers.add(layer.name);
185
+ if (layer.graph.getNodeCount() > 0) {
186
+ for (const node of layer.graph.nodes.values())
187
+ this.graph.addNode(node);
188
+ for (const edge of layer.graph.edges.values())
189
+ this.graph.addEdge(edge);
190
+ }
191
+ layer.graph = this.graph, this.layers.set(layer.name, layer), this.loadedLayers.add(layer.name);
127
192
  }
128
193
  /**
129
- * Get all loaded layer names
194
+ * Get all layer names, including layers that exist only in staged changes.
195
+ * When an active changeset introduces elements for a layer with no committed data,
196
+ * that layer name is included so findElementLayer() and other callers can discover it.
130
197
  */
131
198
  getLayerNames() {
132
- return Array.from(this.layers.keys());
199
+ const base = Array.from(this.layers.keys());
200
+ if (this._stagedLayerNames.size === 0)
201
+ return base;
202
+ const staged = Array.from(this._stagedLayerNames).filter((l) => !this.layers.has(l));
203
+ return [...base, ...staged];
133
204
  }
134
205
  /**
135
- * Save a layer to disk (legacy format: model/XX_layername/*.yaml)
206
+ * Save a layer to disk (model/XX_layername/*.yaml)
136
207
  */
137
208
  async saveLayer(name) {
138
209
  const layer = this.layers.get(name);
@@ -140,40 +211,14 @@ class Model {
140
211
  throw new Error(`Layer ${name} not found`);
141
212
  const fs2 = await import("fs/promises"), yaml = await import("yaml");
142
213
  let layerPath = null;
143
- if (this.manifest.layers && this.manifest.layers[name]) {
144
- const layerConfig = this.manifest.layers[name];
145
- if (layerConfig.path) {
146
- const fullPath = `${this.rootPath}/${layerConfig.path.replace(/\/$/, "")}`;
147
- try {
148
- await fs2.access(fullPath), layerPath = fullPath;
149
- } catch {
150
- await ensureDir(fullPath), layerPath = fullPath;
151
- }
152
- }
153
- }
154
- if (!layerPath) {
155
- const modelDir = `${this.rootPath}/documentation-robotics/model`;
156
- try {
157
- const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
158
- layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
159
- } catch {
160
- }
214
+ const modelDir = `${this.rootPath}/documentation-robotics/model`;
215
+ try {
216
+ const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
217
+ layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
218
+ } catch {
161
219
  }
162
220
  if (!layerPath) {
163
- const layerNameWithoutPrefix = name.replace(/^\d{2}-/, ""), index = [
164
- "motivation",
165
- "business",
166
- "security",
167
- "application",
168
- "technology",
169
- "api",
170
- "data-model",
171
- "datastore",
172
- "ux",
173
- "navigation",
174
- "apm",
175
- "testing"
176
- ].indexOf(layerNameWithoutPrefix);
221
+ const layerNameWithoutPrefix = name.replace(/^\d{2}-/, ""), index = CANONICAL_LAYER_NAMES.indexOf(layerNameWithoutPrefix);
177
222
  if (index >= 0) {
178
223
  const orderNum = String(index + 1).padStart(2, "0");
179
224
  layerPath = `${this.rootPath}/documentation-robotics/model/${orderNum}_${layerNameWithoutPrefix}`, await ensureDir(layerPath);
@@ -184,24 +229,41 @@ class Model {
184
229
  const existingFiles = await fs2.readdir(layerPath);
185
230
  for (const file of existingFiles)
186
231
  (file.endsWith(".yaml") || file.endsWith(".yml")) && await fs2.unlink(`${layerPath}/${file}`);
187
- } catch {
232
+ } catch (err) {
233
+ if (!(err?.code === "ENOENT"))
234
+ throw new Error(`Failed to clean existing YAML files: ${err instanceof Error ? err.message : String(err)}`);
188
235
  }
189
236
  const elementsByType = /* @__PURE__ */ new Map();
190
237
  for (const element of layer.elements.values()) {
191
- const type = element.type + "s";
192
- elementsByType.has(type) || elementsByType.set(type, []), elementsByType.get(type).push(element);
238
+ const typeKey = element.type;
239
+ if (!elementsByType.has(typeKey)) {
240
+ const stem = typeToSnakeStem(element.spec_node_id, element.type);
241
+ elementsByType.set(typeKey, { stem, elements: [] });
242
+ }
243
+ elementsByType.get(typeKey).elements.push(element);
193
244
  }
194
- for (const [type, elements] of elementsByType) {
195
- const filename = `${type}.yaml`, filePath = `${layerPath}/${filename}`, yamlData = {};
245
+ const INTERNAL_FIELDS = /* @__PURE__ */ new Set([
246
+ "__references__",
247
+ "__relationships__",
248
+ "source",
249
+ "x-source-reference"
250
+ ]);
251
+ for (const [, { stem, elements }] of elementsByType) {
252
+ const filename = `${stem}.yaml`, filePath = `${layerPath}/${filename}`, yamlData = {};
196
253
  for (const element of elements) {
197
- const key = element.id.split(".").pop() || element.id;
254
+ const json = element.toJSON(), cleanAttrs = json.attributes ? Object.fromEntries(Object.entries(json.attributes).filter(([k]) => !INTERNAL_FIELDS.has(k))) : void 0, key = element.path || element.id;
198
255
  yamlData[key] = {
199
- id: element.id,
200
- name: element.name,
201
- type: element.type,
202
- ...element.description && { documentation: element.description },
203
- ...Object.keys(element.properties || {}).length > 0 && { properties: element.properties },
204
- ...element.references && element.references.length > 0 && { references: element.references }
256
+ id: json.id,
257
+ path: json.path,
258
+ spec_node_id: json.spec_node_id,
259
+ type: json.type,
260
+ layer_id: json.layer_id,
261
+ name: json.name,
262
+ ...json.description && { description: json.description },
263
+ ...cleanAttrs && Object.keys(cleanAttrs).length > 0 && { attributes: cleanAttrs },
264
+ ...json.source_reference && { source_reference: json.source_reference },
265
+ ...json.metadata && { metadata: json.metadata },
266
+ ...json.references && json.references.length > 0 && { references: json.references }
205
267
  // Relationships are now stored in centralized relationships.yaml, not inline
206
268
  };
207
269
  }
@@ -234,15 +296,14 @@ class Model {
234
296
  }
235
297
  }
236
298
  /**
237
- * Save the manifest to disk (Python CLI format: documentation-robotics/model/manifest.yaml)
238
- * Preserves Python CLI metadata fields for compatibility
299
+ * Save the manifest to disk (documentation-robotics/model/manifest.yaml)
239
300
  */
240
301
  async saveManifest() {
241
302
  this.manifest.updateModified();
242
303
  const yaml = await import("yaml"), manifestPath = `${this.rootPath}/documentation-robotics/model/manifest.yaml`, yamlData = {
243
304
  version: this.manifest.version,
244
305
  schema: "documentation-robotics-v1",
245
- cli_version: this.manifest.version || "0.1.0",
306
+ cli_version: getCliVersion(),
246
307
  spec_version: this.manifest.specVersion,
247
308
  created: this.manifest.created,
248
309
  updated: this.manifest.modified,
@@ -252,44 +313,32 @@ class Model {
252
313
  version: this.manifest.version
253
314
  },
254
315
  documentation: ".dr/README.md",
255
- layers: this.manifest.layers || {}
256
- };
257
- if (!this.manifest.layers || Object.keys(this.manifest.layers).length === 0) {
258
- const layerOrder = [
259
- "motivation",
260
- "business",
261
- "security",
262
- "application",
263
- "technology",
264
- "api",
265
- "data-model",
266
- "datastore",
267
- "ux",
268
- "navigation",
269
- "apm",
270
- "testing"
271
- ];
272
- for (let i = 0; i < layerOrder.length; i++) {
273
- const layerName = layerOrder[i], layer = this.layers.get(layerName), orderNum = String(i + 1).padStart(2, "0");
274
- yamlData.layers[layerName] = {
275
- order: i + 1,
276
- name: layerName.charAt(0).toUpperCase() + layerName.slice(1).replace("-", " "),
277
- path: `documentation-robotics/model/${orderNum}_${layerName}/`,
278
- schema: `.dr/schemas/${orderNum}-${layerName}-layer.schema.json`,
279
- enabled: !0,
280
- ...layer && { elements: this.getLayerElementCounts(layer) }
281
- };
282
- }
283
- } else
284
- for (const [layerName, layerConfig] of Object.entries(yamlData.layers)) {
285
- const layer = this.layers.get(layerName);
286
- layer && (layerConfig.elements = this.getLayerElementCounts(layer));
287
- }
288
- if (this.manifest.statistics) {
289
- const stats = { ...this.manifest.statistics };
290
- stats.total_elements = Array.from(this.layers.values()).reduce((sum, layer) => sum + layer.elements.size, 0), yamlData.statistics = stats;
316
+ layers: {}
317
+ }, layerOrder = [
318
+ "motivation",
319
+ "business",
320
+ "security",
321
+ "application",
322
+ "technology",
323
+ "api",
324
+ "data-model",
325
+ "data-store",
326
+ "ux",
327
+ "navigation",
328
+ "apm",
329
+ "testing"
330
+ ];
331
+ for (let i = 0; i < layerOrder.length; i++) {
332
+ const layerName = layerOrder[i], layer = this.layers.get(layerName), orderNum = String(i + 1).padStart(2, "0");
333
+ yamlData.layers[layerName] = {
334
+ order: i + 1,
335
+ name: layerName.charAt(0).toUpperCase() + layerName.slice(1).replace("-", " "),
336
+ path: `documentation-robotics/model/${orderNum}_${layerName}/`,
337
+ enabled: !0,
338
+ ...layer && { elements: this.getLayerElementCounts(layer) }
339
+ };
291
340
  }
292
- this.manifest.cross_references && (yamlData.cross_references = this.manifest.cross_references), this.manifest.conventions && (yamlData.conventions = this.manifest.conventions), this.manifest.upgrade_history && (yamlData.upgrade_history = this.manifest.upgrade_history), this.manifest.changeset_history && this.manifest.changeset_history.length > 0 && (yamlData.changeset_history = this.manifest.changeset_history), await ensureDir(`${this.rootPath}/documentation-robotics/model`), await writeFile(manifestPath, yaml.stringify(yamlData));
341
+ this.manifest.changeset_history && this.manifest.changeset_history.length > 0 && (yamlData.changeset_history = this.manifest.changeset_history), await ensureDir(`${this.rootPath}/documentation-robotics/model`), await writeFile(manifestPath, yaml.stringify(yamlData));
293
342
  }
294
343
  getLayerElementCounts(layer) {
295
344
  const counts = {};
@@ -302,11 +351,33 @@ class Model {
302
351
  */
303
352
  async loadRelationships() {
304
353
  const fs2 = await import("fs/promises"), yaml = await import("yaml"), relationshipsPath = `${this.rootPath}/documentation-robotics/model/relationships.yaml`;
354
+ let data;
305
355
  try {
306
- const yamlContent = await fs2.readFile(relationshipsPath, "utf-8"), data = yaml.parse(yamlContent);
307
- Array.isArray(data) && (this.relationships = Relationships.fromArray(data));
356
+ const yamlContent = await fs2.readFile(relationshipsPath, "utf-8");
357
+ data = yaml.parse(yamlContent);
308
358
  } catch (err) {
309
- err.code !== "ENOENT" && process.env.DEBUG && console.debug(`Warning: Failed to load relationships.yaml: ${err}`);
359
+ if (err.code !== "ENOENT")
360
+ throw new Error(`Failed to load relationships.yaml: ${err instanceof Error ? err.message : String(err)}`);
361
+ return;
362
+ }
363
+ if (!Array.isArray(data)) {
364
+ console.warn(`Warning: relationships.yaml contains non-array content (received ${data === null ? "null" : typeof data}). Expected an array of relationships. Skipping relationship loading.`);
365
+ return;
366
+ }
367
+ this.relationships = Relationships.fromArray(data);
368
+ for (let i = 0; i < data.length; i++) {
369
+ const rel = data[i], edge = {
370
+ id: `rel-${i}-${crypto.randomBytes(4).toString("hex")}`,
371
+ source: rel.source,
372
+ destination: rel.target,
373
+ predicate: rel.predicate,
374
+ properties: rel.properties,
375
+ category: rel.category
376
+ };
377
+ try {
378
+ this.graph.addEdge(edge);
379
+ } catch {
380
+ }
310
381
  }
311
382
  }
312
383
  /**
@@ -366,88 +437,224 @@ Run "dr init" to create a model.`);
366
437
  * @returns Loaded Model instance
367
438
  */
368
439
  static async load(startPath, options = {}) {
369
- const span = isTelemetryEnabled ? startSpan("model.load", {
370
- "model.path": startPath || process.cwd(),
371
- "model.type": "dr-legacy"
372
- }) : null;
373
- try {
374
- const { projectRoot, manifestPath } = await Model.resolveModelPaths(startPath), yaml = await import("yaml"), yamlContent = await fs.readFile(manifestPath, "utf-8"), legacyData = yaml.parse(yamlContent), manifestData = {
375
- name: legacyData.project?.name || "Unnamed Model",
376
- description: legacyData.project?.description || "",
377
- version: legacyData.project?.version || legacyData.version || "0.1.0",
378
- specVersion: legacyData.spec_version || "0.6.0",
379
- created: legacyData.created || (/* @__PURE__ */ new Date()).toISOString(),
380
- modified: legacyData.updated || (/* @__PURE__ */ new Date()).toISOString(),
381
- // Preserve Python CLI metadata fields
382
- layers: legacyData.layers,
383
- statistics: legacyData.statistics || {
384
- total_elements: 0,
385
- total_relationships: 0,
386
- completeness: 0,
387
- last_validation: null,
388
- validation_status: "not_validated"
389
- },
390
- cross_references: legacyData.cross_references || {
391
- total: 0,
392
- by_type: {}
393
- },
394
- conventions: legacyData.conventions,
395
- upgrade_history: legacyData.upgrade_history || []
440
+ return startActiveSpan("model.load", async (span) => {
441
+ const { projectRoot, manifestPath } = await Model.resolveModelPaths(startPath), yaml = await import("yaml"), yamlContent = await fs.readFile(manifestPath, "utf-8"), manifestYaml = yaml.parse(yamlContent), manifestData = {
442
+ name: manifestYaml.project?.name || "Unnamed Model",
443
+ description: manifestYaml.project?.description || "",
444
+ version: manifestYaml.project?.version || manifestYaml.version || "0.1.0",
445
+ specVersion: manifestYaml.spec_version || "0.6.0",
446
+ created: manifestYaml.created || (/* @__PURE__ */ new Date()).toISOString(),
447
+ modified: manifestYaml.updated || (/* @__PURE__ */ new Date()).toISOString()
396
448
  }, manifest = new Manifest(manifestData), model = new Model(projectRoot, manifest, options);
397
449
  if (!options.lazyLoad)
398
450
  try {
399
- if (legacyData.layers)
400
- for (const layerName of Object.keys(legacyData.layers))
401
- legacyData.layers[layerName].enabled !== !1 && await model.loadLayer(layerName);
451
+ if (manifestYaml.layers)
452
+ for (const layerName of Object.keys(manifestYaml.layers))
453
+ manifestYaml.layers[layerName].enabled !== !1 && (!options.layers || options.layers.includes(layerName)) && await model.loadLayer(layerName);
402
454
  else {
403
455
  const modelDir = manifestPath.replace("/manifest.yaml", ""), entries = await fs.readdir(modelDir, { withFileTypes: !0 });
404
456
  for (const entry of entries)
405
457
  if (entry.isDirectory() && entry.name.match(/^\d{2}_/)) {
406
458
  const layerName = entry.name.replace(/^\d{2}_/, "");
407
- await model.loadLayer(layerName);
459
+ (!options.layers || options.layers.includes(layerName)) && await model.loadLayer(layerName);
408
460
  }
409
461
  }
410
462
  } catch (e) {
411
- if (process.env.DEBUG) {
412
- const error = e instanceof Error ? e.message : String(e);
413
- error.includes("ENOENT") || console.debug(`Warning: Failed to load layers directory: ${error}`);
414
- }
463
+ if (!(e?.code === "ENOENT"))
464
+ throw new Error(`Failed to load layers: ${e instanceof Error ? e.message : String(e)}`);
415
465
  }
416
466
  await model.loadRelationships();
467
+ const activePath = path.join(projectRoot, "documentation-robotics", "changesets", ".active");
468
+ try {
469
+ const activeId = (await fs.readFile(activePath, "utf-8")).trim();
470
+ if (activeId) {
471
+ model._activeChangesetId = activeId;
472
+ const changeset = await new StagedChangesetStorage(projectRoot).load(activeId);
473
+ if (changeset)
474
+ for (const change of changeset.changes) {
475
+ if ((change.type === "add" || change.type === "update" || change.type === "delete") && model._stagedLayerNames.add(change.layerName), change.type === "relationship-add" && change.after) {
476
+ const rel = change.after;
477
+ model.relationships.find(rel.source, rel.target, rel.predicate).length === 0 && model.relationships.add({
478
+ source: rel.source,
479
+ target: rel.target,
480
+ predicate: rel.predicate,
481
+ layer: rel.layer,
482
+ ...rel.targetLayer ? { targetLayer: rel.targetLayer } : {},
483
+ category: rel.category ?? "structural",
484
+ ...rel.properties ? { properties: rel.properties } : {}
485
+ });
486
+ }
487
+ if (change.type === "relationship-delete" && change.before) {
488
+ const rel = change.before;
489
+ model.relationships.delete(rel.source, rel.target, rel.predicate);
490
+ }
491
+ }
492
+ }
493
+ } catch {
494
+ model._activeChangesetId = null;
495
+ }
417
496
  let entityCount = 0;
418
497
  for (const layer of model.layers.values())
419
498
  entityCount += layer.elements.size;
420
- return isTelemetryEnabled && span && (span.setAttribute("model.entity_count", entityCount), span.setAttribute("model.layer_count", model.layers.size)), model;
421
- } finally {
422
- isTelemetryEnabled && endSpan(span);
423
- }
499
+ return span.setAttribute("model.entity_count", entityCount), span.setAttribute("model.layer_count", model.layers.size), model;
500
+ }, {
501
+ "model.path": startPath || process.cwd(),
502
+ "model.type": "dr"
503
+ });
424
504
  }
425
505
  /**
426
- * Initialize a new model in a directory (legacy format)
506
+ * Initialize a new model in a directory
427
507
  */
428
508
  static async init(rootPath, manifestData, options = {}) {
429
509
  await ensureDir(`${rootPath}/documentation-robotics/model`);
430
- const layerOrder = [
431
- "motivation",
432
- "business",
433
- "security",
434
- "application",
435
- "technology",
436
- "api",
437
- "data-model",
438
- "datastore",
439
- "ux",
440
- "navigation",
441
- "apm",
442
- "testing"
443
- ];
444
- for (let i = 0; i < layerOrder.length; i++) {
445
- const orderNum = String(i + 1).padStart(2, "0"), layerName = layerOrder[i];
510
+ for (let i = 0; i < CANONICAL_LAYER_NAMES.length; i++) {
511
+ const orderNum = String(i + 1).padStart(2, "0"), layerName = CANONICAL_LAYER_NAMES[i];
446
512
  await ensureDir(`${rootPath}/documentation-robotics/model/${orderNum}_${layerName}`);
447
513
  }
448
514
  const manifest = new Manifest(manifestData), model = new Model(rootPath, manifest, options);
449
515
  return await model.saveManifest(), model;
450
516
  }
517
+ /**
518
+ * Graph-based API methods for querying the model
519
+ * These methods delegate to the underlying GraphModel for efficient queries
520
+ */
521
+ /**
522
+ * Get a node by its UUID
523
+ */
524
+ getNode(id) {
525
+ return this.graph.getNode(id);
526
+ }
527
+ /**
528
+ * Get all nodes in a specific layer
529
+ */
530
+ getNodesByLayer(layerId) {
531
+ return this.graph.getNodesByLayer(layerId);
532
+ }
533
+ /**
534
+ * Get all nodes of a specific type
535
+ */
536
+ getNodesByType(type) {
537
+ return this.graph.getNodesByType(type);
538
+ }
539
+ /**
540
+ * Get all edges (relationships) in the model
541
+ */
542
+ getAllEdges() {
543
+ return this.graph.getAllEdges();
544
+ }
545
+ /**
546
+ * Get edges originating from a specific node
547
+ */
548
+ getEdgesFrom(nodeId, predicate) {
549
+ return this.graph.getEdgesFrom(nodeId, predicate);
550
+ }
551
+ /**
552
+ * Get edges terminating at a specific node
553
+ */
554
+ getEdgesTo(nodeId, predicate) {
555
+ return this.graph.getEdgesTo(nodeId, predicate);
556
+ }
557
+ /**
558
+ * Get edges between two specific nodes
559
+ */
560
+ getEdgesBetween(sourceId, destinationId, predicate) {
561
+ return this.graph.getEdgesBetween(sourceId, destinationId, predicate);
562
+ }
563
+ /**
564
+ * Traverse the graph starting from a node, following a specific predicate
565
+ */
566
+ async traverse(startNodeId, predicate, maxDepth = 10) {
567
+ return this.graph.traverse(startNodeId, predicate, maxDepth);
568
+ }
569
+ /**
570
+ * Create a backup of the current model state
571
+ * Used for rollback support in migration operations
572
+ *
573
+ * @param label Descriptive label for the backup (e.g., "pre-migration")
574
+ * @returns Path to the backup directory
575
+ */
576
+ async createBackup(label) {
577
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"), backupDir = path.join(this.rootPath, ".dr-backups", `${label}-${timestamp}`);
578
+ await ensureDir(backupDir);
579
+ const layersSource = path.join(this.rootPath, "documentation-robotics", "model"), layersBackup = path.join(backupDir, "layers");
580
+ await this.directoryExists(layersSource) && await this.copyDirectory(layersSource, layersBackup);
581
+ const manifestSource = path.join(this.rootPath, "documentation-robotics", "model", "manifest.yaml"), manifestBackup = path.join(backupDir, "manifest.yaml");
582
+ return await this.fileExists(manifestSource) && await fs.copyFile(manifestSource, manifestBackup), backupDir;
583
+ }
584
+ /**
585
+ * Restore model from a backup directory
586
+ * Used for rollback after failed migrations
587
+ *
588
+ * @param backupPath Path to the backup directory
589
+ */
590
+ async restoreFromBackup(backupPath) {
591
+ const layersBackup = path.join(backupPath, "layers"), layersTarget = path.join(this.rootPath, "documentation-robotics", "model");
592
+ await this.directoryExists(layersBackup) && await this.copyDirectory(layersBackup, layersTarget);
593
+ const manifestBackup = path.join(backupPath, "manifest.yaml"), manifestTarget = path.join(this.rootPath, "documentation-robotics", "model", "manifest.yaml");
594
+ await this.fileExists(manifestBackup) && await fs.copyFile(manifestBackup, manifestTarget);
595
+ const reloadedModel = await Model.load(this.rootPath, { lazyLoad: this.lazyLoad });
596
+ this.layers = reloadedModel.layers, this.manifest = reloadedModel.manifest, this.graph = reloadedModel.graph, this.relationships = reloadedModel.relationships;
597
+ }
598
+ /**
599
+ * Save dirty layers with atomic write semantics
600
+ * Writes to temporary file first, then renames on success to ensure data safety
601
+ *
602
+ * @throws Error if atomic write fails
603
+ */
604
+ async saveDirtyLayersAtomic() {
605
+ const tempLayerWrites = [];
606
+ try {
607
+ for (const layer of this.layers.values())
608
+ if (layer.isDirty()) {
609
+ const tempPath = `${path.join(this.rootPath, "documentation-robotics", "model", `${layer.name}_layer`)}.tmp`;
610
+ await ensureDir(tempPath);
611
+ for (const element of layer.elements.values()) {
612
+ const elementPath = path.join(tempPath, `${element.path || element.id}.json`), content = JSON.stringify(element.toJSON(), null, 2);
613
+ await writeFile(elementPath, content);
614
+ }
615
+ tempLayerWrites.push({ layer, tempPath });
616
+ }
617
+ for (const { layer, tempPath } of tempLayerWrites) {
618
+ const layerPath = path.join(this.rootPath, "documentation-robotics", "model", `${layer.name}_layer`);
619
+ await this.directoryExists(layerPath) && await fs.rm(layerPath, { recursive: !0, force: !0 }), await fs.rename(tempPath, layerPath);
620
+ }
621
+ } catch (error) {
622
+ for (const { tempPath } of tempLayerWrites)
623
+ await this.directoryExists(tempPath) && await fs.rm(tempPath, { recursive: !0, force: !0 });
624
+ throw error;
625
+ }
626
+ }
627
+ /**
628
+ * Helper: Check if file exists
629
+ */
630
+ async fileExists(filePath) {
631
+ try {
632
+ return await fs.access(filePath), !0;
633
+ } catch {
634
+ return !1;
635
+ }
636
+ }
637
+ /**
638
+ * Helper: Check if directory exists
639
+ */
640
+ async directoryExists(dirPath) {
641
+ try {
642
+ return (await fs.stat(dirPath)).isDirectory();
643
+ } catch {
644
+ return !1;
645
+ }
646
+ }
647
+ /**
648
+ * Helper: Recursively copy directory
649
+ */
650
+ async copyDirectory(src, dest) {
651
+ await ensureDir(dest);
652
+ const entries = await fs.readdir(src, { withFileTypes: !0 });
653
+ for (const entry of entries) {
654
+ const srcPath = path.join(src, entry.name), destPath = path.join(dest, entry.name);
655
+ entry.isDirectory() ? await this.copyDirectory(srcPath, destPath) : await fs.copyFile(srcPath, destPath);
656
+ }
657
+ }
451
658
  }
452
659
  export {
453
660
  Model