@documentation-robotics/cli 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/README.md +46 -16
  2. package/dist/analysis/relationship-classifier.js +2 -9
  3. package/dist/analysis/relationship-classifier.js.map +2 -2
  4. package/dist/analyzers/active-analyzer.js +62 -0
  5. package/dist/analyzers/active-analyzer.js.map +7 -0
  6. package/dist/analyzers/call-graph-utils.js +2 -2
  7. package/dist/analyzers/call-graph-utils.js.map +2 -2
  8. package/dist/analyzers/cards/build.js +191 -0
  9. package/dist/analyzers/cards/build.js.map +7 -0
  10. package/dist/analyzers/cards/classify.js +42 -0
  11. package/dist/analyzers/cards/classify.js.map +7 -0
  12. package/dist/analyzers/cards/edge-index.js +44 -0
  13. package/dist/analyzers/cards/edge-index.js.map +7 -0
  14. package/dist/analyzers/cards/extract.js +138 -0
  15. package/dist/analyzers/cards/extract.js.map +7 -0
  16. package/dist/analyzers/cards/facts.js +46 -0
  17. package/dist/analyzers/cards/facts.js.map +7 -0
  18. package/dist/analyzers/cards/graph.js +42 -0
  19. package/dist/analyzers/cards/graph.js.map +7 -0
  20. package/dist/analyzers/cards/index.js +8 -0
  21. package/dist/analyzers/cards/index.js.map +7 -0
  22. package/dist/analyzers/cards/routes.js +96 -0
  23. package/dist/analyzers/cards/routes.js.map +7 -0
  24. package/dist/analyzers/cards/tier0.js +18 -0
  25. package/dist/analyzers/cards/tier0.js.map +7 -0
  26. package/dist/analyzers/cards/types.js +1 -0
  27. package/dist/analyzers/cards/types.js.map +7 -0
  28. package/dist/analyzers/cbm-analyzer.js +270 -174
  29. package/dist/analyzers/cbm-analyzer.js.map +2 -2
  30. package/dist/analyzers/cbm-responses.js +342 -0
  31. package/dist/analyzers/cbm-responses.js.map +7 -0
  32. package/dist/analyzers/code-changes.js +159 -0
  33. package/dist/analyzers/code-changes.js.map +7 -0
  34. package/dist/analyzers/edge-relationships.js +49 -0
  35. package/dist/analyzers/edge-relationships.js.map +7 -0
  36. package/dist/analyzers/extraction-plan.js +541 -0
  37. package/dist/analyzers/extraction-plan.js.map +7 -0
  38. package/dist/analyzers/index-job.js +229 -0
  39. package/dist/analyzers/index-job.js.map +7 -0
  40. package/dist/analyzers/mapping-loader.js +9 -0
  41. package/dist/analyzers/mapping-loader.js.map +2 -2
  42. package/dist/analyzers/tier0-rules.js +153 -0
  43. package/dist/analyzers/tier0-rules.js.map +7 -0
  44. package/dist/analyzers/types.js.map +1 -1
  45. package/dist/audit/relationships/ai/prompts.js +10 -10
  46. package/dist/audit/relationships/ai/prompts.js.map +2 -2
  47. package/dist/audit/relationships/analysis/layer-templates.js +52 -52
  48. package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
  49. package/dist/cli.js +23 -11
  50. package/dist/cli.js.map +2 -2
  51. package/dist/coding-agents/default-permissions.js +31 -1
  52. package/dist/coding-agents/default-permissions.js.map +2 -2
  53. package/dist/coding-agents/drbot-mcp.js +269 -0
  54. package/dist/coding-agents/drbot-mcp.js.map +7 -0
  55. package/dist/commands/add.js +7 -4
  56. package/dist/commands/add.js.map +2 -2
  57. package/dist/commands/analyzer.js +143 -40
  58. package/dist/commands/analyzer.js.map +2 -2
  59. package/dist/commands/changeset-verify.js +63 -0
  60. package/dist/commands/changeset-verify.js.map +7 -0
  61. package/dist/commands/changeset.js +80 -29
  62. package/dist/commands/changeset.js.map +2 -2
  63. package/dist/commands/delete.js +10 -4
  64. package/dist/commands/delete.js.map +2 -2
  65. package/dist/commands/farm.js +176 -60
  66. package/dist/commands/farm.js.map +2 -2
  67. package/dist/commands/init.js +9 -26
  68. package/dist/commands/init.js.map +2 -2
  69. package/dist/commands/mcp.js +40 -21
  70. package/dist/commands/mcp.js.map +3 -3
  71. package/dist/commands/relationship.js +208 -37
  72. package/dist/commands/relationship.js.map +2 -2
  73. package/dist/commands/reports.js +3 -3
  74. package/dist/commands/reports.js.map +2 -2
  75. package/dist/commands/update.js +52 -32
  76. package/dist/commands/update.js.map +2 -2
  77. package/dist/commands/upgrade.js +99 -103
  78. package/dist/commands/upgrade.js.map +3 -3
  79. package/dist/commands/validate.js +19 -3
  80. package/dist/commands/validate.js.map +2 -2
  81. package/dist/commands/version.js +1 -1
  82. package/dist/commands/visualize.js +34 -15
  83. package/dist/commands/visualize.js.map +2 -2
  84. package/dist/core/annotation-store.js +15 -5
  85. package/dist/core/annotation-store.js.map +2 -2
  86. package/dist/core/catalog-migration.js +34 -0
  87. package/dist/core/catalog-migration.js.map +7 -0
  88. package/dist/core/changeset-analysis.js +421 -0
  89. package/dist/core/changeset-analysis.js.map +7 -0
  90. package/dist/core/changeset-base-index.js +160 -0
  91. package/dist/core/changeset-base-index.js.map +7 -0
  92. package/dist/core/changeset-brief-compact.js +256 -0
  93. package/dist/core/changeset-brief-compact.js.map +7 -0
  94. package/dist/core/changeset-brief-delta.js +102 -0
  95. package/dist/core/changeset-brief-delta.js.map +7 -0
  96. package/dist/core/changeset-graph.js +281 -0
  97. package/dist/core/changeset-graph.js.map +7 -0
  98. package/dist/core/changeset-only-guard.js +32 -0
  99. package/dist/core/changeset-only-guard.js.map +7 -0
  100. package/dist/core/changeset-summary.js +68 -0
  101. package/dist/core/changeset-summary.js.map +7 -0
  102. package/dist/core/changeset-validator.js +66 -20
  103. package/dist/core/changeset-validator.js.map +2 -2
  104. package/dist/core/changeset-verify.js +848 -0
  105. package/dist/core/changeset-verify.js.map +7 -0
  106. package/dist/core/changeset.js +56 -9
  107. package/dist/core/changeset.js.map +2 -2
  108. package/dist/core/element-type-migration.js +145 -0
  109. package/dist/core/element-type-migration.js.map +7 -0
  110. package/dist/core/extraction-observability.js +161 -0
  111. package/dist/core/extraction-observability.js.map +7 -0
  112. package/dist/core/farm-manifest.js +12 -6
  113. package/dist/core/farm-manifest.js.map +2 -2
  114. package/dist/core/farm-sync-engine.js +1 -1
  115. package/dist/core/farm-sync-engine.js.map +2 -2
  116. package/dist/core/farm-sync-state.js +10 -2
  117. package/dist/core/farm-sync-state.js.map +2 -2
  118. package/dist/core/migration-registry.js +88 -47
  119. package/dist/core/migration-registry.js.map +2 -2
  120. package/dist/core/model-upgrade.js +102 -0
  121. package/dist/core/model-upgrade.js.map +7 -0
  122. package/dist/core/model.js +82 -30
  123. package/dist/core/model.js.map +2 -2
  124. package/dist/core/mutation-handler.js +71 -6
  125. package/dist/core/mutation-handler.js.map +2 -2
  126. package/dist/core/project-init.js +53 -0
  127. package/dist/core/project-init.js.map +7 -0
  128. package/dist/core/relationship-migration-table.js +6639 -0
  129. package/dist/core/relationship-migration-table.js.map +7 -0
  130. package/dist/core/relationship-migration.js +272 -0
  131. package/dist/core/relationship-migration.js.map +7 -0
  132. package/dist/core/relationship-prune.js +49 -0
  133. package/dist/core/relationship-prune.js.map +7 -0
  134. package/dist/core/relationship-staging.js +161 -0
  135. package/dist/core/relationship-staging.js.map +7 -0
  136. package/dist/core/relationships.js +26 -4
  137. package/dist/core/relationships.js.map +2 -2
  138. package/dist/core/report-data-model.js +7 -13
  139. package/dist/core/report-data-model.js.map +2 -2
  140. package/dist/core/staged-changeset-storage.js +145 -18
  141. package/dist/core/staged-changeset-storage.js.map +2 -2
  142. package/dist/core/staged-source-reference.js +28 -0
  143. package/dist/core/staged-source-reference.js.map +7 -0
  144. package/dist/core/staging-area.js +166 -24
  145. package/dist/core/staging-area.js.map +2 -2
  146. package/dist/core/upgrade-plan.js +117 -0
  147. package/dist/core/upgrade-plan.js.map +7 -0
  148. package/dist/core/virtual-projection.js +13 -5
  149. package/dist/core/virtual-projection.js.map +2 -2
  150. package/dist/export/archimate-exporter.js +1 -0
  151. package/dist/export/archimate-exporter.js.map +2 -2
  152. package/dist/generated/compiled-validators.js +2 -2
  153. package/dist/generated/compiled-validators.js.map +2 -2
  154. package/dist/generated/layer-registry.js +16 -16
  155. package/dist/generated/layer-registry.js.map +2 -2
  156. package/dist/generated/node-types.js +186 -168
  157. package/dist/generated/node-types.js.map +2 -2
  158. package/dist/generated/relationship-index.js +2468 -7913
  159. package/dist/generated/relationship-index.js.map +2 -2
  160. package/dist/integrations/claude_code/.claude-plugin/plugin.json +24 -0
  161. package/dist/integrations/claude_code/agents/dr-architect.md +30 -3
  162. package/dist/integrations/claude_code/agents/dr-extractor.md +8 -2
  163. package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
  164. package/dist/integrations/claude_code/commands/dr-changeset.md +2 -1
  165. package/dist/integrations/claude_code/commands/dr-map.md +20 -1
  166. package/dist/integrations/claude_code/commands/dr-plan.md +66 -0
  167. package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
  168. package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
  169. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
  170. package/dist/integrations/claude_code/docs/MCP_SERVER.md +47 -26
  171. package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
  172. package/dist/integrations/claude_code/docs/README.md +68 -2
  173. package/dist/integrations/claude_code/plugin-agents/dr-advisor.md +255 -0
  174. package/dist/integrations/claude_code/plugin-agents/dr-architect.md +306 -0
  175. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
  176. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
  177. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
  178. package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
  179. package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
  180. package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
  181. package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
  182. package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
  183. package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
  184. package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
  185. package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
  186. package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
  187. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +95 -15
  188. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +178 -0
  189. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +8 -0
  190. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +6 -1
  191. package/dist/integrations/github_copilot/agents/dr-architect.md +3 -1
  192. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
  193. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
  194. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
  195. package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
  196. package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
  197. package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
  198. package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
  199. package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
  200. package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
  201. package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
  202. package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
  203. package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
  204. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +225 -102
  205. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +177 -0
  206. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
  207. package/dist/integrations/hash-utils.js.map +2 -2
  208. package/dist/mcp/api-key-manager.js +14 -1
  209. package/dist/mcp/api-key-manager.js.map +2 -2
  210. package/dist/mcp/changeset-only.js +45 -0
  211. package/dist/mcp/changeset-only.js.map +7 -0
  212. package/dist/mcp/project-root.js +85 -0
  213. package/dist/mcp/project-root.js.map +7 -0
  214. package/dist/mcp/server-instructions.js +27 -0
  215. package/dist/mcp/server-instructions.js.map +7 -0
  216. package/dist/mcp/strict-schema.js +52 -0
  217. package/dist/mcp/strict-schema.js.map +7 -0
  218. package/dist/mcp/tool-registry.js +55 -5
  219. package/dist/mcp/tool-registry.js.map +2 -2
  220. package/dist/mcp/tools/changeset-analysis-tools.js +98 -0
  221. package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
  222. package/dist/mcp/tools/changeset-commit.js +224 -0
  223. package/dist/mcp/tools/changeset-commit.js.map +7 -0
  224. package/dist/mcp/tools/changeset-editing.js +260 -0
  225. package/dist/mcp/tools/changeset-editing.js.map +7 -0
  226. package/dist/mcp/tools/changeset-lifecycle.js +83 -0
  227. package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
  228. package/dist/mcp/tools/changeset-shared.js +31 -0
  229. package/dist/mcp/tools/changeset-shared.js.map +7 -0
  230. package/dist/mcp/tools/changeset-show.js +17 -1
  231. package/dist/mcp/tools/changeset-show.js.map +2 -2
  232. package/dist/mcp/tools/changeset-verify.js +49 -0
  233. package/dist/mcp/tools/changeset-verify.js.map +7 -0
  234. package/dist/mcp/tools/codebase-extract.js +836 -0
  235. package/dist/mcp/tools/codebase-extract.js.map +7 -0
  236. package/dist/mcp/tools/codebase-index.js +125 -0
  237. package/dist/mcp/tools/codebase-index.js.map +7 -0
  238. package/dist/mcp/tools/codebase-shared.js +265 -0
  239. package/dist/mcp/tools/codebase-shared.js.map +7 -0
  240. package/dist/mcp/tools/codebase-verify.js +48 -0
  241. package/dist/mcp/tools/codebase-verify.js.map +7 -0
  242. package/dist/mcp/tools/index.js +34 -1
  243. package/dist/mcp/tools/index.js.map +2 -2
  244. package/dist/mcp/tools/model-add.js +191 -74
  245. package/dist/mcp/tools/model-add.js.map +2 -2
  246. package/dist/mcp/tools/model-delete.js +2 -2
  247. package/dist/mcp/tools/model-delete.js.map +2 -2
  248. package/dist/mcp/tools/model-search.js +14 -0
  249. package/dist/mcp/tools/model-search.js.map +2 -2
  250. package/dist/mcp/tools/model-show.js +29 -2
  251. package/dist/mcp/tools/model-show.js.map +2 -2
  252. package/dist/mcp/tools/model-trace.js +93 -30
  253. package/dist/mcp/tools/model-trace.js.map +2 -2
  254. package/dist/mcp/tools/model-update.js +46 -26
  255. package/dist/mcp/tools/model-update.js.map +2 -2
  256. package/dist/mcp/tools/model-validate.js +46 -4
  257. package/dist/mcp/tools/model-validate.js.map +2 -2
  258. package/dist/mcp/tools/project-status-collectors.js +381 -0
  259. package/dist/mcp/tools/project-status-collectors.js.map +7 -0
  260. package/dist/mcp/tools/project-status.js +237 -0
  261. package/dist/mcp/tools/project-status.js.map +7 -0
  262. package/dist/mcp/tools/project-tools.js +505 -0
  263. package/dist/mcp/tools/project-tools.js.map +7 -0
  264. package/dist/mcp/tools/relationship-tools.js +167 -0
  265. package/dist/mcp/tools/relationship-tools.js.map +7 -0
  266. package/dist/mcp/tools/review-open.js +210 -0
  267. package/dist/mcp/tools/review-open.js.map +7 -0
  268. package/dist/mcp/tools/shared.js +35 -4
  269. package/dist/mcp/tools/shared.js.map +2 -2
  270. package/dist/mcp/tools/spec-type.js +90 -0
  271. package/dist/mcp/tools/spec-type.js.map +7 -0
  272. package/dist/reports/model-report-orchestrator.js +31 -2
  273. package/dist/reports/model-report-orchestrator.js.map +2 -2
  274. package/dist/schemas/bundled/analyzers/cbm.json +1200 -70
  275. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  276. package/dist/schemas/bundled/api.json +1029 -1658
  277. package/dist/schemas/bundled/apm.json +499 -734
  278. package/dist/schemas/bundled/application.json +735 -371
  279. package/dist/schemas/bundled/base.json +3 -220
  280. package/dist/schemas/bundled/business.json +452 -366
  281. package/dist/schemas/bundled/data-model.json +395 -411
  282. package/dist/schemas/bundled/data-store.json +334 -558
  283. package/dist/schemas/bundled/manifest.json +18 -18
  284. package/dist/schemas/bundled/motivation.json +349 -368
  285. package/dist/schemas/bundled/navigation.json +415 -731
  286. package/dist/schemas/bundled/product.json +325 -159
  287. package/dist/schemas/bundled/security.json +864 -1232
  288. package/dist/schemas/bundled/technology.json +574 -703
  289. package/dist/schemas/bundled/testing.json +865 -1646
  290. package/dist/schemas/bundled/ux.json +739 -1352
  291. package/dist/server/schemas.js +8 -0
  292. package/dist/server/schemas.js.map +2 -2
  293. package/dist/server/server.js +686 -231
  294. package/dist/server/server.js.map +3 -3
  295. package/dist/server/viewer-registry.js +264 -0
  296. package/dist/server/viewer-registry.js.map +7 -0
  297. package/dist/server-entry.js +2 -1
  298. package/dist/server-entry.js.map +2 -2
  299. package/dist/telemetry/index.js +1 -1
  300. package/dist/utils/ansi-suppressor.js +6 -8
  301. package/dist/utils/ansi-suppressor.js.map +2 -2
  302. package/dist/utils/attribute-patch.js +41 -0
  303. package/dist/utils/attribute-patch.js.map +7 -0
  304. package/dist/utils/error-hints.js +29 -0
  305. package/dist/utils/error-hints.js.map +7 -0
  306. package/dist/utils/errors.js +9 -3
  307. package/dist/utils/errors.js.map +2 -2
  308. package/dist/utils/id-generator.js +5 -2
  309. package/dist/utils/id-generator.js.map +2 -2
  310. package/dist/utils/project-paths.js +26 -1
  311. package/dist/utils/project-paths.js.map +2 -2
  312. package/dist/utils/source-reference.js +64 -31
  313. package/dist/utils/source-reference.js.map +2 -2
  314. package/dist/utils/spec-version.js +2 -2
  315. package/dist/utils/spec-version.js.map +1 -1
  316. package/dist/validators/composed-reference-validator.js +5 -27
  317. package/dist/validators/composed-reference-validator.js.map +2 -2
  318. package/dist/validators/model-warnings.js +28 -0
  319. package/dist/validators/model-warnings.js.map +7 -0
  320. package/dist/validators/reference-validator.js +0 -7
  321. package/dist/validators/reference-validator.js.map +2 -2
  322. package/dist/validators/relationship-schema-validator.js +3 -3
  323. package/dist/validators/relationship-schema-validator.js.map +2 -2
  324. package/dist/validators/schema-validator.js +27 -9
  325. package/dist/validators/schema-validator.js.map +2 -2
  326. package/dist/validators/types.js.map +1 -1
  327. package/dist/viewer/README.md +6 -6
  328. package/dist/viewer/assets/main-CUOQGGRA.css +1 -0
  329. package/dist/viewer/assets/main-DvH3gYrI.js +84 -0
  330. package/dist/viewer/index.html +2 -2
  331. package/dist/viewer/manifest.json +10 -10
  332. package/dist/viewer/version.json +1 -1
  333. package/package.json +2 -1
  334. package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
  335. package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
