@documentation-robotics/cli 0.1.13 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/README.md +74 -2
  2. package/dist/analysis/relationship-classifier.js +2 -9
  3. package/dist/analysis/relationship-classifier.js.map +2 -2
  4. package/dist/analyzers/call-graph-utils.js +2 -2
  5. package/dist/analyzers/call-graph-utils.js.map +2 -2
  6. package/dist/analyzers/cards/build.js +171 -0
  7. package/dist/analyzers/cards/build.js.map +7 -0
  8. package/dist/analyzers/cards/classify.js +42 -0
  9. package/dist/analyzers/cards/classify.js.map +7 -0
  10. package/dist/analyzers/cards/edge-index.js +44 -0
  11. package/dist/analyzers/cards/edge-index.js.map +7 -0
  12. package/dist/analyzers/cards/facts.js +46 -0
  13. package/dist/analyzers/cards/facts.js.map +7 -0
  14. package/dist/analyzers/cards/graph.js +42 -0
  15. package/dist/analyzers/cards/graph.js.map +7 -0
  16. package/dist/analyzers/cards/index.js +8 -0
  17. package/dist/analyzers/cards/index.js.map +7 -0
  18. package/dist/analyzers/cards/tier0.js +18 -0
  19. package/dist/analyzers/cards/tier0.js.map +7 -0
  20. package/dist/analyzers/cards/types.js +1 -0
  21. package/dist/analyzers/cards/types.js.map +7 -0
  22. package/dist/analyzers/cbm-analyzer.js +195 -171
  23. package/dist/analyzers/cbm-analyzer.js.map +2 -2
  24. package/dist/analyzers/cbm-responses.js +336 -0
  25. package/dist/analyzers/cbm-responses.js.map +7 -0
  26. package/dist/analyzers/edge-relationships.js +45 -0
  27. package/dist/analyzers/edge-relationships.js.map +7 -0
  28. package/dist/analyzers/mapping-loader.js +9 -0
  29. package/dist/analyzers/mapping-loader.js.map +2 -2
  30. package/dist/analyzers/tier0-rules.js +153 -0
  31. package/dist/analyzers/tier0-rules.js.map +7 -0
  32. package/dist/analyzers/types.js.map +1 -1
  33. package/dist/analyzers/verify-engine.js +22 -19
  34. package/dist/analyzers/verify-engine.js.map +2 -2
  35. package/dist/audit/relationships/ai/prompts.js +10 -10
  36. package/dist/audit/relationships/ai/prompts.js.map +2 -2
  37. package/dist/audit/relationships/analysis/layer-templates.js +52 -52
  38. package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
  39. package/dist/cli.js +112 -28
  40. package/dist/cli.js.map +3 -3
  41. package/dist/coding-agents/agents/claude-code.js +3 -1
  42. package/dist/coding-agents/agents/claude-code.js.map +2 -2
  43. package/dist/coding-agents/claude-code-client.js +2 -1
  44. package/dist/coding-agents/claude-code-client.js.map +2 -2
  45. package/dist/coding-agents/copilot-client.js +17 -12
  46. package/dist/coding-agents/copilot-client.js.map +2 -2
  47. package/dist/coding-agents/default-permissions.js +152 -0
  48. package/dist/coding-agents/default-permissions.js.map +7 -0
  49. package/dist/coding-agents/drbot-mcp.js +167 -0
  50. package/dist/coding-agents/drbot-mcp.js.map +7 -0
  51. package/dist/commands/add.js +12 -7
  52. package/dist/commands/add.js.map +2 -2
  53. package/dist/commands/analyzer.js +134 -23
  54. package/dist/commands/analyzer.js.map +2 -2
  55. package/dist/commands/audit.js +6 -6
  56. package/dist/commands/audit.js.map +2 -2
  57. package/dist/commands/changeset-verify.js +55 -0
  58. package/dist/commands/changeset-verify.js.map +7 -0
  59. package/dist/commands/changeset.js +261 -147
  60. package/dist/commands/changeset.js.map +2 -2
  61. package/dist/commands/delete.js +52 -19
  62. package/dist/commands/delete.js.map +3 -3
  63. package/dist/commands/element.js +3 -2
  64. package/dist/commands/element.js.map +2 -2
  65. package/dist/commands/farm.js +773 -0
  66. package/dist/commands/farm.js.map +7 -0
  67. package/dist/commands/init.js +11 -1
  68. package/dist/commands/init.js.map +2 -2
  69. package/dist/commands/mcp.js +122 -0
  70. package/dist/commands/mcp.js.map +7 -0
  71. package/dist/commands/relationship.js +251 -47
  72. package/dist/commands/relationship.js.map +2 -2
  73. package/dist/commands/reports.js +1 -1
  74. package/dist/commands/reports.js.map +2 -2
  75. package/dist/commands/update.js +56 -24
  76. package/dist/commands/update.js.map +2 -2
  77. package/dist/commands/upgrade.js +99 -11
  78. package/dist/commands/upgrade.js.map +3 -3
  79. package/dist/commands/validate.js +41 -3
  80. package/dist/commands/validate.js.map +2 -2
  81. package/dist/commands/version.js +2 -2
  82. package/dist/commands/version.js.map +2 -2
  83. package/dist/commands/visualize.js +4 -3
  84. package/dist/commands/visualize.js.map +2 -2
  85. package/dist/config.js +83 -0
  86. package/dist/config.js.map +7 -0
  87. package/dist/core/annotation-store.js +138 -0
  88. package/dist/core/annotation-store.js.map +7 -0
  89. package/dist/core/catalog-migration.js +33 -0
  90. package/dist/core/catalog-migration.js.map +7 -0
  91. package/dist/core/changeset-analysis.js +421 -0
  92. package/dist/core/changeset-analysis.js.map +7 -0
  93. package/dist/core/changeset-base-index.js +153 -0
  94. package/dist/core/changeset-base-index.js.map +7 -0
  95. package/dist/core/changeset-brief-delta.js +102 -0
  96. package/dist/core/changeset-brief-delta.js.map +7 -0
  97. package/dist/core/changeset-graph.js +281 -0
  98. package/dist/core/changeset-graph.js.map +7 -0
  99. package/dist/core/changeset-only-guard.js +32 -0
  100. package/dist/core/changeset-only-guard.js.map +7 -0
  101. package/dist/core/changeset-summary.js +68 -0
  102. package/dist/core/changeset-summary.js.map +7 -0
  103. package/dist/core/changeset-validator.js +66 -20
  104. package/dist/core/changeset-validator.js.map +2 -2
  105. package/dist/core/changeset-verify.js +518 -0
  106. package/dist/core/changeset-verify.js.map +7 -0
  107. package/dist/core/changeset.js +56 -9
  108. package/dist/core/changeset.js.map +2 -2
  109. package/dist/core/element-type-migration.js +145 -0
  110. package/dist/core/element-type-migration.js.map +7 -0
  111. package/dist/core/farm-manifest.js +140 -0
  112. package/dist/core/farm-manifest.js.map +7 -0
  113. package/dist/core/farm-sync-engine.js +346 -0
  114. package/dist/core/farm-sync-engine.js.map +7 -0
  115. package/dist/core/farm-sync-state.js +167 -0
  116. package/dist/core/farm-sync-state.js.map +7 -0
  117. package/dist/core/manifest.js +6 -3
  118. package/dist/core/manifest.js.map +2 -2
  119. package/dist/core/migration-registry.js +84 -1
  120. package/dist/core/migration-registry.js.map +2 -2
  121. package/dist/core/model-upgrade.js +102 -0
  122. package/dist/core/model-upgrade.js.map +7 -0
  123. package/dist/core/model.js +257 -44
  124. package/dist/core/model.js.map +3 -3
  125. package/dist/core/mutation-handler.js +71 -6
  126. package/dist/core/mutation-handler.js.map +2 -2
  127. package/dist/core/relationship-migration-table.js +6639 -0
  128. package/dist/core/relationship-migration-table.js.map +7 -0
  129. package/dist/core/relationship-migration.js +272 -0
  130. package/dist/core/relationship-migration.js.map +7 -0
  131. package/dist/core/relationship-prune.js +49 -0
  132. package/dist/core/relationship-prune.js.map +7 -0
  133. package/dist/core/relationship-staging.js +68 -0
  134. package/dist/core/relationship-staging.js.map +7 -0
  135. package/dist/core/relationships.js +26 -4
  136. package/dist/core/relationships.js.map +2 -2
  137. package/dist/core/report-data-model.js +7 -13
  138. package/dist/core/report-data-model.js.map +2 -2
  139. package/dist/core/staged-changeset-storage.js +64 -17
  140. package/dist/core/staged-changeset-storage.js.map +2 -2
  141. package/dist/core/staged-source-reference.js +28 -0
  142. package/dist/core/staged-source-reference.js.map +7 -0
  143. package/dist/core/staging-area.js +83 -18
  144. package/dist/core/staging-area.js.map +2 -2
  145. package/dist/core/virtual-projection.js +14 -5
  146. package/dist/core/virtual-projection.js.map +2 -2
  147. package/dist/export/archimate-exporter.js +1 -0
  148. package/dist/export/archimate-exporter.js.map +2 -2
  149. package/dist/generated/compiled-validators.js +2 -2
  150. package/dist/generated/compiled-validators.js.map +2 -2
  151. package/dist/generated/layer-registry.js +16 -16
  152. package/dist/generated/layer-registry.js.map +2 -2
  153. package/dist/generated/node-types.js +186 -168
  154. package/dist/generated/node-types.js.map +2 -2
  155. package/dist/generated/relationship-index.js +2209 -7727
  156. package/dist/generated/relationship-index.js.map +2 -2
  157. package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
  158. package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
  159. package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
  160. package/dist/integrations/claude_code/commands/dr-map.md +13 -0
  161. package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
  162. package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
  163. package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
  164. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
  165. package/dist/integrations/claude_code/docs/MCP_SERVER.md +79 -0
  166. package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
  167. package/dist/integrations/claude_code/docs/README.md +52 -1
  168. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
  169. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
  170. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
  171. package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
  172. package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
  173. package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
  174. package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
  175. package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
  176. package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
  177. package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
  178. package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
  179. package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
  180. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
  181. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
  182. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
  183. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
  184. package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
  185. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
  186. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
  187. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
  188. package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
  189. package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
  190. package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
  191. package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
  192. package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
  193. package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
  194. package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
  195. package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
  196. package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
  197. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
  198. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
  199. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
  200. package/dist/integrations/hash-utils.js.map +2 -2
  201. package/dist/mcp/api-key-manager.js +111 -0
  202. package/dist/mcp/api-key-manager.js.map +7 -0
  203. package/dist/mcp/changeset-only.js +34 -0
  204. package/dist/mcp/changeset-only.js.map +7 -0
  205. package/dist/mcp/http-transport.js +176 -0
  206. package/dist/mcp/http-transport.js.map +7 -0
  207. package/dist/mcp/resource-registry.js +97 -0
  208. package/dist/mcp/resource-registry.js.map +7 -0
  209. package/dist/mcp/tool-registry.js +60 -0
  210. package/dist/mcp/tool-registry.js.map +7 -0
  211. package/dist/mcp/tools/annotation-create.js +39 -0
  212. package/dist/mcp/tools/annotation-create.js.map +7 -0
  213. package/dist/mcp/tools/annotation-delete.js +29 -0
  214. package/dist/mcp/tools/annotation-delete.js.map +7 -0
  215. package/dist/mcp/tools/annotation-get.js +29 -0
  216. package/dist/mcp/tools/annotation-get.js.map +7 -0
  217. package/dist/mcp/tools/annotation-list.js +24 -0
  218. package/dist/mcp/tools/annotation-list.js.map +7 -0
  219. package/dist/mcp/tools/annotation-reply.js +34 -0
  220. package/dist/mcp/tools/annotation-reply.js.map +7 -0
  221. package/dist/mcp/tools/annotation-shared.js +10 -0
  222. package/dist/mcp/tools/annotation-shared.js.map +7 -0
  223. package/dist/mcp/tools/annotation-update.js +39 -0
  224. package/dist/mcp/tools/annotation-update.js.map +7 -0
  225. package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
  226. package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
  227. package/dist/mcp/tools/changeset-editing.js +184 -0
  228. package/dist/mcp/tools/changeset-editing.js.map +7 -0
  229. package/dist/mcp/tools/changeset-lifecycle.js +83 -0
  230. package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
  231. package/dist/mcp/tools/changeset-list.js +33 -0
  232. package/dist/mcp/tools/changeset-list.js.map +7 -0
  233. package/dist/mcp/tools/changeset-shared.js +31 -0
  234. package/dist/mcp/tools/changeset-shared.js.map +7 -0
  235. package/dist/mcp/tools/changeset-show.js +55 -0
  236. package/dist/mcp/tools/changeset-show.js.map +7 -0
  237. package/dist/mcp/tools/changeset-verify.js +43 -0
  238. package/dist/mcp/tools/changeset-verify.js.map +7 -0
  239. package/dist/mcp/tools/chat-launch.js +50 -0
  240. package/dist/mcp/tools/chat-launch.js.map +7 -0
  241. package/dist/mcp/tools/chat-status.js +26 -0
  242. package/dist/mcp/tools/chat-status.js.map +7 -0
  243. package/dist/mcp/tools/index.js +65 -0
  244. package/dist/mcp/tools/index.js.map +7 -0
  245. package/dist/mcp/tools/model-add.js +107 -0
  246. package/dist/mcp/tools/model-add.js.map +7 -0
  247. package/dist/mcp/tools/model-delete.js +99 -0
  248. package/dist/mcp/tools/model-delete.js.map +7 -0
  249. package/dist/mcp/tools/model-export.js +72 -0
  250. package/dist/mcp/tools/model-export.js.map +7 -0
  251. package/dist/mcp/tools/model-info.js +51 -0
  252. package/dist/mcp/tools/model-info.js.map +7 -0
  253. package/dist/mcp/tools/model-list.js +53 -0
  254. package/dist/mcp/tools/model-list.js.map +7 -0
  255. package/dist/mcp/tools/model-reload.js +34 -0
  256. package/dist/mcp/tools/model-reload.js.map +7 -0
  257. package/dist/mcp/tools/model-search.js +102 -0
  258. package/dist/mcp/tools/model-search.js.map +7 -0
  259. package/dist/mcp/tools/model-show.js +68 -0
  260. package/dist/mcp/tools/model-show.js.map +7 -0
  261. package/dist/mcp/tools/model-stats.js +22 -0
  262. package/dist/mcp/tools/model-stats.js.map +7 -0
  263. package/dist/mcp/tools/model-trace.js +118 -0
  264. package/dist/mcp/tools/model-trace.js.map +7 -0
  265. package/dist/mcp/tools/model-update.js +121 -0
  266. package/dist/mcp/tools/model-update.js.map +7 -0
  267. package/dist/mcp/tools/model-validate.js +78 -0
  268. package/dist/mcp/tools/model-validate.js.map +7 -0
  269. package/dist/mcp/tools/relationship-tools.js +98 -0
  270. package/dist/mcp/tools/relationship-tools.js.map +7 -0
  271. package/dist/mcp/tools/shared.js +126 -0
  272. package/dist/mcp/tools/shared.js.map +7 -0
  273. package/dist/mcp/tools/spec-type.js +90 -0
  274. package/dist/mcp/tools/spec-type.js.map +7 -0
  275. package/dist/reports/model-layer-report-generator.js +5 -3
  276. package/dist/reports/model-layer-report-generator.js.map +2 -2
  277. package/dist/reports/model-readme-generator.js +116 -0
  278. package/dist/reports/model-readme-generator.js.map +7 -0
  279. package/dist/reports/model-report-data.js +37 -1
  280. package/dist/reports/model-report-data.js.map +2 -2
  281. package/dist/reports/model-report-orchestrator.js +29 -6
  282. package/dist/reports/model-report-orchestrator.js.map +2 -2
  283. package/dist/reports/model-report-utils.js +9 -0
  284. package/dist/reports/model-report-utils.js.map +7 -0
  285. package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
  286. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  287. package/dist/schemas/bundled/api.json +1029 -1658
  288. package/dist/schemas/bundled/apm.json +499 -734
  289. package/dist/schemas/bundled/application.json +745 -370
  290. package/dist/schemas/bundled/base.json +3 -220
  291. package/dist/schemas/bundled/business.json +452 -366
  292. package/dist/schemas/bundled/data-model.json +403 -383
  293. package/dist/schemas/bundled/data-store.json +334 -558
  294. package/dist/schemas/bundled/manifest.json +18 -18
  295. package/dist/schemas/bundled/motivation.json +349 -368
  296. package/dist/schemas/bundled/navigation.json +415 -731
  297. package/dist/schemas/bundled/product.json +325 -159
  298. package/dist/schemas/bundled/security.json +864 -1232
  299. package/dist/schemas/bundled/technology.json +574 -703
  300. package/dist/schemas/bundled/testing.json +718 -1643
  301. package/dist/schemas/bundled/ux.json +739 -1352
  302. package/dist/server/schemas.js +8 -0
  303. package/dist/server/schemas.js.map +2 -2
  304. package/dist/server/server.js +234 -31
  305. package/dist/server/server.js.map +2 -2
  306. package/dist/server-entry.js +2 -1
  307. package/dist/server-entry.js.map +2 -2
  308. package/dist/telemetry/config.js +2 -40
  309. package/dist/telemetry/config.js.map +2 -2
  310. package/dist/telemetry/index.js +1 -1
  311. package/dist/types/index.js.map +2 -2
  312. package/dist/utils/ansi-suppressor.js +23 -0
  313. package/dist/utils/ansi-suppressor.js.map +7 -0
  314. package/dist/utils/attribute-patch.js +41 -0
  315. package/dist/utils/attribute-patch.js.map +7 -0
  316. package/dist/utils/color-utils.js +34 -0
  317. package/dist/utils/color-utils.js.map +7 -0
  318. package/dist/utils/element-reference-shape.js +15 -1
  319. package/dist/utils/element-reference-shape.js.map +3 -3
  320. package/dist/utils/error-hints.js +29 -0
  321. package/dist/utils/error-hints.js.map +7 -0
  322. package/dist/utils/errors.js +75 -10
  323. package/dist/utils/errors.js.map +2 -2
  324. package/dist/utils/globals.js +17 -1
  325. package/dist/utils/globals.js.map +2 -2
  326. package/dist/utils/project-paths.js +50 -3
  327. package/dist/utils/project-paths.js.map +2 -2
  328. package/dist/utils/reference-path-parser.js +49 -0
  329. package/dist/utils/reference-path-parser.js.map +7 -0
  330. package/dist/utils/source-reference.js +17 -10
  331. package/dist/utils/source-reference.js.map +2 -2
  332. package/dist/utils/spec-version.js +10 -2
  333. package/dist/utils/spec-version.js.map +2 -2
  334. package/dist/validators/composed-reference-validator.js +240 -0
  335. package/dist/validators/composed-reference-validator.js.map +7 -0
  336. package/dist/validators/reference-validator.js +57 -13
  337. package/dist/validators/reference-validator.js.map +3 -3
  338. package/dist/validators/relationship-schema-validator.js +33 -5
  339. package/dist/validators/relationship-schema-validator.js.map +3 -3
  340. package/dist/validators/schema-validator.js +27 -9
  341. package/dist/validators/schema-validator.js.map +2 -2
  342. package/dist/validators/types.js.map +1 -1
  343. package/dist/viewer/README.md +6 -6
  344. package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
  345. package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
  346. package/dist/viewer/index.html +2 -2
  347. package/dist/viewer/manifest.json +10 -10
  348. package/dist/viewer/version.json +1 -1
  349. package/package.json +7 -3
  350. package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
  351. package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
@@ -0,0 +1,167 @@
1
+ import yaml from "yaml";
2
+ import { readFile, writeFile, fileExists } from "../utils/file-io.js";
3
+ class FarmSyncStateNotFoundError extends Error {
4
+ code = "FARM_SYNC_STATE_NOT_FOUND";
5
+ filePath;
6
+ constructor(filePath) {
7
+ super(`Farm sync state file not found: ${filePath}`), this.name = "FarmSyncStateNotFoundError", this.filePath = filePath;
8
+ }
9
+ }
10
+ class FarmSyncState {
11
+ projectName;
12
+ lastSyncCommit;
13
+ lastSyncTimestamp;
14
+ syncHistory;
15
+ ambiguities;
16
+ filePath;
17
+ constructor(data) {
18
+ this.projectName = data.project_name, this.lastSyncCommit = data.last_sync_commit, this.lastSyncTimestamp = data.last_sync_timestamp, this.syncHistory = data.sync_history || [], this.ambiguities = data.ambiguities?.map((a) => ({
19
+ filePath: a.file_path,
20
+ possibleElements: a.possible_elements.map((e) => ({
21
+ elementId: e.element_id,
22
+ layer: e.layer,
23
+ confidence: e.confidence
24
+ }))
25
+ })) || [];
26
+ }
27
+ /**
28
+ * Load farm sync state from file
29
+ * @param filePath - Path to .farm-sync.yaml
30
+ * @returns FarmSyncState instance
31
+ */
32
+ static async load(filePath) {
33
+ if (!await fileExists(filePath))
34
+ throw new FarmSyncStateNotFoundError(filePath);
35
+ const content = await readFile(filePath), rawData = yaml.parse(content);
36
+ if (!rawData)
37
+ throw new Error(`Invalid farm sync state: file is empty or contains invalid YAML at ${filePath}`);
38
+ const data = {
39
+ project_name: rawData.project_name || rawData.projectName,
40
+ last_sync_commit: rawData.last_sync_commit || rawData.lastSyncCommit,
41
+ last_sync_timestamp: rawData.last_sync_timestamp || rawData.lastSyncTimestamp,
42
+ sync_history: (rawData.sync_history || rawData.syncHistory || []).map((record) => ({
43
+ timestamp: record.timestamp,
44
+ commit: record.commit,
45
+ changeset: record.changeset || record.changesetId,
46
+ files_changed: record.files_changed || record.filesChanged,
47
+ elements_affected: record.elements_affected || record.elementsAffected,
48
+ status: record.status,
49
+ notes: record.notes
50
+ })),
51
+ ambiguities: (rawData.ambiguities || []).map((a) => ({
52
+ file_path: a.file_path || a.filePath,
53
+ possible_elements: (a.possible_elements || a.possibleElements || []).map((e) => ({
54
+ element_id: e.element_id || e.elementId,
55
+ layer: e.layer,
56
+ confidence: e.confidence
57
+ }))
58
+ }))
59
+ };
60
+ if (!data.project_name)
61
+ throw new Error("Farm sync state must have a 'project_name' field");
62
+ const state = new FarmSyncState(data);
63
+ return state.filePath = filePath, state;
64
+ }
65
+ /**
66
+ * Create a new farm sync state
67
+ * @param projectName - Name of the project
68
+ * @param options - Optional initial state
69
+ * @returns FarmSyncState instance
70
+ */
71
+ static create(projectName, options = {}) {
72
+ return new FarmSyncState({
73
+ project_name: projectName,
74
+ last_sync_commit: options.lastSyncCommit,
75
+ last_sync_timestamp: options.lastSyncTimestamp,
76
+ sync_history: [],
77
+ ambiguities: []
78
+ });
79
+ }
80
+ /**
81
+ * Load or create farm sync state
82
+ * @param filePath - Path to .farm-sync.yaml
83
+ * @param projectName - Project name (used if creating new)
84
+ * @returns FarmSyncState instance
85
+ */
86
+ static async loadOrCreate(filePath, projectName) {
87
+ return await fileExists(filePath) ? this.load(filePath) : this.create(projectName);
88
+ }
89
+ /**
90
+ * Save state to file
91
+ * @param filePath - Path where to save .farm-sync.yaml
92
+ */
93
+ async save(filePath) {
94
+ const targetPath = filePath || this.filePath;
95
+ if (!targetPath)
96
+ throw new Error("No file path specified for saving farm sync state");
97
+ const data = this.toJSON(), yamlContent = yaml.stringify(data, { lineWidth: 0 });
98
+ await writeFile(targetPath, yamlContent), this.filePath = targetPath;
99
+ }
100
+ /**
101
+ * Record a sync (successful or failed)
102
+ */
103
+ recordSync(record) {
104
+ const status = record.status || "success";
105
+ (status === "success" || status === "partial") && (this.lastSyncCommit = record.commit, this.lastSyncTimestamp = record.timestamp), this.syncHistory.push({
106
+ timestamp: record.timestamp,
107
+ commit: record.commit,
108
+ status,
109
+ changeset: record.changeset,
110
+ files_changed: record.files_changed,
111
+ elements_affected: record.elements_affected,
112
+ notes: record.notes
113
+ }), this.syncHistory.length > 100 && (this.syncHistory = this.syncHistory.slice(-100));
114
+ }
115
+ /**
116
+ * Record ambiguous file-to-element mappings for review
117
+ */
118
+ recordAmbiguities(ambiguities) {
119
+ this.ambiguities = ambiguities;
120
+ }
121
+ /**
122
+ * Clear ambiguities after review
123
+ */
124
+ clearAmbiguities() {
125
+ this.ambiguities = [];
126
+ }
127
+ /**
128
+ * Get the last sync record if any
129
+ */
130
+ getLastSync() {
131
+ return this.syncHistory.length > 0 ? this.syncHistory[this.syncHistory.length - 1] : void 0;
132
+ }
133
+ /**
134
+ * Serialize to JSON representation for saving
135
+ */
136
+ toJSON() {
137
+ return {
138
+ project_name: this.projectName,
139
+ last_sync_commit: this.lastSyncCommit,
140
+ last_sync_timestamp: this.lastSyncTimestamp,
141
+ sync_history: this.syncHistory.map((record) => ({
142
+ timestamp: record.timestamp,
143
+ commit: record.commit,
144
+ changeset: record.changeset,
145
+ files_changed: record.files_changed,
146
+ elements_affected: record.elements_affected,
147
+ status: record.status,
148
+ notes: record.notes
149
+ })),
150
+ ...this.ambiguities.length > 0 && {
151
+ ambiguities: this.ambiguities.map((a) => ({
152
+ file_path: a.filePath,
153
+ possible_elements: a.possibleElements.map((e) => ({
154
+ element_id: e.elementId,
155
+ layer: e.layer,
156
+ confidence: e.confidence
157
+ }))
158
+ }))
159
+ }
160
+ };
161
+ }
162
+ }
163
+ export {
164
+ FarmSyncState,
165
+ FarmSyncStateNotFoundError
166
+ };
167
+ //# sourceMappingURL=farm-sync-state.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/farm-sync-state.ts"],
4
+ "sourcesContent": ["/**\n * Farm Sync State - Manages .farm-sync.yaml per project\n *\n * Tracks sync history, last sync commit, and timestamps for each project.\n */\n\nimport yaml from \"yaml\";\nimport { readFile, writeFile, fileExists } from \"../utils/file-io.js\";\n\n/**\n * Represents a single sync record\n */\nexport interface SyncRecord {\n timestamp: string; // ISO 8601 timestamp\n commit: string; // Commit SHA where sync occurred\n changeset?: string; // ID of generated changeset\n files_changed?: number; // Number of files that changed\n elements_affected?: number; // Number of model elements affected\n status: \"success\" | \"partial\" | \"failed\"; // Status of the sync\n notes?: string; // Optional notes about the sync\n}\n\n/**\n * Farm sync state for a single project\n */\nexport interface FarmSyncStateData {\n project_name: string;\n last_sync_commit?: string; // Last successful sync commit SHA\n last_sync_timestamp?: string; // ISO 8601 timestamp\n sync_history: SyncRecord[]; // Complete history of syncs\n ambiguities?: Array<{\n file_path: string;\n possible_elements: Array<{\n element_id: string;\n layer: string;\n confidence: number; // 0-100 confidence score\n }>;\n }>; // Ambiguous file-to-element mappings flagged for review\n}\n\n/**\n * Thrown by FarmSyncState.load when the .farm-sync.yaml file does not exist.\n * Callers that treat \"never synced\" differently from other failures check `code`\n * (or `instanceof`) rather than matching the message text.\n */\nexport class FarmSyncStateNotFoundError extends Error {\n readonly code = \"FARM_SYNC_STATE_NOT_FOUND\";\n readonly filePath: string;\n\n constructor(filePath: string) {\n super(`Farm sync state file not found: ${filePath}`);\n this.name = \"FarmSyncStateNotFoundError\";\n this.filePath = filePath;\n }\n}\n\n/**\n * FarmSyncState class - Load, validate, and save farm sync state per project\n */\nexport class FarmSyncState {\n projectName: string;\n lastSyncCommit?: string;\n lastSyncTimestamp?: string;\n syncHistory: SyncRecord[];\n ambiguities: Array<{\n filePath: string;\n possibleElements: Array<{\n elementId: string;\n layer: string;\n confidence: number;\n }>;\n }>;\n filePath?: string;\n\n constructor(data: FarmSyncStateData) {\n this.projectName = data.project_name;\n this.lastSyncCommit = data.last_sync_commit;\n this.lastSyncTimestamp = data.last_sync_timestamp;\n this.syncHistory = data.sync_history || [];\n this.ambiguities = data.ambiguities?.map(a => ({\n filePath: a.file_path,\n possibleElements: a.possible_elements.map(e => ({\n elementId: e.element_id,\n layer: e.layer,\n confidence: e.confidence,\n })),\n })) || [];\n }\n\n /**\n * Load farm sync state from file\n * @param filePath - Path to .farm-sync.yaml\n * @returns FarmSyncState instance\n */\n static async load(filePath: string): Promise<FarmSyncState> {\n if (!(await fileExists(filePath))) {\n throw new FarmSyncStateNotFoundError(filePath);\n }\n\n const content = await readFile(filePath);\n const rawData = yaml.parse(content) as any;\n\n if (!rawData) {\n throw new Error(\n `Invalid farm sync state: file is empty or contains invalid YAML at ${filePath}`\n );\n }\n\n // Handle both old (camelCase) and new (snake_case) formats for backward compatibility\n const data: FarmSyncStateData = {\n project_name: rawData.project_name || rawData.projectName,\n last_sync_commit: rawData.last_sync_commit || rawData.lastSyncCommit,\n last_sync_timestamp: rawData.last_sync_timestamp || rawData.lastSyncTimestamp,\n sync_history: (rawData.sync_history || rawData.syncHistory || []).map((record: any) => ({\n timestamp: record.timestamp,\n commit: record.commit,\n changeset: record.changeset || record.changesetId,\n files_changed: record.files_changed || record.filesChanged,\n elements_affected: record.elements_affected || record.elementsAffected,\n status: record.status,\n notes: record.notes,\n })),\n ambiguities: (rawData.ambiguities || []).map((a: any) => ({\n file_path: a.file_path || a.filePath,\n possible_elements: (a.possible_elements || a.possibleElements || []).map((e: any) => ({\n element_id: e.element_id || e.elementId,\n layer: e.layer,\n confidence: e.confidence,\n })),\n })),\n };\n\n if (!data.project_name) {\n throw new Error(\"Farm sync state must have a 'project_name' field\");\n }\n\n const state = new FarmSyncState(data);\n state.filePath = filePath;\n return state;\n }\n\n /**\n * Create a new farm sync state\n * @param projectName - Name of the project\n * @param options - Optional initial state\n * @returns FarmSyncState instance\n */\n static create(\n projectName: string,\n options: {\n lastSyncCommit?: string;\n lastSyncTimestamp?: string;\n } = {}\n ): FarmSyncState {\n return new FarmSyncState({\n project_name: projectName,\n last_sync_commit: options.lastSyncCommit,\n last_sync_timestamp: options.lastSyncTimestamp,\n sync_history: [],\n ambiguities: [],\n });\n }\n\n /**\n * Load or create farm sync state\n * @param filePath - Path to .farm-sync.yaml\n * @param projectName - Project name (used if creating new)\n * @returns FarmSyncState instance\n */\n static async loadOrCreate(filePath: string, projectName: string): Promise<FarmSyncState> {\n if (await fileExists(filePath)) {\n return this.load(filePath);\n }\n return this.create(projectName);\n }\n\n /**\n * Save state to file\n * @param filePath - Path where to save .farm-sync.yaml\n */\n async save(filePath?: string): Promise<void> {\n const targetPath = filePath || this.filePath;\n if (!targetPath) {\n throw new Error(\"No file path specified for saving farm sync state\");\n }\n\n const data = this.toJSON();\n const yamlContent = yaml.stringify(data, { lineWidth: 0 });\n\n await writeFile(targetPath, yamlContent);\n this.filePath = targetPath;\n }\n\n /**\n * Record a sync (successful or failed)\n */\n recordSync(record: Omit<SyncRecord, \"status\"> & { status?: \"success\" | \"partial\" | \"failed\" }): void {\n const status = record.status || \"success\";\n\n // Update lastSyncCommit on successful and partial syncs to avoid reprocessing confident files.\n // Failed syncs do not advance the pointer (will retry from same point).\n // Partial syncs advance because confident files have been processed and staged;\n // ambiguous files are tracked separately for manual review.\n if (status === \"success\" || status === \"partial\") {\n this.lastSyncCommit = record.commit;\n this.lastSyncTimestamp = record.timestamp;\n }\n\n this.syncHistory.push({\n timestamp: record.timestamp,\n commit: record.commit,\n status,\n changeset: record.changeset,\n files_changed: record.files_changed,\n elements_affected: record.elements_affected,\n notes: record.notes,\n });\n\n // Keep history trimmed to last 100 syncs\n if (this.syncHistory.length > 100) {\n this.syncHistory = this.syncHistory.slice(-100);\n }\n }\n\n /**\n * Record ambiguous file-to-element mappings for review\n */\n recordAmbiguities(\n ambiguities: Array<{\n filePath: string;\n possibleElements: Array<{\n elementId: string;\n layer: string;\n confidence: number;\n }>;\n }>\n ): void {\n this.ambiguities = ambiguities;\n }\n\n /**\n * Clear ambiguities after review\n */\n clearAmbiguities(): void {\n this.ambiguities = [];\n }\n\n /**\n * Get the last sync record if any\n */\n getLastSync(): SyncRecord | undefined {\n return this.syncHistory.length > 0 ? this.syncHistory[this.syncHistory.length - 1] : undefined;\n }\n\n /**\n * Serialize to JSON representation for saving\n */\n toJSON(): FarmSyncStateData {\n return {\n project_name: this.projectName,\n last_sync_commit: this.lastSyncCommit,\n last_sync_timestamp: this.lastSyncTimestamp,\n sync_history: this.syncHistory.map(record => ({\n timestamp: record.timestamp,\n commit: record.commit,\n changeset: record.changeset,\n files_changed: record.files_changed,\n elements_affected: record.elements_affected,\n status: record.status,\n notes: record.notes,\n })),\n ...(this.ambiguities.length > 0 && {\n ambiguities: this.ambiguities.map(a => ({\n file_path: a.filePath,\n possible_elements: a.possibleElements.map(e => ({\n element_id: e.elementId,\n layer: e.layer,\n confidence: e.confidence,\n })),\n }))\n }),\n };\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -5,10 +5,13 @@ class Manifest {
5
5
  author;
6
6
  created;
7
7
  modified;
8
+ cliVersion;
8
9
  specVersion;
9
10
  changeset_history;
11
+ models;
12
+ codebase_path;
10
13
  constructor(data) {
11
- this.name = data.name, this.version = data.version, this.description = data.description, this.author = data.author, this.created = data.created ?? (/* @__PURE__ */ new Date()).toISOString(), this.modified = data.modified ?? (/* @__PURE__ */ new Date()).toISOString(), this.specVersion = data.specVersion, this.changeset_history = this.migrateChangesetHistory(data.changeset_history);
14
+ this.name = data.name, this.version = data.version, this.description = data.description, this.author = data.author, this.created = data.created ?? (/* @__PURE__ */ new Date()).toISOString(), this.modified = data.modified ?? (/* @__PURE__ */ new Date()).toISOString(), this.cliVersion = data.cliVersion, this.specVersion = data.specVersion, this.models = data.models, this.codebase_path = data.codebase_path, this.changeset_history = this.migrateChangesetHistory(data.changeset_history);
12
15
  }
13
16
  /**
14
17
  * Migrate changeset history from legacy field names to new format
@@ -45,7 +48,7 @@ class Manifest {
45
48
  * Serialize to JSON representation
46
49
  *
47
50
  * Converts the Manifest to a JSON-serializable object that includes project
48
- * metadata and changeset history.
51
+ * metadata, changeset history, and external model declarations.
49
52
  *
50
53
  * @returns ManifestData object suitable for JSON serialization
51
54
  */
@@ -56,7 +59,7 @@ class Manifest {
56
59
  created: this.created,
57
60
  modified: this.modified
58
61
  };
59
- return this.description && (result.description = this.description), this.author && (result.author = this.author), this.specVersion && (result.specVersion = this.specVersion), this.changeset_history && this.changeset_history.length > 0 && (result.changeset_history = this.changeset_history), result;
62
+ return this.description && (result.description = this.description), this.author && (result.author = this.author), this.cliVersion && (result.cliVersion = this.cliVersion), this.specVersion && (result.specVersion = this.specVersion), this.codebase_path && (result.codebase_path = this.codebase_path), this.changeset_history && this.changeset_history.length > 0 && (result.changeset_history = this.changeset_history), this.models && Object.keys(this.models).length > 0 && (result.models = this.models), result;
60
63
  }
61
64
  /**
62
65
  * Create Manifest from JSON string
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/core/manifest.ts"],
4
- "sourcesContent": ["import type {\n ManifestData,\n ChangesetHistoryEntry,\n} from \"../types/index.js\";\n\n/**\n * Manifest representing model metadata and configuration\n *\n * The Manifest class manages all metadata about a Documentation Robotics model,\n * including project information, changeset history, and layer configuration.\n *\n * This class handles:\n * - Project metadata (name, version, description, author)\n * - Changeset application history\n */\nexport class Manifest {\n name: string;\n version: string;\n description?: string;\n author?: string;\n created: string;\n modified: string;\n specVersion?: string;\n changeset_history?: ChangesetHistoryEntry[];\n\n constructor(data: ManifestData) {\n this.name = data.name;\n this.version = data.version;\n this.description = data.description;\n this.author = data.author;\n this.created = data.created ?? new Date().toISOString();\n this.modified = data.modified ?? new Date().toISOString();\n this.specVersion = data.specVersion;\n\n // Migrate changeset history from legacy format (if present)\n this.changeset_history = this.migrateChangesetHistory(data.changeset_history);\n }\n\n /**\n * Migrate changeset history from legacy field names to new format\n * Legacy format: applied_at \u2192 committed_at, action: \"applied\" \u2192 action: \"committed\"\n *\n * @param history - Changeset history from manifest (may use legacy field names)\n * @returns Migrated changeset history using new field names\n * @throws Error if action value is unrecognized (not \"committed\", \"discarded\", \"applied\", or \"reverted\")\n */\n private migrateChangesetHistory(\n history: ChangesetHistoryEntry[] | undefined\n ): ChangesetHistoryEntry[] {\n if (!history || history.length === 0) {\n return [];\n }\n\n return history.map((entry: any) => {\n // Validate action value to prevent silent data corruption\n let action: \"committed\" | \"discarded\";\n if (entry.action === \"applied\" || entry.action === \"committed\") {\n action = \"committed\";\n } else if (entry.action === \"reverted\" || entry.action === \"discarded\") {\n action = \"discarded\";\n } else {\n throw new Error(\n `Invalid changeset history action '${entry.action}' for changeset '${entry.name}'. ` +\n `Expected: committed, discarded, applied (legacy), or reverted (legacy).`\n );\n }\n\n return {\n name: entry.name,\n // Handle legacy field name: applied_at \u2192 committed_at\n committed_at: entry.committed_at || entry.applied_at || new Date().toISOString(),\n action,\n };\n });\n }\n\n /**\n * Update the modified timestamp to current time\n */\n updateModified(): void {\n this.modified = new Date().toISOString();\n }\n\n /**\n * Serialize to JSON representation\n *\n * Converts the Manifest to a JSON-serializable object that includes project\n * metadata and changeset history.\n *\n * @returns ManifestData object suitable for JSON serialization\n */\n toJSON(): Record<string, unknown> {\n const result: Record<string, unknown> = {\n name: this.name,\n version: this.version,\n created: this.created,\n modified: this.modified,\n };\n\n if (this.description) {\n result.description = this.description;\n }\n\n if (this.author) {\n result.author = this.author;\n }\n\n if (this.specVersion) {\n result.specVersion = this.specVersion;\n }\n\n if (this.changeset_history && this.changeset_history.length > 0) {\n result.changeset_history = this.changeset_history;\n }\n\n return result;\n }\n\n /**\n * Create Manifest from JSON string\n */\n static fromJSON(json: string): Manifest {\n const data = JSON.parse(json) as ManifestData;\n return new Manifest(data);\n }\n\n /**\n * String representation\n */\n toString(): string {\n return `Manifest(${this.name} v${this.version})`;\n }\n}\n"],
5
- "mappings": "AAeM,MAAO,SAAQ;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA,YAAY,MAAkB;AAC5B,SAAK,OAAO,KAAK,MACjB,KAAK,UAAU,KAAK,SACpB,KAAK,cAAc,KAAK,aACxB,KAAK,SAAS,KAAK,QACnB,KAAK,UAAU,KAAK,YAAW,oBAAI,KAAI,GAAG,YAAW,GACrD,KAAK,WAAW,KAAK,aAAY,oBAAI,KAAI,GAAG,YAAW,GACvD,KAAK,cAAc,KAAK,aAGxB,KAAK,oBAAoB,KAAK,wBAAwB,KAAK,iBAAiB;EAC9E;;;;;;;;;EAUQ,wBACN,SAA4C;AAE5C,WAAI,CAAC,WAAW,QAAQ,WAAW,IAC1B,CAAA,IAGF,QAAQ,IAAI,CAAC,UAAc;AAEhC,UAAI;AACJ,UAAI,MAAM,WAAW,aAAa,MAAM,WAAW;AACjD,iBAAS;eACA,MAAM,WAAW,cAAc,MAAM,WAAW;AACzD,iBAAS;;AAET,cAAM,IAAI,MACR,qCAAqC,MAAM,MAAM,oBAAoB,MAAM,IAAI,4EACN;AAI7E,aAAO;QACL,MAAM,MAAM;;QAEZ,cAAc,MAAM,gBAAgB,MAAM,eAAc,oBAAI,KAAI,GAAG,YAAW;QAC9E;;IAEJ,CAAC;EACH;;;;EAKA,iBAAc;AACZ,SAAK,YAAW,oBAAI,KAAI,GAAG,YAAW;EACxC;;;;;;;;;EAUA,SAAM;AACJ,UAAM,SAAkC;MACtC,MAAM,KAAK;MACX,SAAS,KAAK;MACd,SAAS,KAAK;MACd,UAAU,KAAK;;AAGjB,WAAI,KAAK,gBACP,OAAO,cAAc,KAAK,cAGxB,KAAK,WACP,OAAO,SAAS,KAAK,SAGnB,KAAK,gBACP,OAAO,cAAc,KAAK,cAGxB,KAAK,qBAAqB,KAAK,kBAAkB,SAAS,MAC5D,OAAO,oBAAoB,KAAK,oBAG3B;EACT;;;;EAKA,OAAO,SAAS,MAAY;AAC1B,UAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,WAAO,IAAI,SAAS,IAAI;EAC1B;;;;EAKA,WAAQ;AACN,WAAO,YAAY,KAAK,IAAI,KAAK,KAAK,OAAO;EAC/C;;",
4
+ "sourcesContent": ["import type {\n ManifestData,\n ChangesetHistoryEntry,\n ExternalModelDeclaration,\n} from \"../types/index.js\";\n\n/**\n * Manifest representing model metadata and configuration\n *\n * The Manifest class manages all metadata about a Documentation Robotics model,\n * including project information, changeset history, layer configuration, and\n * external model declarations.\n *\n * This class handles:\n * - Project metadata (name, version, description, author)\n * - Changeset application history\n * - External model declarations for cross-model references\n */\nexport class Manifest {\n name: string;\n version: string;\n description?: string;\n author?: string;\n created: string;\n modified: string;\n cliVersion?: string;\n specVersion?: string;\n changeset_history?: ChangesetHistoryEntry[];\n models?: Record<string, ExternalModelDeclaration>;\n codebase_path?: string;\n\n constructor(data: ManifestData) {\n this.name = data.name;\n this.version = data.version;\n this.description = data.description;\n this.author = data.author;\n this.created = data.created ?? new Date().toISOString();\n this.modified = data.modified ?? new Date().toISOString();\n this.cliVersion = data.cliVersion;\n this.specVersion = data.specVersion;\n this.models = data.models;\n this.codebase_path = data.codebase_path;\n\n // Migrate changeset history from legacy format (if present)\n this.changeset_history = this.migrateChangesetHistory(data.changeset_history);\n }\n\n /**\n * Migrate changeset history from legacy field names to new format\n * Legacy format: applied_at \u2192 committed_at, action: \"applied\" \u2192 action: \"committed\"\n *\n * @param history - Changeset history from manifest (may use legacy field names)\n * @returns Migrated changeset history using new field names\n * @throws Error if action value is unrecognized (not \"committed\", \"discarded\", \"applied\", or \"reverted\")\n */\n private migrateChangesetHistory(\n history: ChangesetHistoryEntry[] | undefined\n ): ChangesetHistoryEntry[] {\n if (!history || history.length === 0) {\n return [];\n }\n\n return history.map((entry: any) => {\n // Validate action value to prevent silent data corruption\n let action: \"committed\" | \"discarded\";\n if (entry.action === \"applied\" || entry.action === \"committed\") {\n action = \"committed\";\n } else if (entry.action === \"reverted\" || entry.action === \"discarded\") {\n action = \"discarded\";\n } else {\n throw new Error(\n `Invalid changeset history action '${entry.action}' for changeset '${entry.name}'. ` +\n `Expected: committed, discarded, applied (legacy), or reverted (legacy).`\n );\n }\n\n return {\n name: entry.name,\n // Handle legacy field name: applied_at \u2192 committed_at\n committed_at: entry.committed_at || entry.applied_at || new Date().toISOString(),\n action,\n };\n });\n }\n\n /**\n * Update the modified timestamp to current time\n */\n updateModified(): void {\n this.modified = new Date().toISOString();\n }\n\n /**\n * Serialize to JSON representation\n *\n * Converts the Manifest to a JSON-serializable object that includes project\n * metadata, changeset history, and external model declarations.\n *\n * @returns ManifestData object suitable for JSON serialization\n */\n toJSON(): Record<string, unknown> {\n const result: Record<string, unknown> = {\n name: this.name,\n version: this.version,\n created: this.created,\n modified: this.modified,\n };\n\n if (this.description) {\n result.description = this.description;\n }\n\n if (this.author) {\n result.author = this.author;\n }\n\n if (this.cliVersion) {\n result.cliVersion = this.cliVersion;\n }\n\n if (this.specVersion) {\n result.specVersion = this.specVersion;\n }\n\n if (this.codebase_path) {\n result.codebase_path = this.codebase_path;\n }\n\n if (this.changeset_history && this.changeset_history.length > 0) {\n result.changeset_history = this.changeset_history;\n }\n\n if (this.models && Object.keys(this.models).length > 0) {\n result.models = this.models;\n }\n\n return result;\n }\n\n /**\n * Create Manifest from JSON string\n */\n static fromJSON(json: string): Manifest {\n const data = JSON.parse(json) as ManifestData;\n return new Manifest(data);\n }\n\n /**\n * String representation\n */\n toString(): string {\n return `Manifest(${this.name} v${this.version})`;\n }\n}\n"],
5
+ "mappings": "AAkBM,MAAO,SAAQ;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA,YAAY,MAAkB;AAC5B,SAAK,OAAO,KAAK,MACjB,KAAK,UAAU,KAAK,SACpB,KAAK,cAAc,KAAK,aACxB,KAAK,SAAS,KAAK,QACnB,KAAK,UAAU,KAAK,YAAW,oBAAI,KAAI,GAAG,YAAW,GACrD,KAAK,WAAW,KAAK,aAAY,oBAAI,KAAI,GAAG,YAAW,GACvD,KAAK,aAAa,KAAK,YACvB,KAAK,cAAc,KAAK,aACxB,KAAK,SAAS,KAAK,QACnB,KAAK,gBAAgB,KAAK,eAG1B,KAAK,oBAAoB,KAAK,wBAAwB,KAAK,iBAAiB;EAC9E;;;;;;;;;EAUQ,wBACN,SAA4C;AAE5C,WAAI,CAAC,WAAW,QAAQ,WAAW,IAC1B,CAAA,IAGF,QAAQ,IAAI,CAAC,UAAc;AAEhC,UAAI;AACJ,UAAI,MAAM,WAAW,aAAa,MAAM,WAAW;AACjD,iBAAS;eACA,MAAM,WAAW,cAAc,MAAM,WAAW;AACzD,iBAAS;;AAET,cAAM,IAAI,MACR,qCAAqC,MAAM,MAAM,oBAAoB,MAAM,IAAI,4EACN;AAI7E,aAAO;QACL,MAAM,MAAM;;QAEZ,cAAc,MAAM,gBAAgB,MAAM,eAAc,oBAAI,KAAI,GAAG,YAAW;QAC9E;;IAEJ,CAAC;EACH;;;;EAKA,iBAAc;AACZ,SAAK,YAAW,oBAAI,KAAI,GAAG,YAAW;EACxC;;;;;;;;;EAUA,SAAM;AACJ,UAAM,SAAkC;MACtC,MAAM,KAAK;MACX,SAAS,KAAK;MACd,SAAS,KAAK;MACd,UAAU,KAAK;;AAGjB,WAAI,KAAK,gBACP,OAAO,cAAc,KAAK,cAGxB,KAAK,WACP,OAAO,SAAS,KAAK,SAGnB,KAAK,eACP,OAAO,aAAa,KAAK,aAGvB,KAAK,gBACP,OAAO,cAAc,KAAK,cAGxB,KAAK,kBACP,OAAO,gBAAgB,KAAK,gBAG1B,KAAK,qBAAqB,KAAK,kBAAkB,SAAS,MAC5D,OAAO,oBAAoB,KAAK,oBAG9B,KAAK,UAAU,OAAO,KAAK,KAAK,MAAM,EAAE,SAAS,MACnD,OAAO,SAAS,KAAK,SAGhB;EACT;;;;EAKA,OAAO,SAAS,MAAY;AAC1B,UAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,WAAO,IAAI,SAAS,IAAI;EAC1B;;;;EAKA,WAAQ;AACN,WAAO,YAAY,KAAK,IAAI,KAAK,KAAK,OAAO;EAC/C;;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import { getErrorMessage } from "../utils/errors.js";
2
+ import { applyCatalogMigrations } from "./catalog-migration.js";
2
3
  class MigrationRegistry {
3
4
  migrations = [];
4
5
  constructor() {
@@ -134,13 +135,41 @@ class MigrationRegistry {
134
135
  description: "Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)"
135
136
  };
136
137
  }
138
+ }), this.migrations.push({
139
+ fromVersion: "0.9.0",
140
+ toVersion: "0.10.0",
141
+ description: "Data Model objectschema.required type change, element type renames and relationship catalog rewrites (Spec v0.10.0)",
142
+ apply: async (model) => {
143
+ const required = await migrateObjectSchemaRequired(model), catalog = await applyCatalogMigrations(model), errors = [...required.errors, ...catalog.errors];
144
+ return {
145
+ migrationsApplied: 1,
146
+ filesModified: required.filesModified + catalog.filesModified,
147
+ description: `Spec version updated to 0.10.0 (objectschema.required converted from string to array; ${catalog.summaries.join("; ")})`,
148
+ ...errors.length > 0 && { error: errors.join("; ") },
149
+ details: catalog.details
150
+ };
151
+ }
152
+ }), this.migrations.push({
153
+ fromVersion: "0.10.0",
154
+ toVersion: "0.11.0",
155
+ description: "Relationship catalog rewrites from the layer rules pass (Spec v0.11.0)",
156
+ apply: async (model) => {
157
+ const catalog = await applyCatalogMigrations(model);
158
+ return {
159
+ migrationsApplied: 1,
160
+ filesModified: catalog.filesModified,
161
+ description: `Spec version updated to 0.11.0 (${catalog.summaries.join("; ")})`,
162
+ ...catalog.errors.length > 0 && { error: catalog.errors.join("; ") },
163
+ details: catalog.details
164
+ };
165
+ }
137
166
  });
138
167
  }
139
168
  /**
140
169
  * Get the latest available specification version
141
170
  */
142
171
  getLatestVersion() {
143
- return this.migrations.length === 0 ? "0.7.0" : this.migrations.map((m) => m.toVersion).sort().reverse()[0];
172
+ return this.migrations.length === 0 ? "0.7.0" : this.migrations.map((m) => m.toVersion).reduce((max, current) => this.compareVersions(current, max) > 0 ? current : max);
144
173
  }
145
174
  /**
146
175
  * Get the sequence of migrations needed from one version to another
@@ -237,6 +266,60 @@ class MigrationRegistry {
237
266
  return 0;
238
267
  }
239
268
  }
269
+ async function migrateObjectSchemaRequired(model) {
270
+ const result = await migrateObjectSchemaRequiredFiles(model);
271
+ return migrateLoadedObjectSchemaRequired(model), result;
272
+ }
273
+ function requiredAsArray(required) {
274
+ return !required || typeof required != "string" ? null : required.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
275
+ }
276
+ function migrateLoadedObjectSchemaRequired(model) {
277
+ const layer = model.layers.get("data-model");
278
+ if (layer)
279
+ for (const element of layer.listElements()) {
280
+ if (element.spec_node_id !== "data-model.objectschema")
281
+ continue;
282
+ const required = requiredAsArray(element.attributes.required);
283
+ required && (element.attributes = { ...element.attributes, required }, layer.updateElement(element));
284
+ }
285
+ }
286
+ async function migrateObjectSchemaRequiredFiles(model) {
287
+ const fs = await import("fs/promises"), pathModule = await import("path"), YAML = await import("yaml"), dataModelDir = pathModule.join(model.rootPath, "documentation-robotics/model/08_data-model");
288
+ let filesModified = 0;
289
+ try {
290
+ await fs.access(dataModelDir);
291
+ } catch (error) {
292
+ if (error.code === "ENOENT")
293
+ return { filesModified: 0, errors: [] };
294
+ throw error;
295
+ }
296
+ const files = await fs.readdir(dataModelDir), errors = [];
297
+ for (const file of files) {
298
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
299
+ continue;
300
+ const filePath = pathModule.join(dataModelDir, file);
301
+ try {
302
+ const content = await fs.readFile(filePath, "utf-8");
303
+ let modified = !1;
304
+ const doc = YAML.parse(content);
305
+ if (!doc || typeof doc != "object")
306
+ continue;
307
+ for (const [, element] of Object.entries(doc)) {
308
+ if (!element || typeof element != "object" || element.spec_node_id !== "data-model.objectschema")
309
+ continue;
310
+ const attributes = element.attributes;
311
+ if (attributes && typeof attributes == "object") {
312
+ const required = requiredAsArray(attributes.required);
313
+ required && (attributes.required = required, modified = !0);
314
+ }
315
+ }
316
+ modified && (await fs.writeFile(filePath, YAML.stringify(doc), "utf-8"), filesModified++);
317
+ } catch (error) {
318
+ errors.push(`Failed to migrate ${file}: ${getErrorMessage(error)}`);
319
+ }
320
+ }
321
+ return { filesModified, errors };
322
+ }
240
323
  export {
241
324
  MigrationRegistry
242
325
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/core/migration-registry.ts"],
4
- "sourcesContent": ["/**\n * Migration Registry - Manages version-based model migrations\n *\n * Handles sequential migrations from one spec version to another,\n * supporting the evolution of the architecture model specification.\n */\n\nimport type { Model } from \"./model.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * Represents a single version migration\n */\nexport interface Migration {\n fromVersion: string;\n toVersion: string;\n description: string;\n apply: (model: Model) => Promise<MigrationResult>;\n}\n\n/**\n * Result of applying a migration\n */\nexport interface MigrationResult {\n migrationsApplied: number;\n filesModified: number;\n description: string;\n error?: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Migration Registry managing all available migrations\n */\nexport class MigrationRegistry {\n private migrations: Migration[] = [];\n\n constructor() {\n this.registerMigrations();\n }\n\n /**\n * Register all available migrations\n */\n private registerMigrations(): void {\n // Migration from v0.5.0 to v0.6.0: Relationship Taxonomy\n this.migrations.push({\n fromVersion: \"0.5.0\",\n toVersion: \"0.6.0\",\n description: \"Relationship Taxonomy (Spec v0.6.0)\",\n apply: async () => {\n // This migration is opt-in - the new relationship taxonomy\n // is fully backward compatible with existing models\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.6.0 (Relationship Taxonomy now available)\",\n };\n },\n });\n\n // Migration from v0.6.0 to v0.7.0: Layer Schema Relationship Metadata\n this.migrations.push({\n fromVersion: \"0.6.0\",\n toVersion: \"0.7.0\",\n description: \"Layer Schema Relationship Metadata (Spec v0.7.0)\",\n apply: async () => {\n // This migration updates layer schemas with relationship metadata sections\n // - All 12 layer schemas now include layerMetadata, intraLayerRelationships, crossLayerRelationships\n // - Deprecated link-registry.json (removed in v0.8.0)\n // - Terminology change: \"cross-layer links\" \u2192 \"cross-layer relationships\"\n // Fully backward compatible - existing models continue to work\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description:\n \"Spec version updated to 0.7.0 (Layer schemas now include relationship metadata sections)\",\n };\n },\n });\n\n // Migration from v0.7.0 to v0.7.1: Source Code Reference Infrastructure\n this.migrations.push({\n fromVersion: \"0.7.0\",\n toVersion: \"0.7.1\",\n description: \"Source Code Reference Infrastructure (Spec v0.7.1)\",\n apply: async () => {\n // This migration adds source code reference infrastructure\n // - Common schema definitions in spec/schemas/common/\n // - Source references integrated across 10 layers\n // - Backward compatible - existing models continue to work\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.7.1 (Source Code Reference Infrastructure)\",\n };\n },\n });\n\n // Migration from v0.7.1 to v0.8.0: JSON Schema-Only Spec Architecture\n this.migrations.push({\n fromVersion: \"0.7.1\",\n toVersion: \"0.8.0\",\n description: \"JSON Schema-Only Spec Architecture (Spec v0.8.0)\",\n apply: async () => {\n // Spec architecture refactored to JSON Schema-only model:\n // - Node type definitions moved to spec/schemas/nodes/{layer}/*.node.schema.json\n // - Relationship definitions moved to spec/schemas/relationships/{layer}/*.relationship.schema.json\n // - Predicates relocated to spec/schemas/base/predicates.json\n // This is a spec architecture change only - model data files are fully backward compatible\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.0 (JSON Schema-Only Spec Architecture)\",\n };\n },\n });\n\n // Migration from v0.8.0 to v0.8.1: Expanded Relationship Coverage and Node Improvements\n this.migrations.push({\n fromVersion: \"0.8.0\",\n toVersion: \"0.8.1\",\n description: \"Expanded Relationship Coverage and Node Improvements (Spec v0.8.1)\",\n apply: async () => {\n // Spec enhancements only - no model data format changes:\n // - 733 new relationship schemas added across all 12 layers\n // - Node type descriptions improved for all 12 layers\n // - Enum-masquerader node types consolidated into parent attributes\n // - Existing models continue to work without modification\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.1 (Expanded Relationship Coverage and Node Improvements)\",\n };\n },\n });\n\n // Migration from v0.8.1 to v0.8.2: UUID/Path identifier separation\n this.migrations.push({\n fromVersion: \"0.8.1\",\n toVersion: \"0.8.2\",\n description: \"UUID/Path identifier separation (Spec v0.8.2)\",\n apply: async () => {\n // The path field is now required on all spec nodes (spec-node.schema.json).\n // Existing elements are migrated automatically on load in model.ts (loadLayer):\n // - Elements with slug-format id: slug moved to path, deterministic UUID assigned to id\n // - Elements with UUID id but no path: path derived from layer_id.type.kebab(name)\n // No manual data file changes needed; migration runs transparently on next load/save.\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.2 (UUID/Path identifier separation)\",\n };\n },\n });\n\n // Migration from v0.8.2 to v0.8.3: Inter-layer Relationship Schemas and APM Node Types\n this.migrations.push({\n fromVersion: \"0.8.2\",\n toVersion: \"0.8.3\",\n description: \"Inter-layer Relationship Schemas and APM Node Types (Spec v0.8.3)\",\n apply: async () => {\n // Major expansion of inter-layer relationship coverage and APM layer:\n // - 495 new inter-layer relationship schemas across all 12 layers\n // - Total relationship count increased from 969 to 1,447\n // - 2 new APM node types: apm.alert and apm.dashboard\n // - Backward compatible - existing models continue to work without modification\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.3 (Inter-layer Relationship Schemas and APM Node Types)\",\n };\n },\n });\n\n // Migration from v0.8.3 to v0.8.4: Analyzer Infrastructure (CBM Mapping)\n this.migrations.push({\n fromVersion: \"0.8.3\",\n toVersion: \"0.8.4\",\n description: \"Analyzer Infrastructure (Spec v0.8.4)\",\n apply: async () => {\n // Introduces spec/analyzers/ with codebase-memory (CBM) mapping:\n // - New analyzer adapter foundation and full analyzer subcommands\n // - dr analyzer verify with changeset awareness\n // - Claude Code integration commands (/dr-verify, /dr-map, dr-extractor)\n // - No model data format changes; existing models are fully compatible\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.4 (Analyzer Infrastructure)\",\n };\n },\n });\n\n // Migration from v0.8.4 to v0.9.0: Product Layer Introduction\n this.migrations.push({\n fromVersion: \"0.8.4\",\n toVersion: \"0.9.0\",\n description: \"Product Layer Introduction (Spec v0.9.0)\",\n apply: async (model: Model) => {\n const fs = await import(\"fs/promises\");\n const pathModule = await import(\"path\");\n\n const modelDir = `${model.rootPath}/documentation-robotics/model`;\n let filesModified = 0;\n\n try {\n // Verify model directory exists\n await fs.access(modelDir);\n } catch (error) {\n const err = error as { code?: string };\n // ENOENT means the directory doesn't exist - nothing to migrate\n if (err.code === \"ENOENT\") {\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced; no model directories to migrate)\",\n };\n }\n // Re-throw other errors (e.g., EACCES for permission denied)\n throw error;\n }\n\n // Rename existing layer directories: shift from 03-12 to 04-13\n // Must do this in reverse order (12\u219213, 11\u219212, ..., 03\u219204) to avoid conflicts\n const renameMap = [\n [\"12_testing\", \"13_testing\"],\n [\"11_apm\", \"12_apm\"],\n [\"10_navigation\", \"11_navigation\"],\n [\"09_ux\", \"10_ux\"],\n [\"08_data-store\", \"09_data-store\"],\n [\"07_data-model\", \"08_data-model\"],\n [\"06_api\", \"07_api\"],\n [\"05_technology\", \"06_technology\"],\n [\"04_application\", \"05_application\"],\n [\"03_security\", \"04_security\"],\n ];\n\n const renameErrors: string[] = [];\n\n for (const [oldName, newName] of renameMap) {\n const oldPath = pathModule.join(modelDir, oldName);\n const newPath = pathModule.join(modelDir, newName);\n\n try {\n await fs.rename(oldPath, newPath);\n filesModified++;\n } catch (error) {\n const err = error as { code?: string };\n // ENOENT is expected when a directory hasn't been created yet\n if (err.code === \"ENOENT\") {\n continue;\n }\n // Track non-ENOENT errors to report them in the result\n renameErrors.push(\n `Failed to rename ${oldName} to ${newName}: ${getErrorMessage(error)}`\n );\n }\n }\n\n // If there were any rename failures, include them in the error field\n if (renameErrors.length > 0) {\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)\",\n error: renameErrors.join(\"; \"),\n };\n }\n\n // Create new product layer directory (it will be empty for now)\n // recursive: true handles EEXIST, so no catch needed\n const productPath = pathModule.join(modelDir, \"03_product\");\n await fs.mkdir(productPath, { recursive: true });\n\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)\",\n };\n },\n });\n }\n\n /**\n * Get the latest available specification version\n */\n getLatestVersion(): string {\n if (this.migrations.length === 0) {\n return \"0.7.0\";\n }\n\n // Return the highest toVersion from all migrations\n const versions = this.migrations.map((m) => m.toVersion);\n return versions.sort().reverse()[0];\n }\n\n /**\n * Get the sequence of migrations needed from one version to another\n */\n getMigrationPath(fromVersion: string, toVersion?: string): Migration[] {\n const targetVersion = toVersion || this.getLatestVersion();\n\n if (this.compareVersions(fromVersion, targetVersion) >= 0) {\n return [];\n }\n\n const path: Migration[] = [];\n let current = fromVersion;\n\n // Sort migrations by fromVersion\n const sortedMigrations = [...this.migrations].sort((a, b) =>\n this.compareVersions(a.fromVersion, b.fromVersion)\n );\n\n for (const migration of sortedMigrations) {\n // Check if this migration is in our path\n if (\n this.compareVersions(current, migration.fromVersion) >= 0 &&\n this.compareVersions(current, migration.toVersion) < 0 &&\n this.compareVersions(migration.toVersion, targetVersion) <= 0\n ) {\n path.push(migration);\n current = migration.toVersion;\n }\n }\n\n return path;\n }\n\n /**\n * Check if a model requires migration\n */\n requiresMigration(currentVersion: string): boolean {\n const path = this.getMigrationPath(currentVersion);\n return path.length > 0;\n }\n\n /**\n * Apply all migrations in sequence\n */\n async applyMigrations(\n model: Model,\n options: {\n fromVersion: string;\n toVersion?: string;\n dryRun?: boolean;\n validate?: boolean;\n }\n ): Promise<ApplyMigrationsResult> {\n const path = this.getMigrationPath(options.fromVersion, options.toVersion);\n const targetVersion = options.toVersion || this.getLatestVersion();\n\n if (path.length === 0) {\n return {\n applied: [],\n currentVersion: options.fromVersion,\n targetVersion,\n totalChanges: 0,\n };\n }\n\n const results: ApplyMigrationsResult = {\n applied: [],\n currentVersion: options.fromVersion,\n targetVersion,\n totalChanges: 0,\n };\n\n for (const migration of path) {\n if (!options.dryRun) {\n try {\n const result = await migration.apply(model);\n\n results.applied.push({\n from: migration.fromVersion,\n to: migration.toVersion,\n description: migration.description,\n changes: result,\n });\n\n results.totalChanges += result.migrationsApplied;\n\n // Only update model's spec version if migration succeeded (no error field)\n if (!result.error) {\n model.manifest.specVersion = migration.toVersion;\n } else {\n // If migration has errors, throw to prevent partial success state\n throw new Error(result.error);\n }\n } catch (error) {\n throw new Error(\n `Migration ${migration.fromVersion} \u2192 ${migration.toVersion} failed: ${\n getErrorMessage(error)\n }`\n );\n }\n } else {\n // Dry run - just record what would happen\n results.applied.push({\n from: migration.fromVersion,\n to: migration.toVersion,\n description: migration.description,\n dryRun: true,\n });\n }\n }\n\n return results;\n }\n\n /**\n * Get a summary of migrations that would be applied\n */\n getMigrationSummary(\n fromVersion: string,\n toVersion?: string\n ): {\n currentVersion: string;\n targetVersion: string;\n migrationsNeeded: number;\n migrations: Array<{\n from: string;\n to: string;\n description: string;\n }>;\n } {\n const path = this.getMigrationPath(fromVersion, toVersion);\n const targetVersion = toVersion || this.getLatestVersion();\n\n return {\n currentVersion: fromVersion,\n targetVersion,\n migrationsNeeded: path.length,\n migrations: path.map((m) => ({\n from: m.fromVersion,\n to: m.toVersion,\n description: m.description,\n })),\n };\n }\n\n /**\n * Compare two version strings\n * Returns: -1 if a < b, 0 if a == b, 1 if a > b\n */\n private compareVersions(a: string, b: string): number {\n const aParts = a.split(\".\").map(Number);\n const bParts = b.split(\".\").map(Number);\n\n const maxLength = Math.max(aParts.length, bParts.length);\n\n for (let i = 0; i < maxLength; i++) {\n const aPart = aParts[i] || 0;\n const bPart = bParts[i] || 0;\n\n if (aPart < bPart) return -1;\n if (aPart > bPart) return 1;\n }\n\n return 0;\n }\n}\n\n/**\n * Result of applying multiple migrations\n */\nexport interface ApplyMigrationsResult {\n applied: Array<{\n from: string;\n to: string;\n description: string;\n changes?: MigrationResult;\n dryRun?: boolean;\n }>;\n currentVersion: string;\n targetVersion: string;\n totalChanges: number;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Migration Registry - Manages version-based model migrations\n *\n * Handles sequential migrations from one spec version to another,\n * supporting the evolution of the architecture model specification.\n */\n\nimport type { Model } from \"./model.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { applyCatalogMigrations } from \"./catalog-migration.js\";\n\n/**\n * Represents a single version migration\n */\nexport interface Migration {\n fromVersion: string;\n toVersion: string;\n description: string;\n apply: (model: Model) => Promise<MigrationResult>;\n}\n\n/**\n * Result of applying a migration\n */\nexport interface MigrationResult {\n migrationsApplied: number;\n filesModified: number;\n description: string;\n error?: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Migration Registry managing all available migrations\n */\nexport class MigrationRegistry {\n private migrations: Migration[] = [];\n\n constructor() {\n this.registerMigrations();\n }\n\n /**\n * Register all available migrations\n */\n private registerMigrations(): void {\n // Migration from v0.5.0 to v0.6.0: Relationship Taxonomy\n this.migrations.push({\n fromVersion: \"0.5.0\",\n toVersion: \"0.6.0\",\n description: \"Relationship Taxonomy (Spec v0.6.0)\",\n apply: async () => {\n // This migration is opt-in - the new relationship taxonomy\n // is fully backward compatible with existing models\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.6.0 (Relationship Taxonomy now available)\",\n };\n },\n });\n\n // Migration from v0.6.0 to v0.7.0: Layer Schema Relationship Metadata\n this.migrations.push({\n fromVersion: \"0.6.0\",\n toVersion: \"0.7.0\",\n description: \"Layer Schema Relationship Metadata (Spec v0.7.0)\",\n apply: async () => {\n // This migration updates layer schemas with relationship metadata sections\n // - All 12 layer schemas now include layerMetadata, intraLayerRelationships, crossLayerRelationships\n // - Deprecated link-registry.json (removed in v0.8.0)\n // - Terminology change: \"cross-layer links\" \u2192 \"cross-layer relationships\"\n // Fully backward compatible - existing models continue to work\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description:\n \"Spec version updated to 0.7.0 (Layer schemas now include relationship metadata sections)\",\n };\n },\n });\n\n // Migration from v0.7.0 to v0.7.1: Source Code Reference Infrastructure\n this.migrations.push({\n fromVersion: \"0.7.0\",\n toVersion: \"0.7.1\",\n description: \"Source Code Reference Infrastructure (Spec v0.7.1)\",\n apply: async () => {\n // This migration adds source code reference infrastructure\n // - Common schema definitions in spec/schemas/common/\n // - Source references integrated across 10 layers\n // - Backward compatible - existing models continue to work\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.7.1 (Source Code Reference Infrastructure)\",\n };\n },\n });\n\n // Migration from v0.7.1 to v0.8.0: JSON Schema-Only Spec Architecture\n this.migrations.push({\n fromVersion: \"0.7.1\",\n toVersion: \"0.8.0\",\n description: \"JSON Schema-Only Spec Architecture (Spec v0.8.0)\",\n apply: async () => {\n // Spec architecture refactored to JSON Schema-only model:\n // - Node type definitions moved to spec/schemas/nodes/{layer}/*.node.schema.json\n // - Relationship definitions moved to spec/schemas/relationships/{layer}/*.relationship.schema.json\n // - Predicates relocated to spec/schemas/base/predicates.json\n // This is a spec architecture change only - model data files are fully backward compatible\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.0 (JSON Schema-Only Spec Architecture)\",\n };\n },\n });\n\n // Migration from v0.8.0 to v0.8.1: Expanded Relationship Coverage and Node Improvements\n this.migrations.push({\n fromVersion: \"0.8.0\",\n toVersion: \"0.8.1\",\n description: \"Expanded Relationship Coverage and Node Improvements (Spec v0.8.1)\",\n apply: async () => {\n // Spec enhancements only - no model data format changes:\n // - 733 new relationship schemas added across all 12 layers\n // - Node type descriptions improved for all 12 layers\n // - Enum-masquerader node types consolidated into parent attributes\n // - Existing models continue to work without modification\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.1 (Expanded Relationship Coverage and Node Improvements)\",\n };\n },\n });\n\n // Migration from v0.8.1 to v0.8.2: UUID/Path identifier separation\n this.migrations.push({\n fromVersion: \"0.8.1\",\n toVersion: \"0.8.2\",\n description: \"UUID/Path identifier separation (Spec v0.8.2)\",\n apply: async () => {\n // The path field is now required on all spec nodes (spec-node.schema.json).\n // Existing elements are migrated automatically on load in model.ts (loadLayer):\n // - Elements with slug-format id: slug moved to path, deterministic UUID assigned to id\n // - Elements with UUID id but no path: path derived from layer_id.type.kebab(name)\n // No manual data file changes needed; migration runs transparently on next load/save.\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.2 (UUID/Path identifier separation)\",\n };\n },\n });\n\n // Migration from v0.8.2 to v0.8.3: Inter-layer Relationship Schemas and APM Node Types\n this.migrations.push({\n fromVersion: \"0.8.2\",\n toVersion: \"0.8.3\",\n description: \"Inter-layer Relationship Schemas and APM Node Types (Spec v0.8.3)\",\n apply: async () => {\n // Major expansion of inter-layer relationship coverage and APM layer:\n // - 495 new inter-layer relationship schemas across all 12 layers\n // - Total relationship count increased from 969 to 1,447\n // - 2 new APM node types: apm.alert and apm.dashboard\n // - Backward compatible - existing models continue to work without modification\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.3 (Inter-layer Relationship Schemas and APM Node Types)\",\n };\n },\n });\n\n // Migration from v0.8.3 to v0.8.4: Analyzer Infrastructure (CBM Mapping)\n this.migrations.push({\n fromVersion: \"0.8.3\",\n toVersion: \"0.8.4\",\n description: \"Analyzer Infrastructure (Spec v0.8.4)\",\n apply: async () => {\n // Introduces spec/analyzers/ with codebase-memory (CBM) mapping:\n // - New analyzer adapter foundation and full analyzer subcommands\n // - dr analyzer verify with changeset awareness\n // - Claude Code integration commands (/dr-verify, /dr-map, dr-extractor)\n // - No model data format changes; existing models are fully compatible\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.4 (Analyzer Infrastructure)\",\n };\n },\n });\n\n // Migration from v0.8.4 to v0.9.0: Product Layer Introduction\n this.migrations.push({\n fromVersion: \"0.8.4\",\n toVersion: \"0.9.0\",\n description: \"Product Layer Introduction (Spec v0.9.0)\",\n apply: async (model: Model) => {\n const fs = await import(\"fs/promises\");\n const pathModule = await import(\"path\");\n\n const modelDir = `${model.rootPath}/documentation-robotics/model`;\n let filesModified = 0;\n\n try {\n // Verify model directory exists\n await fs.access(modelDir);\n } catch (error) {\n const err = error as { code?: string };\n // ENOENT means the directory doesn't exist - nothing to migrate\n if (err.code === \"ENOENT\") {\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced; no model directories to migrate)\",\n };\n }\n // Re-throw other errors (e.g., EACCES for permission denied)\n throw error;\n }\n\n // Rename existing layer directories: shift from 03-12 to 04-13\n // Must do this in reverse order (12\u219213, 11\u219212, ..., 03\u219204) to avoid conflicts\n const renameMap = [\n [\"12_testing\", \"13_testing\"],\n [\"11_apm\", \"12_apm\"],\n [\"10_navigation\", \"11_navigation\"],\n [\"09_ux\", \"10_ux\"],\n [\"08_data-store\", \"09_data-store\"],\n [\"07_data-model\", \"08_data-model\"],\n [\"06_api\", \"07_api\"],\n [\"05_technology\", \"06_technology\"],\n [\"04_application\", \"05_application\"],\n [\"03_security\", \"04_security\"],\n ];\n\n const renameErrors: string[] = [];\n\n for (const [oldName, newName] of renameMap) {\n const oldPath = pathModule.join(modelDir, oldName);\n const newPath = pathModule.join(modelDir, newName);\n\n try {\n await fs.rename(oldPath, newPath);\n filesModified++;\n } catch (error) {\n const err = error as { code?: string };\n // ENOENT is expected when a directory hasn't been created yet\n if (err.code === \"ENOENT\") {\n continue;\n }\n // Track non-ENOENT errors to report them in the result\n renameErrors.push(\n `Failed to rename ${oldName} to ${newName}: ${getErrorMessage(error)}`\n );\n }\n }\n\n // If there were any rename failures, include them in the error field\n if (renameErrors.length > 0) {\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)\",\n error: renameErrors.join(\"; \"),\n };\n }\n\n // Create new product layer directory (it will be empty for now)\n // recursive: true handles EEXIST, so no catch needed\n const productPath = pathModule.join(modelDir, \"03_product\");\n await fs.mkdir(productPath, { recursive: true });\n\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)\",\n };\n },\n });\n\n // Migration from v0.9.0 to v0.10.0: objectschema.required type change and relationship catalog rewrites\n this.migrations.push({\n fromVersion: \"0.9.0\",\n toVersion: \"0.10.0\",\n description:\n \"Data Model objectschema.required type change, element type renames and relationship catalog rewrites (Spec v0.10.0)\",\n apply: async (model: Model) => {\n const required = await migrateObjectSchemaRequired(model);\n\n // Element type renames, then relationship rewrites in relationships.yaml and in staged\n // changesets: shared with `dr upgrade` on a model already at the current version\n const catalog = await applyCatalogMigrations(model);\n const errors = [...required.errors, ...catalog.errors];\n\n return {\n migrationsApplied: 1,\n filesModified: required.filesModified + catalog.filesModified,\n description: `Spec version updated to 0.10.0 (objectschema.required converted from string to array; ${catalog.summaries.join(\"; \")})`,\n ...(errors.length > 0 && { error: errors.join(\"; \") }),\n details: catalog.details,\n };\n },\n });\n\n // Migration from v0.10.0 to v0.11.0: the layer rules pass retired relationship types and\n // predicates; CLI 0.2.0 stamped models 0.10.0 with the catalog from before it\n this.migrations.push({\n fromVersion: \"0.10.0\",\n toVersion: \"0.11.0\",\n description: \"Relationship catalog rewrites from the layer rules pass (Spec v0.11.0)\",\n apply: async (model: Model) => {\n const catalog = await applyCatalogMigrations(model);\n return {\n migrationsApplied: 1,\n filesModified: catalog.filesModified,\n description: `Spec version updated to 0.11.0 (${catalog.summaries.join(\"; \")})`,\n ...(catalog.errors.length > 0 && { error: catalog.errors.join(\"; \") }),\n details: catalog.details,\n };\n },\n });\n }\n\n /**\n * Get the latest available specification version\n */\n getLatestVersion(): string {\n if (this.migrations.length === 0) {\n return \"0.7.0\";\n }\n\n // Return the highest toVersion from all migrations using proper version comparison\n const versions = this.migrations.map((m) => m.toVersion);\n return versions.reduce((max, current) => {\n return this.compareVersions(current, max) > 0 ? current : max;\n });\n }\n\n /**\n * Get the sequence of migrations needed from one version to another\n */\n getMigrationPath(fromVersion: string, toVersion?: string): Migration[] {\n const targetVersion = toVersion || this.getLatestVersion();\n\n if (this.compareVersions(fromVersion, targetVersion) >= 0) {\n return [];\n }\n\n const path: Migration[] = [];\n let current = fromVersion;\n\n // Sort migrations by fromVersion\n const sortedMigrations = [...this.migrations].sort((a, b) =>\n this.compareVersions(a.fromVersion, b.fromVersion)\n );\n\n for (const migration of sortedMigrations) {\n // Check if this migration is in our path\n if (\n this.compareVersions(current, migration.fromVersion) >= 0 &&\n this.compareVersions(current, migration.toVersion) < 0 &&\n this.compareVersions(migration.toVersion, targetVersion) <= 0\n ) {\n path.push(migration);\n current = migration.toVersion;\n }\n }\n\n return path;\n }\n\n /**\n * Check if a model requires migration\n */\n requiresMigration(currentVersion: string): boolean {\n const path = this.getMigrationPath(currentVersion);\n return path.length > 0;\n }\n\n /**\n * Apply all migrations in sequence\n */\n async applyMigrations(\n model: Model,\n options: {\n fromVersion: string;\n toVersion?: string;\n dryRun?: boolean;\n validate?: boolean;\n }\n ): Promise<ApplyMigrationsResult> {\n const path = this.getMigrationPath(options.fromVersion, options.toVersion);\n const targetVersion = options.toVersion || this.getLatestVersion();\n\n if (path.length === 0) {\n return {\n applied: [],\n currentVersion: options.fromVersion,\n targetVersion,\n totalChanges: 0,\n };\n }\n\n const results: ApplyMigrationsResult = {\n applied: [],\n currentVersion: options.fromVersion,\n targetVersion,\n totalChanges: 0,\n };\n\n for (const migration of path) {\n if (!options.dryRun) {\n try {\n const result = await migration.apply(model);\n\n results.applied.push({\n from: migration.fromVersion,\n to: migration.toVersion,\n description: migration.description,\n changes: result,\n });\n\n results.totalChanges += result.migrationsApplied;\n\n // Only update model's spec version if migration succeeded (no error field)\n if (!result.error) {\n model.manifest.specVersion = migration.toVersion;\n } else {\n // If migration has errors, throw to prevent partial success state\n throw new Error(result.error);\n }\n } catch (error) {\n throw new Error(\n `Migration ${migration.fromVersion} \u2192 ${migration.toVersion} failed: ${\n getErrorMessage(error)\n }`\n );\n }\n } else {\n // Dry run - just record what would happen\n results.applied.push({\n from: migration.fromVersion,\n to: migration.toVersion,\n description: migration.description,\n dryRun: true,\n });\n }\n }\n\n return results;\n }\n\n /**\n * Get a summary of migrations that would be applied\n */\n getMigrationSummary(\n fromVersion: string,\n toVersion?: string\n ): {\n currentVersion: string;\n targetVersion: string;\n migrationsNeeded: number;\n migrations: Array<{\n from: string;\n to: string;\n description: string;\n }>;\n } {\n const path = this.getMigrationPath(fromVersion, toVersion);\n const targetVersion = toVersion || this.getLatestVersion();\n\n return {\n currentVersion: fromVersion,\n targetVersion,\n migrationsNeeded: path.length,\n migrations: path.map((m) => ({\n from: m.fromVersion,\n to: m.toVersion,\n description: m.description,\n })),\n };\n }\n\n /**\n * Compare two version strings\n * Returns: -1 if a < b, 0 if a == b, 1 if a > b\n */\n private compareVersions(a: string, b: string): number {\n const aParts = a.split(\".\").map(Number);\n const bParts = b.split(\".\").map(Number);\n\n const maxLength = Math.max(aParts.length, bParts.length);\n\n for (let i = 0; i < maxLength; i++) {\n const aPart = aParts[i] || 0;\n const bPart = bParts[i] || 0;\n\n if (aPart < bPart) return -1;\n if (aPart > bPart) return 1;\n }\n\n return 0;\n }\n}\n\n/**\n * Result of applying multiple migrations\n */\nexport interface ApplyMigrationsResult {\n applied: Array<{\n from: string;\n to: string;\n description: string;\n changes?: MigrationResult;\n dryRun?: boolean;\n }>;\n currentVersion: string;\n targetVersion: string;\n totalChanges: number;\n}\n\n/**\n * Spec v0.10.0: data-model.objectschema `required` changed from a comma-separated string to an\n * array of property names. Converts every string value in 08_data-model/*.yaml, and in the\n * data-model layer when the model has it loaded: `dr upgrade` saves the loaded layers after\n * migrating, which would otherwise write the old strings back over the converted files.\n */\nasync function migrateObjectSchemaRequired(\n model: Model\n): Promise<{ filesModified: number; errors: string[] }> {\n const result = await migrateObjectSchemaRequiredFiles(model);\n migrateLoadedObjectSchemaRequired(model);\n return result;\n}\n\n/** A comma-separated `required` string as an array of property names; null for any other value */\nfunction requiredAsArray(required: unknown): string[] | null {\n if (!required || typeof required !== \"string\") {\n return null;\n }\n return required\n .split(\",\")\n .map((item) => item.trim())\n .filter((item) => item.length > 0);\n}\n\nfunction migrateLoadedObjectSchemaRequired(model: Model): void {\n // The committed layer: getLayer() would merge in an active changeset's staged changes\n const layer = model.layers.get(\"data-model\");\n if (!layer) {\n return;\n }\n for (const element of layer.listElements()) {\n if (element.spec_node_id !== \"data-model.objectschema\") {\n continue;\n }\n const required = requiredAsArray(element.attributes.required);\n if (required) {\n element.attributes = { ...element.attributes, required };\n layer.updateElement(element);\n }\n }\n}\n\nasync function migrateObjectSchemaRequiredFiles(\n model: Model\n): Promise<{ filesModified: number; errors: string[] }> {\n const fs = await import(\"fs/promises\");\n const pathModule = await import(\"path\");\n const YAML = await import(\"yaml\");\n\n const dataModelDir = pathModule.join(model.rootPath, \"documentation-robotics/model/08_data-model\");\n let filesModified = 0;\n\n try {\n await fs.access(dataModelDir);\n } catch (error) {\n // ENOENT means the directory doesn't exist - nothing to migrate\n if ((error as { code?: string }).code === \"ENOENT\") {\n return { filesModified: 0, errors: [] };\n }\n throw error;\n }\n\n const files = await fs.readdir(dataModelDir);\n const errors: string[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".yaml\") && !file.endsWith(\".yml\")) {\n continue;\n }\n\n const filePath = pathModule.join(dataModelDir, file);\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n let modified = false;\n const doc = YAML.parse(content);\n\n if (!doc || typeof doc !== \"object\") {\n continue;\n }\n\n for (const [, element] of Object.entries(doc)) {\n if (\n !element ||\n typeof element !== \"object\" ||\n (element as Record<string, unknown>).spec_node_id !== \"data-model.objectschema\"\n ) {\n continue;\n }\n\n const attributes = (element as Record<string, unknown>).attributes as\n | Record<string, unknown>\n | undefined;\n\n if (attributes && typeof attributes === \"object\") {\n const required = requiredAsArray(attributes.required);\n if (required) {\n attributes.required = required;\n modified = true;\n }\n }\n }\n\n if (modified) {\n await fs.writeFile(filePath, YAML.stringify(doc), \"utf-8\");\n filesModified++;\n }\n } catch (error) {\n errors.push(`Failed to migrate ${file}: ${getErrorMessage(error)}`);\n }\n }\n\n return { filesModified, errors };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,102 @@
1
+ import { readdir } from "fs/promises";
2
+ import { Model } from "./model.js";
3
+ import { MigrationRegistry } from "./migration-registry.js";
4
+ import { applyCatalogMigrations } from "./catalog-migration.js";
5
+ import { BaseSnapshotManager } from "./base-snapshot-manager.js";
6
+ import { StagedChangesetStorage } from "./staged-changeset-storage.js";
7
+ import { FileLock } from "../utils/file-lock.js";
8
+ import { getErrorMessage } from "../utils/errors.js";
9
+ async function upgradeModel(startPath, options) {
10
+ const model = await Model.load(startPath, { lazyLoad: !1 }), baselines = await captureChangesetBaselines(model);
11
+ let migration;
12
+ if (options.versionBumpOnly)
13
+ model.manifest.specVersion = options.toVersion, await model.saveManifest();
14
+ else if (options.catalogOnly) {
15
+ const catalog = await applyCatalogMigrations(model);
16
+ if (catalog.errors.length > 0)
17
+ throw new Error(`Catalog migration failed: ${catalog.errors.join("; ")}`);
18
+ migration = {
19
+ applied: [
20
+ {
21
+ from: options.fromVersion,
22
+ to: options.toVersion,
23
+ description: `Catalog changes applied (${catalog.summaries.join("; ")})`,
24
+ changes: {
25
+ migrationsApplied: 1,
26
+ filesModified: catalog.filesModified,
27
+ description: catalog.summaries.join("; "),
28
+ details: catalog.details
29
+ }
30
+ }
31
+ ],
32
+ currentVersion: options.fromVersion,
33
+ targetVersion: options.toVersion,
34
+ totalChanges: 1
35
+ }, await model.saveDirtyLayers();
36
+ } else
37
+ migration = await new MigrationRegistry().applyMigrations(model, {
38
+ fromVersion: options.fromVersion,
39
+ toVersion: options.toVersion,
40
+ dryRun: !1,
41
+ validate: options.validate ?? !0
42
+ }), await model.saveManifest(), await model.saveDirtyLayers();
43
+ return { migration, changesets: await rebaselineChangesets(startPath, baselines) };
44
+ }
45
+ async function captureChangesetBaselines(model) {
46
+ if (model.lazyLoad)
47
+ throw new Error("captureChangesetBaselines needs a model loaded with lazyLoad: false");
48
+ const { changesets, errors } = await listStagedChangesets(model.rootPath), baselines = { snapshot: null, inStep: [], drifted: [], errors };
49
+ if (changesets.length === 0)
50
+ return baselines;
51
+ const snapshot = await new BaseSnapshotManager().captureSnapshot(model);
52
+ baselines.snapshot = snapshot;
53
+ for (const { ref, baseSnapshot } of changesets)
54
+ baseSnapshot && (baseSnapshot === snapshot ? baselines.inStep : baselines.drifted).push(ref);
55
+ return baselines;
56
+ }
57
+ async function rebaselineChangesets(startPath, baselines) {
58
+ const result = {
59
+ rebaselined: [],
60
+ drifted: baselines.drifted,
61
+ errors: [...baselines.errors]
62
+ };
63
+ if (baselines.inStep.length === 0)
64
+ return result;
65
+ const upgraded = await Model.load(startPath, { lazyLoad: !1 }), snapshot = await new BaseSnapshotManager().captureSnapshot(upgraded), storage = new StagedChangesetStorage(upgraded.rootPath);
66
+ for (const ref of baselines.inStep)
67
+ try {
68
+ await new FileLock(storage.getChangesetPath(ref.id)).withLock(async () => {
69
+ const changeset = await storage.load(ref.id);
70
+ !changeset || changeset.status !== "staged" || changeset.baseSnapshot !== baselines.snapshot || (changeset.baseSnapshot = snapshot, changeset.updateModified(), await storage.save(changeset), result.rebaselined.push(ref));
71
+ });
72
+ } catch (error) {
73
+ result.errors.push(`Could not re-baseline changeset '${ref.name}': ${getErrorMessage(error)}; committing it needs --force`);
74
+ }
75
+ return result;
76
+ }
77
+ async function listStagedChangesets(rootPath) {
78
+ const storage = new StagedChangesetStorage(rootPath), changesets = [], errors = [];
79
+ let ids;
80
+ try {
81
+ ids = (await readdir(storage.getChangesetsDir(), { withFileTypes: !0 })).filter((entry) => entry.isDirectory() && !entry.name.endsWith(".lock")).map((entry) => entry.name).sort();
82
+ } catch (error) {
83
+ return error.code !== "ENOENT" && errors.push(`Could not list changesets: ${getErrorMessage(error)}`), { changesets, errors };
84
+ }
85
+ for (const id of ids)
86
+ try {
87
+ const changeset = await storage.load(id);
88
+ changeset?.status === "staged" && changesets.push({
89
+ ref: { id, name: changeset.name || id },
90
+ baseSnapshot: changeset.baseSnapshot
91
+ });
92
+ } catch (error) {
93
+ errors.push(`Could not read changeset '${id}': ${getErrorMessage(error)}`);
94
+ }
95
+ return { changesets, errors };
96
+ }
97
+ export {
98
+ captureChangesetBaselines,
99
+ rebaselineChangesets,
100
+ upgradeModel
101
+ };
102
+ //# sourceMappingURL=model-upgrade.js.map