@@ -0,0 +1,90 @@
1
+ import { z } from "zod";
2
+ import { SpecDataLoader } from "../../core/spec-loader.js";
3
+ import { getAllLayerIds, isValidLayer } from "../../generated/layer-registry.js";
4
+ import { getNodeType, getNodeTypesForLayer, isValidNodeType, normalizeNodeType } from "../../generated/node-types.js";
5
+ import { RELATIONSHIPS_BY_DESTINATION, RELATIONSHIPS_BY_SOURCE } from "../../generated/relationship-index.js";
6
+ import { CLIError, ErrorCategory, findSimilar, formatValidOptions } from "../../utils/errors.js";
7
+ import { jsonResult, runTool } from "./shared.js";
8
+ const inputSchema = {
9
+ layer: z.string().describe("Canonical layer name, e.g. 'api', 'data-model'."),
10
+ type: z.string().optional().describe("Node type within the layer, e.g. 'operation'. Omit to list the layer's types."),
11
+ predicate: z.string().optional().describe("Only list relationships with this predicate."),
12
+ otherLayer: z.string().optional().describe("Only list relationships whose other end is in this layer.")
13
+ };
14
+ let loaderPromise = null;
15
+ function specLoader() {
16
+ if (!loaderPromise) {
17
+ const loader = new SpecDataLoader();
18
+ loaderPromise = loader.load().then(() => loader), loaderPromise.catch(() => {
19
+ loaderPromise = null;
20
+ });
21
+ }
22
+ return loaderPromise;
23
+ }
24
+ async function specTypeHandler(args) {
25
+ return runTool(async () => {
26
+ if (!isValidLayer(args.layer)) {
27
+ const layers = getAllLayerIds(), similar = findSimilar(args.layer, layers, 3);
28
+ throw new CLIError(`Unknown layer "${args.layer}"`, ErrorCategory.USER, [
29
+ ...similar.length > 0 ? [`Did you mean: ${similar.join(" or ")}?`] : [],
30
+ `Valid layers: ${formatValidOptions(layers)}`
31
+ ]);
32
+ }
33
+ const loader = await specLoader();
34
+ if (!args.type) {
35
+ const layer = loader.getLayer(args.layer);
36
+ return jsonResult({
37
+ layer: args.layer,
38
+ description: layer?.description,
39
+ types: getNodeTypesForLayer(args.layer).map((t) => ({
40
+ type: t.type,
41
+ title: t.title,
42
+ definition: firstSentence(loader.getNodeType(t.specNodeId)?.description),
43
+ requiredAttributes: t.requiredAttributes
44
+ }))
45
+ });
46
+ }
47
+ if (!isValidNodeType(args.layer, args.type)) {
48
+ const names = getNodeTypesForLayer(args.layer).map((t) => t.type), similar = findSimilar(args.type, names, 3);
49
+ throw new CLIError(`Invalid type "${args.type}" for layer "${args.layer}"`, ErrorCategory.USER, [
50
+ ...similar.length > 0 ? [`Did you mean: ${similar.join(" or ")}?`] : [],
51
+ `Valid types for ${args.layer}: ${formatValidOptions(names)}`
52
+ ]);
53
+ }
54
+ const specNodeId = `${args.layer}.${normalizeNodeType(args.layer, args.type)}`, info = getNodeType(specNodeId), spec = loader.getNodeType(specNodeId), keep = (rel, otherEnd) => (!args.predicate || rel.predicate === args.predicate) && (!args.otherLayer || otherEnd.startsWith(`${args.otherLayer}.`)), outgoing = (RELATIONSHIPS_BY_SOURCE.get(specNodeId) ?? []).filter((r) => keep(r, r.destinationSpecNodeId)).map((r) => ({ predicate: r.predicate, target: r.destinationSpecNodeId, ...r.required ? { required: !0 } : {} })), incoming = (RELATIONSHIPS_BY_DESTINATION.get(specNodeId) ?? []).filter((r) => keep(r, r.sourceSpecNodeId)).map((r) => ({ source: r.sourceSpecNodeId, predicate: r.predicate, ...r.required ? { required: !0 } : {} })), attributes = Object.entries(info.attributeConstraints).map(([name, constraint]) => {
55
+ const c = constraint;
56
+ return {
57
+ name,
58
+ required: info.requiredAttributes.includes(name),
59
+ ...c.type ? { type: c.type } : {},
60
+ ...c.enum ? { enum: c.enum } : {},
61
+ ...c.description ? { description: c.description } : {}
62
+ };
63
+ });
64
+ return jsonResult({
65
+ specNodeId,
66
+ title: info.title,
67
+ definition: spec?.description,
68
+ attributes,
69
+ outgoing,
70
+ incoming
71
+ });
72
+ });
73
+ }
74
+ const specTypeTool = {
75
+ name: "spec_type",
76
+ description: "Look up a node type in the spec: its definition, attributes (required ones, enum values) and the relationships the catalog allows from and to it. Omit 'type' to list a layer's types. Use before model_add and relationship_add.",
77
+ inputSchema,
78
+ handler: specTypeHandler
79
+ };
80
+ function firstSentence(text) {
81
+ if (!text)
82
+ return;
83
+ const match = text.match(/^.*?[.!?](\s|$)/);
84
+ return (match ? match[0] : text).trim();
85
+ }
86
+ export {
87
+ specTypeHandler,
88
+ specTypeTool
89
+ };
90
+ //# sourceMappingURL=spec-type.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mcp/tools/spec-type.ts"],
4
+ "sourcesContent": ["/**\n * spec_type \u2014 what the spec says about one node type: its definition, its attributes (required,\n * enums), and every relationship the catalog allows from it and to it.\n *\n * This is the lookup a planner needs before staging an element: model_add fails on a missing\n * required attribute, and relationship_add fails on a triple the catalog does not list.\n */\n\nimport { z } from \"zod\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { SpecDataLoader } from \"../../core/spec-loader.js\";\nimport { getAllLayerIds, isValidLayer } from \"../../generated/layer-registry.js\";\nimport {\n getNodeType,\n getNodeTypesForLayer,\n isValidNodeType,\n normalizeNodeType,\n type SpecNodeId,\n} from \"../../generated/node-types.js\";\nimport { RELATIONSHIPS_BY_DESTINATION, RELATIONSHIPS_BY_SOURCE, type RelationshipSpec } from \"../../generated/relationship-index.js\";\nimport { CLIError, ErrorCategory, findSimilar, formatValidOptions } from \"../../utils/errors.js\";\nimport { jsonResult, runTool, type McpToolDefinition } from \"./shared.js\";\n\nconst inputSchema = {\n layer: z.string().describe(\"Canonical layer name, e.g. 'api', 'data-model'.\"),\n type: z\n .string()\n .optional()\n .describe(\"Node type within the layer, e.g. 'operation'. Omit to list the layer's types.\"),\n predicate: z.string().optional().describe(\"Only list relationships with this predicate.\"),\n otherLayer: z.string().optional().describe(\"Only list relationships whose other end is in this layer.\"),\n};\n\nexport type SpecTypeArgs = z.infer<z.ZodObject<typeof inputSchema>>;\n\nlet loaderPromise: Promise<SpecDataLoader> | null = null;\nfunction specLoader(): Promise<SpecDataLoader> {\n if (!loaderPromise) {\n const loader = new SpecDataLoader();\n loaderPromise = loader.load().then(() => loader);\n loaderPromise.catch(() => {\n loaderPromise = null;\n });\n }\n return loaderPromise;\n}\n\nexport async function specTypeHandler(args: SpecTypeArgs): Promise<CallToolResult> {\n return runTool(async () => {\n if (!isValidLayer(args.layer)) {\n const layers = getAllLayerIds();\n const similar = findSimilar(args.layer, layers, 3);\n throw new CLIError(`Unknown layer \"${args.layer}\"`, ErrorCategory.USER, [\n ...(similar.length > 0 ? [`Did you mean: ${similar.join(\" or \")}?`] : []),\n `Valid layers: ${formatValidOptions(layers)}`,\n ]);\n }\n\n const loader = await specLoader();\n\n if (!args.type) {\n const layer = loader.getLayer(args.layer);\n return jsonResult({\n layer: args.layer,\n description: layer?.description,\n types: getNodeTypesForLayer(args.layer).map((t) => ({\n type: t.type,\n title: t.title,\n definition: firstSentence(loader.getNodeType(t.specNodeId)?.description),\n requiredAttributes: t.requiredAttributes,\n })),\n });\n }\n\n if (!isValidNodeType(args.layer, args.type)) {\n const names = getNodeTypesForLayer(args.layer).map((t) => t.type);\n const similar = findSimilar(args.type, names, 3);\n throw new CLIError(`Invalid type \"${args.type}\" for layer \"${args.layer}\"`, ErrorCategory.USER, [\n ...(similar.length > 0 ? [`Did you mean: ${similar.join(\" or \")}?`] : []),\n `Valid types for ${args.layer}: ${formatValidOptions(names)}`,\n ]);\n }\n\n const specNodeId = `${args.layer}.${normalizeNodeType(args.layer, args.type)}` as SpecNodeId;\n const info = getNodeType(specNodeId)!;\n const spec = loader.getNodeType(specNodeId);\n\n const keep = (rel: RelationshipSpec, otherEnd: string) =>\n (!args.predicate || rel.predicate === args.predicate) &&\n (!args.otherLayer || otherEnd.startsWith(`${args.otherLayer}.`));\n\n const outgoing = (RELATIONSHIPS_BY_SOURCE.get(specNodeId) ?? [])\n .filter((r) => keep(r, r.destinationSpecNodeId))\n .map((r) => ({ predicate: r.predicate, target: r.destinationSpecNodeId, ...(r.required ? { required: true } : {}) }));\n const incoming = (RELATIONSHIPS_BY_DESTINATION.get(specNodeId) ?? [])\n .filter((r) => keep(r, r.sourceSpecNodeId))\n .map((r) => ({ source: r.sourceSpecNodeId, predicate: r.predicate, ...(r.required ? { required: true } : {}) }));\n\n const attributes = Object.entries(info.attributeConstraints).map(([name, constraint]) => {\n const c = constraint as Record<string, unknown>;\n return {\n name,\n required: info.requiredAttributes.includes(name),\n ...(c.type ? { type: c.type } : {}),\n ...(c.enum ? { enum: c.enum } : {}),\n ...(c.description ? { description: c.description } : {}),\n };\n });\n\n return jsonResult({\n specNodeId,\n title: info.title,\n definition: spec?.description,\n attributes,\n outgoing,\n incoming,\n });\n });\n}\n\nexport const specTypeTool: McpToolDefinition<typeof inputSchema> = {\n name: \"spec_type\",\n description:\n \"Look up a node type in the spec: its definition, attributes (required ones, enum values) and the relationships \" +\n \"the catalog allows from and to it. Omit 'type' to list a layer's types. Use before model_add and relationship_add.\",\n inputSchema,\n handler: specTypeHandler,\n};\n\nfunction firstSentence(text: string | undefined): string | undefined {\n if (!text) return undefined;\n const match = text.match(/^.*?[.!?](\\s|$)/);\n return (match ? match[0] : text).trim();\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -23,6 +23,7 @@ class ModelReportOrchestrator {
23
23
  * Invalid layer names in the affected set are skipped with a warning.
24
24
  * Also regenerates the README since it contains model-wide stats that may have changed.
25
25
  * README is generated first so it exists even if a layer report throws.
26
+ * A report whose content is unchanged apart from its timestamp is not rewritten.
26
27
  */
27
28
  async regenerate(affectedLayers) {
28
29
  if (!await this.isInitialized())
@@ -60,6 +61,21 @@ class ModelReportOrchestrator {
60
61
  rel.layer === primaryLayer && rel.targetLayer && isValidLayerName(rel.targetLayer) && affected.add(rel.targetLayer), rel.targetLayer === primaryLayer && isValidLayerName(rel.layer) && affected.add(rel.layer);
61
62
  return affected;
62
63
  }
64
+ /**
65
+ * The layers whose reports a change to these elements can alter: the elements' own layer, and
66
+ * the layer at the other end of each of their relationships (that report lists them).
67
+ * Unlike {@link computeAffectedLayers}, a relationship elsewhere in the layer does not count.
68
+ */
69
+ computeElementAffectedLayers(layerName, elementIds) {
70
+ const affected = /* @__PURE__ */ new Set();
71
+ isValidLayerName(layerName) && affected.add(layerName);
72
+ const ids = new Set(elementIds);
73
+ if (ids.size === 0)
74
+ return affected;
75
+ for (const rel of this.model.relationships.getAll())
76
+ ids.has(rel.source) && rel.targetLayer && isValidLayerName(rel.targetLayer) && affected.add(rel.targetLayer), ids.has(rel.target) && isValidLayerName(rel.layer) && affected.add(rel.layer);
77
+ return affected;
78
+ }
63
79
  /**
64
80
  * Check if all 13 report files exist and are initialized.
65
81
  * Returns false only if files don't exist (ENOENT); propagates other errors like EACCES, EMFILE, EIO.
@@ -101,7 +117,7 @@ class ModelReportOrchestrator {
101
117
  throw new Error(`Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}`, { cause: error });
102
118
  }
103
119
  const filePath = this.getReportFilePath(layerName);
104
- await fs.writeFile(filePath, markdown, "utf-8");
120
+ await writeIfChanged(filePath, markdown);
105
121
  }
106
122
  /**
107
123
  * Generate and write the model README.
@@ -117,8 +133,21 @@ class ModelReportOrchestrator {
117
133
  throw new Error(`Failed to generate README content (programming error or corrupted model) - ${getErrorMessage(error)}`, { cause: error });
118
134
  }
119
135
  const filePath = path.join(this.rootPath, "documentation-robotics", "README.md");
120
- await fs.writeFile(filePath, markdown, "utf-8");
136
+ await writeIfChanged(filePath, markdown);
137
+ }
138
+ }
139
+ const GENERATED_AT = /^Generated: [^|\n]*\|/m, LAST_UPDATED_ROW = /^\|\s*Last Updated\s*\|[^\n]*$/m;
140
+ function withoutTimestamps(markdown) {
141
+ return markdown.replace(GENERATED_AT, "Generated: |").replace(LAST_UPDATED_ROW, "| Last Updated |");
142
+ }
143
+ async function writeIfChanged(filePath, markdown) {
144
+ let current;
145
+ try {
146
+ current = await fs.readFile(filePath, "utf-8");
147
+ } catch {
148
+ current = void 0;
121
149
  }
150
+ current !== void 0 && withoutTimestamps(current) === withoutTimestamps(markdown) || await fs.writeFile(filePath, markdown, "utf-8");
122
151
  }
123
152
  export {
124
153
  ModelReportOrchestrator
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/reports/model-report-orchestrator.ts"],
4
- "sourcesContent": ["/**\n * Model Report Orchestrator\n *\n * Coordinates report generation across all 13 architecture layers.\n * Determines which reports need regeneration and manages the report directory.\n */\n\nimport type { Model } from '../core/model.js';\nimport { ModelReportDataCollector } from './model-report-data.js';\nimport { ModelLayerReportGenerator } from './model-layer-report-generator.js';\nimport { ModelReadmeGenerator } from './model-readme-generator.js';\nimport { CANONICAL_LAYER_NAMES, type CanonicalLayerName, isValidLayerName } from '../core/layers.js';\nimport { getErrorMessage } from '../utils/errors.js';\nimport { getLayerReportFileName } from './model-report-utils.js';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\n\nexport class ModelReportOrchestrator {\n private collector: ModelReportDataCollector;\n private generator: ModelLayerReportGenerator;\n private readmeGenerator: ModelReadmeGenerator;\n\n constructor(private model: Model, private rootPath: string) {\n this.collector = new ModelReportDataCollector();\n const modelVersion = model.manifest.version || 'unknown';\n const generatedAt = new Date().toISOString();\n this.generator = new ModelLayerReportGenerator(modelVersion, generatedAt);\n this.readmeGenerator = new ModelReadmeGenerator(generatedAt);\n }\n\n /**\n * Regenerate reports for a specific set of affected layers.\n * On first invocation or when reports don't exist, regenerates all 13 reports.\n * Invalid layer names in the affected set are skipped with a warning.\n * Also regenerates the README since it contains model-wide stats that may have changed.\n * README is generated first so it exists even if a layer report throws.\n */\n async regenerate(affectedLayers: Set<string>): Promise<void> {\n if (!(await this.isInitialized())) {\n return this.regenerateAll();\n }\n\n // Generate README first so it exists even if a layer report throws\n await this.generateReadme();\n\n for (const layerName of affectedLayers) {\n // Type predicate narrows layerName to CanonicalLayerName automatically\n if (!isValidLayerName(layerName)) {\n console.warn(`Skipping invalid layer name: ${layerName}`);\n continue;\n }\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Regenerate all 13 layer reports and the README.\n * Lets mkdir errors propagate so callers can handle them with proper telemetry.\n * README is generated first so it exists even if a layer report throws.\n */\n async regenerateAll(): Promise<void> {\n // Ensure report directory exists\n const reportDir = this.getReportDir();\n await fs.mkdir(reportDir, { recursive: true });\n\n // Generate README first so it exists even if a layer report throws\n await this.generateReadme();\n\n // Generate all 13 layer reports\n for (const layerName of CANONICAL_LAYER_NAMES) {\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Compute the set of layers affected by a change to a primary layer.\n * Returns the primary layer plus any layers that have cross-layer relationships with it.\n * Throws if the primary layer is not a recognized canonical layer name.\n */\n computeAffectedLayers(primaryLayer: CanonicalLayerName): Set<CanonicalLayerName> {\n // Defense-in-depth: validate despite type signature (guards against as casts from callers)\n if (!isValidLayerName(primaryLayer)) {\n throw new Error(\n `Invalid primary layer name: '${primaryLayer}' is not a recognized canonical layer. ` +\n `Valid layers are: ${CANONICAL_LAYER_NAMES.join(', ')}`\n );\n }\n\n const affected = new Set<CanonicalLayerName>([primaryLayer]);\n\n for (const rel of this.model.relationships.getAll()) {\n // Outbound: this layer references another layer\n if (rel.layer === primaryLayer && rel.targetLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.targetLayer)) {\n affected.add(rel.targetLayer);\n }\n }\n // Inbound: another layer references this layer\n if (rel.targetLayer === primaryLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.layer)) {\n affected.add(rel.layer);\n }\n }\n }\n\n return affected;\n }\n\n /**\n * Check if all 13 report files exist and are initialized.\n * Returns false only if files don't exist (ENOENT); propagates other errors like EACCES, EMFILE, EIO.\n */\n private async isInitialized(): Promise<boolean> {\n for (const layerName of CANONICAL_LAYER_NAMES) {\n try {\n await fs.access(this.getReportFilePath(layerName));\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n // Only ENOENT (file not found) should return false; other errors propagate\n if (err.code === 'ENOENT') {\n return false;\n }\n // Re-throw permission errors, I/O errors, etc.\n throw error;\n }\n }\n return true;\n }\n\n /**\n * Get the reports directory path.\n */\n private getReportDir(): string {\n return path.join(this.rootPath, 'documentation-robotics', 'reports');\n }\n\n /**\n * Get the full file path for a layer report.\n */\n private getReportFilePath(layerName: CanonicalLayerName): string {\n const filename = getLayerReportFileName(layerName);\n return path.join(this.getReportDir(), filename);\n }\n\n /**\n * Generate and write a single layer report.\n * Separates concerns to distinguish programming bugs from I/O errors.\n * Lets generation and write errors propagate so callers can handle them with proper telemetry.\n */\n private async generateLayerReport(layerName: CanonicalLayerName): Promise<void> {\n let data;\n let markdown;\n\n try {\n // Collect layer data (may throw if programming bug or model is corrupted)\n data = this.collector.collectLayerData(this.model, layerName);\n\n // Generate markdown report (may throw if programming bug)\n markdown = this.generator.generate(data);\n } catch (error) {\n throw new Error(\n `Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}`,\n { cause: error }\n );\n }\n\n // Write to file (may throw for I/O errors like ENOSPC, EACCES, EIO)\n // Let write errors propagate so callers can handle them with proper telemetry\n const filePath = this.getReportFilePath(layerName);\n await fs.writeFile(filePath, markdown, 'utf-8');\n }\n\n /**\n * Generate and write the model README.\n * Separates concerns to distinguish programming bugs from I/O errors.\n * Lets generation and write errors propagate so callers can handle them with proper telemetry.\n * README is written to documentation-robotics/README.md (outside reports/ folder).\n */\n private async generateReadme(): Promise<void> {\n let data;\n let markdown;\n\n try {\n // Collect model-wide data (may throw if programming bug or model is corrupted)\n data = this.collector.collectModelData(this.model);\n\n // Generate markdown README (may throw if programming bug)\n markdown = this.readmeGenerator.generate(data);\n } catch (error) {\n throw new Error(\n `Failed to generate README content (programming error or corrupted model) - ${getErrorMessage(error)}`,\n { cause: error }\n );\n }\n\n // Write to file (may throw for I/O errors like ENOSPC, EACCES, EIO)\n // Let write errors propagate so callers can handle them with proper telemetry\n const filePath = path.join(this.rootPath, 'documentation-robotics', 'README.md');\n await fs.writeFile(filePath, markdown, 'utf-8');\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Model Report Orchestrator\n *\n * Coordinates report generation across all 13 architecture layers.\n * Determines which reports need regeneration and manages the report directory.\n */\n\nimport type { Model } from '../core/model.js';\nimport { ModelReportDataCollector } from './model-report-data.js';\nimport { ModelLayerReportGenerator } from './model-layer-report-generator.js';\nimport { ModelReadmeGenerator } from './model-readme-generator.js';\nimport { CANONICAL_LAYER_NAMES, type CanonicalLayerName, isValidLayerName } from '../core/layers.js';\nimport { getErrorMessage } from '../utils/errors.js';\nimport { getLayerReportFileName } from './model-report-utils.js';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\n\nexport class ModelReportOrchestrator {\n private collector: ModelReportDataCollector;\n private generator: ModelLayerReportGenerator;\n private readmeGenerator: ModelReadmeGenerator;\n\n constructor(private model: Model, private rootPath: string) {\n this.collector = new ModelReportDataCollector();\n const modelVersion = model.manifest.version || 'unknown';\n const generatedAt = new Date().toISOString();\n this.generator = new ModelLayerReportGenerator(modelVersion, generatedAt);\n this.readmeGenerator = new ModelReadmeGenerator(generatedAt);\n }\n\n /**\n * Regenerate reports for a specific set of affected layers.\n * On first invocation or when reports don't exist, regenerates all 13 reports.\n * Invalid layer names in the affected set are skipped with a warning.\n * Also regenerates the README since it contains model-wide stats that may have changed.\n * README is generated first so it exists even if a layer report throws.\n * A report whose content is unchanged apart from its timestamp is not rewritten.\n */\n async regenerate(affectedLayers: Set<string>): Promise<void> {\n if (!(await this.isInitialized())) {\n return this.regenerateAll();\n }\n\n // Generate README first so it exists even if a layer report throws\n await this.generateReadme();\n\n for (const layerName of affectedLayers) {\n // Type predicate narrows layerName to CanonicalLayerName automatically\n if (!isValidLayerName(layerName)) {\n console.warn(`Skipping invalid layer name: ${layerName}`);\n continue;\n }\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Regenerate all 13 layer reports and the README.\n * Lets mkdir errors propagate so callers can handle them with proper telemetry.\n * README is generated first so it exists even if a layer report throws.\n */\n async regenerateAll(): Promise<void> {\n // Ensure report directory exists\n const reportDir = this.getReportDir();\n await fs.mkdir(reportDir, { recursive: true });\n\n // Generate README first so it exists even if a layer report throws\n await this.generateReadme();\n\n // Generate all 13 layer reports\n for (const layerName of CANONICAL_LAYER_NAMES) {\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Compute the set of layers affected by a change to a primary layer.\n * Returns the primary layer plus any layers that have cross-layer relationships with it.\n * Throws if the primary layer is not a recognized canonical layer name.\n */\n computeAffectedLayers(primaryLayer: CanonicalLayerName): Set<CanonicalLayerName> {\n // Defense-in-depth: validate despite type signature (guards against as casts from callers)\n if (!isValidLayerName(primaryLayer)) {\n throw new Error(\n `Invalid primary layer name: '${primaryLayer}' is not a recognized canonical layer. ` +\n `Valid layers are: ${CANONICAL_LAYER_NAMES.join(', ')}`\n );\n }\n\n const affected = new Set<CanonicalLayerName>([primaryLayer]);\n\n for (const rel of this.model.relationships.getAll()) {\n // Outbound: this layer references another layer\n if (rel.layer === primaryLayer && rel.targetLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.targetLayer)) {\n affected.add(rel.targetLayer);\n }\n }\n // Inbound: another layer references this layer\n if (rel.targetLayer === primaryLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.layer)) {\n affected.add(rel.layer);\n }\n }\n }\n\n return affected;\n }\n\n /**\n * The layers whose reports a change to these elements can alter: the elements' own layer, and\n * the layer at the other end of each of their relationships (that report lists them).\n * Unlike {@link computeAffectedLayers}, a relationship elsewhere in the layer does not count.\n */\n computeElementAffectedLayers(layerName: string, elementIds: Iterable<string>): Set<CanonicalLayerName> {\n const affected = new Set<CanonicalLayerName>();\n if (isValidLayerName(layerName)) affected.add(layerName);\n const ids = new Set(elementIds);\n if (ids.size === 0) return affected;\n\n for (const rel of this.model.relationships.getAll()) {\n if (ids.has(rel.source) && rel.targetLayer && isValidLayerName(rel.targetLayer)) {\n affected.add(rel.targetLayer);\n }\n if (ids.has(rel.target) && isValidLayerName(rel.layer)) {\n affected.add(rel.layer);\n }\n }\n return affected;\n }\n\n /**\n * Check if all 13 report files exist and are initialized.\n * Returns false only if files don't exist (ENOENT); propagates other errors like EACCES, EMFILE, EIO.\n */\n private async isInitialized(): Promise<boolean> {\n for (const layerName of CANONICAL_LAYER_NAMES) {\n try {\n await fs.access(this.getReportFilePath(layerName));\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n // Only ENOENT (file not found) should return false; other errors propagate\n if (err.code === 'ENOENT') {\n return false;\n }\n // Re-throw permission errors, I/O errors, etc.\n throw error;\n }\n }\n return true;\n }\n\n /**\n * Get the reports directory path.\n */\n private getReportDir(): string {\n return path.join(this.rootPath, 'documentation-robotics', 'reports');\n }\n\n /**\n * Get the full file path for a layer report.\n */\n private getReportFilePath(layerName: CanonicalLayerName): string {\n const filename = getLayerReportFileName(layerName);\n return path.join(this.getReportDir(), filename);\n }\n\n /**\n * Generate and write a single layer report.\n * Separates concerns to distinguish programming bugs from I/O errors.\n * Lets generation and write errors propagate so callers can handle them with proper telemetry.\n */\n private async generateLayerReport(layerName: CanonicalLayerName): Promise<void> {\n let data;\n let markdown;\n\n try {\n // Collect layer data (may throw if programming bug or model is corrupted)\n data = this.collector.collectLayerData(this.model, layerName);\n\n // Generate markdown report (may throw if programming bug)\n markdown = this.generator.generate(data);\n } catch (error) {\n throw new Error(\n `Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}`,\n { cause: error }\n );\n }\n\n // Write to file (may throw for I/O errors like ENOSPC, EACCES, EIO)\n // Let write errors propagate so callers can handle them with proper telemetry\n const filePath = this.getReportFilePath(layerName);\n await writeIfChanged(filePath, markdown);\n }\n\n /**\n * Generate and write the model README.\n * Separates concerns to distinguish programming bugs from I/O errors.\n * Lets generation and write errors propagate so callers can handle them with proper telemetry.\n * README is written to documentation-robotics/README.md (outside reports/ folder).\n */\n private async generateReadme(): Promise<void> {\n let data;\n let markdown;\n\n try {\n // Collect model-wide data (may throw if programming bug or model is corrupted)\n data = this.collector.collectModelData(this.model);\n\n // Generate markdown README (may throw if programming bug)\n markdown = this.readmeGenerator.generate(data);\n } catch (error) {\n throw new Error(\n `Failed to generate README content (programming error or corrupted model) - ${getErrorMessage(error)}`,\n { cause: error }\n );\n }\n\n // Write to file (may throw for I/O errors like ENOSPC, EACCES, EIO)\n // Let write errors propagate so callers can handle them with proper telemetry\n const filePath = path.join(this.rootPath, 'documentation-robotics', 'README.md');\n await writeIfChanged(filePath, markdown);\n }\n}\n\n/**\n * The footer's generation time changes on every generation, and the README's \"Last Updated\" row\n * (the manifest's modified time) on every save: neither is a change to the report's content.\n */\nconst GENERATED_AT = /^Generated: [^|\\n]*\\|/m;\nconst LAST_UPDATED_ROW = /^\\|\\s*Last Updated\\s*\\|[^\\n]*$/m;\n\nfunction withoutTimestamps(markdown: string): string {\n return markdown.replace(GENERATED_AT, 'Generated: |').replace(LAST_UPDATED_ROW, '| Last Updated |');\n}\n\n/**\n * Write a generated report unless the file already holds the same report apart from its\n * timestamps, so regenerating a report nothing changed leaves no diff. A file that cannot be read\n * is written.\n */\nasync function writeIfChanged(filePath: string, markdown: string): Promise<void> {\n let current: string | undefined;\n try {\n current = await fs.readFile(filePath, 'utf-8');\n } catch {\n current = undefined;\n }\n if (current !== undefined && withoutTimestamps(current) === withoutTimestamps(markdown)) {\n return;\n }\n await fs.writeFile(filePath, markdown, 'utf-8');\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }