@open-mercato/cli 0.6.7 → 0.6.8-canary.320.1.bf889f49e4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/.turbo/turbo-build.log +77 -4
  2. package/AGENTS.md +6 -1
  3. package/build.mjs +68 -11
  4. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +32 -88
  5. package/dist/agentic/codex/enforcement-rules.md +2 -16
  6. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +19 -50
  7. package/dist/agentic/cursor/rules/entity-guard.mdc +9 -21
  8. package/dist/agentic/cursor/rules/generated-guard.mdc +3 -5
  9. package/dist/agentic/cursor/rules/open-mercato.mdc +3 -29
  10. package/dist/agentic/guides/ai-workflows.md +76 -0
  11. package/dist/agentic/guides/architecture.md +107 -0
  12. package/dist/agentic/guides/backend-ui.md +86 -0
  13. package/dist/agentic/guides/contracts.md +92 -0
  14. package/dist/agentic/guides/extensions.md +121 -0
  15. package/dist/agentic/guides/framework-extension-points.md +36 -0
  16. package/dist/agentic/guides/integrations.md +81 -0
  17. package/dist/agentic/guides/module-facts.json +116757 -3951
  18. package/dist/agentic/guides/module-system.md +12 -127
  19. package/dist/agentic/guides/modules/ai_assistant.md +227 -28
  20. package/dist/agentic/guides/modules/api_docs.md +58 -2
  21. package/dist/agentic/guides/modules/api_keys.md +92 -7
  22. package/dist/agentic/guides/modules/attachments.md +119 -14
  23. package/dist/agentic/guides/modules/audit_logs.md +131 -10
  24. package/dist/agentic/guides/modules/auth.md +276 -33
  25. package/dist/agentic/guides/modules/business_rules.md +149 -14
  26. package/dist/agentic/guides/modules/catalog.md +482 -43
  27. package/dist/agentic/guides/modules/channel_gmail.md +82 -3
  28. package/dist/agentic/guides/modules/channel_imap.md +82 -3
  29. package/dist/agentic/guides/modules/checkout.md +305 -27
  30. package/dist/agentic/guides/modules/communication_channels.md +315 -33
  31. package/dist/agentic/guides/modules/configs.md +104 -8
  32. package/dist/agentic/guides/modules/content.md +57 -2
  33. package/dist/agentic/guides/modules/currencies.md +152 -17
  34. package/dist/agentic/guides/modules/customer_accounts.md +387 -45
  35. package/dist/agentic/guides/modules/customers.md +746 -85
  36. package/dist/agentic/guides/modules/dashboards.md +155 -10
  37. package/dist/agentic/guides/modules/data_sync.md +149 -16
  38. package/dist/agentic/guides/modules/design_system.md +102 -0
  39. package/dist/agentic/guides/modules/dictionaries.md +107 -12
  40. package/dist/agentic/guides/modules/directory.md +148 -15
  41. package/dist/agentic/guides/modules/entities.md +145 -13
  42. package/dist/agentic/guides/modules/events.md +59 -2
  43. package/dist/agentic/guides/modules/feature_toggles.md +119 -9
  44. package/dist/agentic/guides/modules/gateway_stripe.md +92 -4
  45. package/dist/agentic/guides/modules/generators.md +45 -2
  46. package/dist/agentic/guides/modules/inbox_ops.md +207 -27
  47. package/dist/agentic/guides/modules/integrations.md +149 -16
  48. package/dist/agentic/guides/modules/messages.md +223 -24
  49. package/dist/agentic/guides/modules/notifications.md +96 -8
  50. package/dist/agentic/guides/modules/onboarding.md +77 -6
  51. package/dist/agentic/guides/modules/payment_gateways.md +168 -21
  52. package/dist/agentic/guides/modules/perspectives.md +77 -7
  53. package/dist/agentic/guides/modules/planner.md +136 -18
  54. package/dist/agentic/guides/modules/portal.md +93 -2
  55. package/dist/agentic/guides/modules/progress.md +104 -14
  56. package/dist/agentic/guides/modules/query_index.md +154 -15
  57. package/dist/agentic/guides/modules/record_locks.md +206 -21
  58. package/dist/agentic/guides/modules/resources.md +197 -30
  59. package/dist/agentic/guides/modules/sales.md +687 -84
  60. package/dist/agentic/guides/modules/scheduler.md +133 -14
  61. package/dist/agentic/guides/modules/search.md +170 -6
  62. package/dist/agentic/guides/modules/security.md +294 -35
  63. package/dist/agentic/guides/modules/shipping_carriers.md +134 -18
  64. package/dist/agentic/guides/modules/sso.md +151 -26
  65. package/dist/agentic/guides/modules/staff.md +372 -57
  66. package/dist/agentic/guides/modules/storage_s3.md +95 -5
  67. package/dist/agentic/guides/modules/sync_akeneo.md +85 -3
  68. package/dist/agentic/guides/modules/sync_excel.md +75 -6
  69. package/dist/agentic/guides/modules/system_status_overlays.md +60 -2
  70. package/dist/agentic/guides/modules/translations.md +104 -10
  71. package/dist/agentic/guides/modules/webhooks.md +203 -24
  72. package/dist/agentic/guides/modules/wms.md +469 -47
  73. package/dist/agentic/guides/modules/workflows.md +302 -43
  74. package/dist/agentic/guides/reference-module-facts.json +6942 -0
  75. package/dist/agentic/guides/reference-modules/example.md +462 -0
  76. package/dist/agentic/guides/spec-delivery.md +56 -0
  77. package/dist/agentic/guides/testing-debugging.md +80 -0
  78. package/dist/agentic/guides/upstream/AGENTS.md +300 -0
  79. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +334 -0
  80. package/dist/agentic/guides/upstream/manifest.json +8 -0
  81. package/dist/agentic/shared/AGENTS.md.template +120 -311
  82. package/dist/agentic/shared/ai/agentic.config.json +2 -0
  83. package/dist/agentic/shared/ai/harness/README.md +50 -0
  84. package/dist/agentic/shared/ai/harness/RELEASE.md +59 -0
  85. package/dist/agentic/shared/ai/harness/cases.json +20596 -0
  86. package/dist/agentic/shared/ai/harness/cases.schema.json +196 -0
  87. package/dist/agentic/shared/ai/harness/design-system-inventory.json +6326 -0
  88. package/dist/agentic/shared/ai/harness/fixtures/index.json +51 -0
  89. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +166 -0
  90. package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +21 -0
  91. package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +96 -0
  92. package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +172 -0
  93. package/dist/agentic/shared/ai/harness/knowledge-change.schema.json +228 -0
  94. package/dist/agentic/shared/ai/harness/release-matrix.json +163 -0
  95. package/dist/agentic/shared/ai/harness/release-result.schema.json +219 -0
  96. package/dist/agentic/shared/ai/harness/result.schema.json +136 -0
  97. package/dist/agentic/shared/ai/harness/routing-response.schema.json +22 -0
  98. package/dist/agentic/shared/ai/harness/source-link-inventory.json +1706 -0
  99. package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +73 -0
  100. package/dist/agentic/shared/ai/harness/validators.json +82 -0
  101. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +2121 -0
  102. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +1096 -0
  103. package/dist/agentic/shared/ai/harness/writable-spec-oracles.mjs +602 -0
  104. package/dist/agentic/shared/ai/lessons/_template.md +16 -0
  105. package/dist/agentic/shared/ai/lessons.md +26 -3
  106. package/dist/agentic/shared/ai/review-checklist.md +36 -0
  107. package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +21 -0
  108. package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +4 -0
  109. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +98 -0
  110. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +21 -241
  111. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +31 -0
  112. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +53 -0
  113. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +15 -0
  114. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +11 -0
  115. package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +30 -0
  116. package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +12 -0
  117. package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +13 -0
  118. package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +14 -0
  119. package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +28 -0
  120. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +12 -0
  121. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +22 -0
  122. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +12 -0
  123. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +19 -0
  124. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +17 -601
  125. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +21 -0
  126. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +15 -0
  127. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +16 -0
  128. package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +10 -0
  129. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +15 -310
  130. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +19 -0
  131. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +31 -0
  132. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +100 -0
  133. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +16 -0
  134. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/knowledge-change.md +133 -0
  135. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +17 -0
  136. package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +25 -0
  137. package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +11 -0
  138. package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +13 -0
  139. package/dist/agentic/shared/ai/skills/om-help/SKILL.md +15 -97
  140. package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +18 -0
  141. package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +20 -0
  142. package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +33 -152
  143. package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +33 -0
  144. package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +40 -0
  145. package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +27 -0
  146. package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +24 -0
  147. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +25 -708
  148. package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +16 -0
  149. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +25 -0
  150. package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +15 -0
  151. package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +12 -0
  152. package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +34 -0
  153. package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +4 -0
  154. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +11 -0
  155. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +31 -0
  156. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +39 -0
  157. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +43 -0
  158. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +11 -0
  159. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +20 -820
  160. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +32 -0
  161. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +172 -0
  162. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +14 -0
  163. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +83 -0
  164. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +40 -0
  165. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +11 -0
  166. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +22 -0
  167. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +13 -0
  168. package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +41 -0
  169. package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +19 -0
  170. package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +44 -0
  171. package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +39 -0
  172. package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +20 -0
  173. package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +57 -0
  174. package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +11 -0
  175. package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +486 -0
  176. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +20 -843
  177. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +42 -0
  178. package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +27 -0
  179. package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +13 -0
  180. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +43 -0
  181. package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +11 -0
  182. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +15 -63
  183. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +15 -0
  184. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +17 -467
  185. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +29 -0
  186. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +14 -0
  187. package/dist/agentic/shared/ai/skills/tiers.json +143 -21
  188. package/dist/agentic/shared/ai/skills/tiers.schema.json +52 -40
  189. package/dist/agentic/shared/ai/specs/2026-08-06-reference-module-activation.md +248 -0
  190. package/dist/agentic/shared/ai/specs/README.md +4 -3
  191. package/dist/agentic/shared/ai/specs/SPEC-000-template.md +226 -10
  192. package/dist/agentic/shared/ai/trackers/github.md +108 -4
  193. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +224 -0
  194. package/dist/agentic/shared/scripts/check-lessons.mjs +178 -0
  195. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +3812 -0
  196. package/dist/agentic/shared/scripts/execution-sandbox.mjs +329 -0
  197. package/dist/agentic/shared/scripts/framework-context.mjs +888 -0
  198. package/dist/agentic/shared/scripts/install-skills.mjs +1029 -0
  199. package/dist/agentic/shared/scripts/install-skills.sh +4 -694
  200. package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +222 -0
  201. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +1774 -0
  202. package/dist/agentic/shared/scripts/validate-knowledge-change.mjs +1172 -0
  203. package/dist/bin.js +32 -34
  204. package/dist/bin.js.map +2 -2
  205. package/dist/lib/__integration__/TC-INT-008.spec.js +88 -8
  206. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  207. package/dist/lib/agentic-init.js +13 -3
  208. package/dist/lib/agentic-init.js.map +2 -2
  209. package/dist/lib/agentic-setup.js +434 -186
  210. package/dist/lib/agentic-setup.js.map +2 -2
  211. package/dist/lib/cli-bootstrap-mode.js +32 -0
  212. package/dist/lib/cli-bootstrap-mode.js.map +7 -0
  213. package/dist/lib/dev-supervisor-manifest.js +105 -0
  214. package/dist/lib/dev-supervisor-manifest.js.map +7 -0
  215. package/dist/lib/generate-watch-events.js +15 -0
  216. package/dist/lib/generate-watch-events.js.map +2 -2
  217. package/dist/lib/generators/entity-ids.js +11 -24
  218. package/dist/lib/generators/entity-ids.js.map +2 -2
  219. package/dist/lib/generators/extensions/injection-widgets.js +20 -0
  220. package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
  221. package/dist/lib/generators/module-di.js +10 -23
  222. package/dist/lib/generators/module-di.js.map +2 -2
  223. package/dist/lib/generators/module-entities.js +10 -23
  224. package/dist/lib/generators/module-entities.js.map +2 -2
  225. package/dist/lib/generators/module-extension-facts.js +1882 -0
  226. package/dist/lib/generators/module-extension-facts.js.map +7 -0
  227. package/dist/lib/generators/module-fact-sources.js +70 -0
  228. package/dist/lib/generators/module-fact-sources.js.map +7 -0
  229. package/dist/lib/generators/module-facts-discovery.js +64 -7
  230. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  231. package/dist/lib/generators/module-facts.js +1913 -52
  232. package/dist/lib/generators/module-facts.js.map +3 -3
  233. package/dist/lib/generators/module-override-targets.js +712 -0
  234. package/dist/lib/generators/module-override-targets.js.map +7 -0
  235. package/dist/lib/generators/module-registry.js +405 -36
  236. package/dist/lib/generators/module-registry.js.map +2 -2
  237. package/dist/lib/generators/route-manifest-shards.js +170 -0
  238. package/dist/lib/generators/route-manifest-shards.js.map +7 -0
  239. package/dist/lib/in-process-generate-watcher.js +12 -3
  240. package/dist/lib/in-process-generate-watcher.js.map +2 -2
  241. package/dist/lib/load-env.js +24 -0
  242. package/dist/lib/load-env.js.map +7 -0
  243. package/dist/lib/queue-worker-supervisor.js.map +2 -2
  244. package/dist/lib/resolver.js +28 -3
  245. package/dist/lib/resolver.js.map +2 -2
  246. package/dist/lib/telemetry-init.js +358 -0
  247. package/dist/lib/telemetry-init.js.map +7 -0
  248. package/dist/lib/testing/integration.js +5 -2
  249. package/dist/lib/testing/integration.js.map +2 -2
  250. package/dist/lib/utils.js +15 -8
  251. package/dist/lib/utils.js.map +2 -2
  252. package/dist/mercato.js +35 -9
  253. package/dist/mercato.js.map +2 -2
  254. package/package.json +13 -11
  255. package/src/__tests__/mercato.test.ts +144 -0
  256. package/src/bin.ts +60 -42
  257. package/src/lib/__integration__/TC-INT-008.spec.ts +107 -9
  258. package/src/lib/__tests__/agentic-init.test.ts +41 -5
  259. package/src/lib/__tests__/agentic-setup.ownership.test.ts +386 -0
  260. package/src/lib/__tests__/cli-bootstrap-mode.test.ts +22 -0
  261. package/src/lib/__tests__/content-stable-writes.test.ts +74 -0
  262. package/src/lib/__tests__/dev-supervisor-manifest.test.ts +131 -0
  263. package/src/lib/__tests__/generate-watch-events.test.ts +76 -0
  264. package/src/lib/__tests__/in-process-generate-watcher.test.ts +52 -1
  265. package/src/lib/__tests__/injection-widget-id-validation.test.ts +184 -0
  266. package/src/lib/__tests__/load-env.test.ts +65 -0
  267. package/src/lib/__tests__/queue-worker-supervisor.test.ts +33 -0
  268. package/src/lib/__tests__/resolver.enterprise.test.ts +50 -0
  269. package/src/lib/__tests__/telemetry-init.test.ts +298 -0
  270. package/src/lib/agentic-init.ts +15 -3
  271. package/src/lib/agentic-setup.ts +573 -222
  272. package/src/lib/cli-bootstrap-mode.ts +31 -0
  273. package/src/lib/dev-supervisor-manifest.ts +142 -0
  274. package/src/lib/generate-watch-events.ts +14 -0
  275. package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +92 -0
  276. package/src/lib/generators/__tests__/entity-ids.test.ts +32 -0
  277. package/src/lib/generators/__tests__/helpers/override-target-diagnostic-fixtures.ts +150 -0
  278. package/src/lib/generators/__tests__/module-extension-activations.test.ts +762 -0
  279. package/src/lib/generators/__tests__/module-extension-facts.test.ts +603 -0
  280. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +139 -2
  281. package/src/lib/generators/__tests__/module-facts.computed-di-token.test.ts +88 -0
  282. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +1 -0
  283. package/src/lib/generators/__tests__/module-facts.discovery.test.ts +54 -10
  284. package/src/lib/generators/__tests__/module-facts.example-fact-coverage.test.ts +414 -0
  285. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +108 -0
  286. package/src/lib/generators/__tests__/module-facts.local-reference.test.ts +393 -0
  287. package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +561 -0
  288. package/src/lib/generators/__tests__/module-facts.search-entity-id.test.ts +151 -0
  289. package/src/lib/generators/__tests__/module-facts.source-links.test.ts +160 -0
  290. package/src/lib/generators/__tests__/module-override-targets.diagnostic-wiring.test.ts +92 -0
  291. package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +149 -0
  292. package/src/lib/generators/__tests__/module-override-targets.test.ts +328 -0
  293. package/src/lib/generators/__tests__/module-override-targets.unknown-mode.test.ts +117 -0
  294. package/src/lib/generators/__tests__/module-subset.test.ts +266 -0
  295. package/src/lib/generators/__tests__/output-snapshots.test.ts +3 -3
  296. package/src/lib/generators/__tests__/route-manifest-shards.test.ts +62 -0
  297. package/src/lib/generators/entity-ids.ts +10 -26
  298. package/src/lib/generators/extensions/injection-widgets.ts +51 -0
  299. package/src/lib/generators/module-di.ts +9 -25
  300. package/src/lib/generators/module-entities.ts +9 -25
  301. package/src/lib/generators/module-extension-facts.ts +2472 -0
  302. package/src/lib/generators/module-fact-sources.ts +108 -0
  303. package/src/lib/generators/module-facts-discovery.ts +104 -7
  304. package/src/lib/generators/module-facts.ts +2747 -138
  305. package/src/lib/generators/module-override-targets.ts +907 -0
  306. package/src/lib/generators/module-registry.ts +448 -35
  307. package/src/lib/generators/route-manifest-shards.ts +146 -0
  308. package/src/lib/in-process-generate-watcher.ts +13 -4
  309. package/src/lib/load-env.ts +30 -0
  310. package/src/lib/queue-worker-supervisor.ts +7 -2
  311. package/src/lib/resolver.ts +34 -3
  312. package/src/lib/telemetry-init.ts +461 -0
  313. package/src/lib/testing/__tests__/integration.test.ts +4 -0
  314. package/src/lib/testing/integration.ts +5 -2
  315. package/src/lib/utils.ts +24 -16
  316. package/src/mercato.ts +46 -9
  317. package/dist/agentic/guides/cache.md +0 -45
  318. package/dist/agentic/guides/core.md +0 -453
  319. package/dist/agentic/guides/events.md +0 -117
  320. package/dist/agentic/guides/queue.md +0 -53
  321. package/dist/agentic/guides/search.md +0 -115
  322. package/dist/agentic/guides/shared.md +0 -108
  323. package/dist/agentic/guides/ui.md +0 -347
  324. package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +0 -27
  325. package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +0 -27
  326. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +0 -260
  327. package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +0 -146
  328. package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +0 -84
  329. package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +0 -193
  330. package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +0 -34
  331. package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +0 -762
  332. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +0 -50
  333. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +0 -97
  334. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +0 -271
  335. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +0 -101
@@ -0,0 +1,386 @@
1
+ import { createHash } from 'node:crypto'
2
+ import {
3
+ mkdtempSync,
4
+ mkdirSync,
5
+ readdirSync,
6
+ existsSync,
7
+ readFileSync,
8
+ rmSync,
9
+ statSync,
10
+ symlinkSync,
11
+ writeFileSync,
12
+ } from 'node:fs'
13
+ import { tmpdir } from 'node:os'
14
+ import { dirname, join } from 'node:path'
15
+
16
+ import { applyHarnessUpdate, runAgenticSetup } from '../agentic-setup'
17
+
18
+ type ManifestEntry = {
19
+ path: string
20
+ sha256: string
21
+ source: string
22
+ userEditable: boolean
23
+ }
24
+
25
+ function write(path: string, content: string): void {
26
+ mkdirSync(dirname(path), { recursive: true })
27
+ writeFileSync(path, content)
28
+ }
29
+
30
+ function hash(content: string): string {
31
+ return createHash('sha256').update(content).digest('hex')
32
+ }
33
+
34
+ function entry(path: string, content: string): ManifestEntry {
35
+ return {
36
+ path,
37
+ sha256: hash(content),
38
+ source: 'generated',
39
+ userEditable: false,
40
+ }
41
+ }
42
+
43
+ function writeManifest(root: string, files: ManifestEntry[]): string {
44
+ const path = join(root, '.ai', 'harness', 'manifest.json')
45
+ write(
46
+ path,
47
+ `${JSON.stringify({ version: 1, generator: 'open-mercato-agentic', files }, null, 2)}\n`,
48
+ )
49
+ return path
50
+ }
51
+
52
+ describe('applyHarnessUpdate', () => {
53
+ let tempRoot: string
54
+ let targetDir: string
55
+ let stagingDir: string
56
+
57
+ beforeEach(() => {
58
+ tempRoot = mkdtempSync(join(tmpdir(), 'agentic-ownership-'))
59
+ targetDir = join(tempRoot, 'app')
60
+ stagingDir = join(tempRoot, 'candidate')
61
+ mkdirSync(targetDir, { recursive: true })
62
+ mkdirSync(stagingDir, { recursive: true })
63
+ })
64
+
65
+ afterEach(() => {
66
+ rmSync(tempRoot, { recursive: true, force: true })
67
+ })
68
+
69
+ it('updates unchanged owned files while preserving modified and unknown files', () => {
70
+ const oldUnchanged = 'old generated content\n'
71
+ const oldExpectedModified = 'old generated editable content\n'
72
+ const userModified = 'user customization\n'
73
+ const unknownUserContent = 'private local skill\n'
74
+ const oldMissing = 'previously generated then deleted\n'
75
+
76
+ write(join(targetDir, '.ai', 'owned-unchanged.md'), oldUnchanged)
77
+ write(join(targetDir, '.ai', 'owned-modified.md'), userModified)
78
+ write(join(targetDir, '.ai', 'unknown-collision.md'), unknownUserContent)
79
+ write(join(targetDir, '.ai', 'skills', 'custom-local', 'SKILL.md'), 'keep me\n')
80
+ writeManifest(targetDir, [
81
+ entry('.ai/owned-unchanged.md', oldUnchanged),
82
+ entry('.ai/owned-modified.md', oldExpectedModified),
83
+ entry('.ai/missing-owned.md', oldMissing),
84
+ ])
85
+
86
+ const nextFiles = new Map<string, string>([
87
+ ['.ai/owned-unchanged.md', 'new generated content\n'],
88
+ ['.ai/owned-modified.md', 'new generated editable content\n'],
89
+ ['.ai/unknown-collision.md', 'new generated collision\n'],
90
+ ['.ai/missing-owned.md', 'recreated generated content\n'],
91
+ ])
92
+ for (const [relativePath, content] of nextFiles) {
93
+ write(join(stagingDir, relativePath), content)
94
+ }
95
+ writeManifest(
96
+ stagingDir,
97
+ [...nextFiles].map(([relativePath, content]) => entry(relativePath, content)),
98
+ )
99
+
100
+ const conflicts = applyHarnessUpdate(targetDir, stagingDir)
101
+
102
+ expect(conflicts).toEqual([
103
+ { path: '.ai/owned-modified.md', candidate: '.ai/owned-modified.md.incoming' },
104
+ { path: '.ai/unknown-collision.md', candidate: '.ai/unknown-collision.md.incoming' },
105
+ ])
106
+ expect(readFileSync(join(targetDir, '.ai', 'owned-unchanged.md'), 'utf8')).toBe(
107
+ nextFiles.get('.ai/owned-unchanged.md'),
108
+ )
109
+ expect(readFileSync(join(targetDir, '.ai', 'owned-modified.md'), 'utf8')).toBe(userModified)
110
+ expect(readFileSync(join(targetDir, '.ai', 'owned-modified.md.incoming'), 'utf8')).toBe(
111
+ nextFiles.get('.ai/owned-modified.md'),
112
+ )
113
+ expect(readFileSync(join(targetDir, '.ai', 'unknown-collision.md'), 'utf8')).toBe(
114
+ unknownUserContent,
115
+ )
116
+ expect(readFileSync(join(targetDir, '.ai', 'unknown-collision.md.incoming'), 'utf8')).toBe(
117
+ nextFiles.get('.ai/unknown-collision.md'),
118
+ )
119
+ expect(readFileSync(join(targetDir, '.ai', 'missing-owned.md'), 'utf8')).toBe(
120
+ nextFiles.get('.ai/missing-owned.md'),
121
+ )
122
+ expect(readFileSync(join(targetDir, '.ai', 'skills', 'custom-local', 'SKILL.md'), 'utf8')).toBe(
123
+ 'keep me\n',
124
+ )
125
+ expect(readFileSync(join(targetDir, '.ai', 'harness', 'manifest.json'), 'utf8')).toBe(
126
+ readFileSync(join(stagingDir, '.ai', 'harness', 'manifest.json'), 'utf8'),
127
+ )
128
+ })
129
+
130
+ it('never overwrites a user-edited incoming candidate on a later update', () => {
131
+ const userFile = 'user-owned harness\n'
132
+ const firstCandidate = 'first generated candidate\n'
133
+ const secondCandidate = 'second generated candidate\n'
134
+ write(join(targetDir, '.ai', 'owned.md'), userFile)
135
+ writeManifest(targetDir, [entry('.ai/owned.md', 'original generated file\n')])
136
+ write(join(stagingDir, '.ai', 'owned.md'), firstCandidate)
137
+ writeManifest(stagingDir, [entry('.ai/owned.md', firstCandidate)])
138
+
139
+ expect(applyHarnessUpdate(targetDir, stagingDir)).toEqual([
140
+ { path: '.ai/owned.md', candidate: '.ai/owned.md.incoming' },
141
+ ])
142
+ write(join(targetDir, '.ai', 'owned.md.incoming'), 'user edited the candidate\n')
143
+ write(join(stagingDir, '.ai', 'owned.md'), secondCandidate)
144
+ writeManifest(stagingDir, [entry('.ai/owned.md', secondCandidate)])
145
+
146
+ expect(applyHarnessUpdate(targetDir, stagingDir)).toEqual([
147
+ { path: '.ai/owned.md', candidate: '.ai/owned.md.incoming.2' },
148
+ ])
149
+ expect(readFileSync(join(targetDir, '.ai', 'owned.md'), 'utf8')).toBe(userFile)
150
+ expect(readFileSync(join(targetDir, '.ai', 'owned.md.incoming'), 'utf8')).toBe('user edited the candidate\n')
151
+ expect(readFileSync(join(targetDir, '.ai', 'owned.md.incoming.2'), 'utf8')).toBe(secondCandidate)
152
+ })
153
+
154
+ it('removes retired owned files but preserves modified retired assets with ownership', () => {
155
+ const unchanged = '.claude/generated.json'
156
+ const modified = 'CLAUDE.md'
157
+ write(join(targetDir, unchanged), 'old generated setting\n')
158
+ write(join(targetDir, modified), 'locally edited instructions\n')
159
+ writeManifest(targetDir, [
160
+ entry(unchanged, 'old generated setting\n'),
161
+ entry(modified, 'old generated instructions\n'),
162
+ ])
163
+ writeManifest(stagingDir, [])
164
+
165
+ expect(applyHarnessUpdate(targetDir, stagingDir)).toEqual([
166
+ { path: modified, candidate: null },
167
+ ])
168
+ expect(existsSync(join(targetDir, unchanged))).toBe(false)
169
+ expect(readFileSync(join(targetDir, modified), 'utf8')).toBe('locally edited instructions\n')
170
+ const manifest = JSON.parse(readFileSync(join(targetDir, '.ai', 'harness', 'manifest.json'), 'utf8')) as {
171
+ files: ManifestEntry[]
172
+ }
173
+ expect(manifest.files.map((item) => item.path)).toEqual([modified])
174
+ })
175
+
176
+ it('leaves the prior manifest and app files untouched when candidate validation fails', () => {
177
+ write(join(targetDir, '.ai', 'owned.md'), 'old\n')
178
+ const oldManifestPath = writeManifest(targetDir, [entry('.ai/owned.md', 'old\n')])
179
+ const oldManifest = readFileSync(oldManifestPath, 'utf8')
180
+ writeManifest(stagingDir, [entry('.ai/missing-candidate.md', 'not written\n')])
181
+
182
+ expect(() => applyHarnessUpdate(targetDir, stagingDir)).toThrow(
183
+ 'Generated harness candidate is invalid for ".ai/missing-candidate.md".',
184
+ )
185
+ expect(readFileSync(join(targetDir, '.ai', 'owned.md'), 'utf8')).toBe('old\n')
186
+ expect(readFileSync(oldManifestPath, 'utf8')).toBe(oldManifest)
187
+ })
188
+
189
+ it('rejects manifest paths that escape the app root before publishing', () => {
190
+ const oldManifestPath = writeManifest(targetDir, [])
191
+ const oldManifest = readFileSync(oldManifestPath, 'utf8')
192
+ writeManifest(stagingDir, [entry('../escape.md', 'escape\n')])
193
+
194
+ expect(() => applyHarnessUpdate(targetDir, stagingDir)).toThrow(
195
+ 'Generated harness candidate is invalid for "../escape.md".',
196
+ )
197
+ expect(readFileSync(oldManifestPath, 'utf8')).toBe(oldManifest)
198
+ })
199
+
200
+ it('rejects a symlinked managed ancestor without writing outside the app', () => {
201
+ if (process.platform === 'win32') return
202
+ const outsideDir = join(tempRoot, 'outside')
203
+ mkdirSync(join(outsideDir, 'harness'), { recursive: true })
204
+ write(join(outsideDir, 'sentinel.txt'), 'outside sentinel\n')
205
+ write(
206
+ join(outsideDir, 'harness', 'manifest.json'),
207
+ `${JSON.stringify({ version: 1, generator: 'outside', files: [] }, null, 2)}\n`,
208
+ )
209
+ const nextContent = 'must stay inside the app\n'
210
+ write(join(stagingDir, '.ai', 'owned.md'), nextContent)
211
+ writeManifest(stagingDir, [entry('.ai/owned.md', nextContent)])
212
+ symlinkSync(outsideDir, join(targetDir, '.ai'), 'dir')
213
+
214
+ expect(() => applyHarnessUpdate(targetDir, stagingDir)).toThrow(
215
+ 'Harness-managed path contains a symbolic-link component',
216
+ )
217
+ expect(readFileSync(join(outsideDir, 'sentinel.txt'), 'utf8')).toBe('outside sentinel\n')
218
+ expect(existsSync(join(outsideDir, 'owned.md'))).toBe(false)
219
+ expect(readdirSync(outsideDir).sort()).toEqual(['harness', 'sentinel.txt'])
220
+ })
221
+ })
222
+
223
+ describe('runAgenticSetup ownership modes', () => {
224
+ let appDir: string
225
+ let previousSkipExternal: string | undefined
226
+
227
+ beforeEach(() => {
228
+ appDir = mkdtempSync(join(tmpdir(), 'agentic-setup-app-'))
229
+ write(
230
+ join(appDir, 'src', 'modules.ts'),
231
+ "export const enabledModules: Array<{ id: string }> = []\n",
232
+ )
233
+ previousSkipExternal = process.env.OM_SKIP_EXTERNAL_SKILLS
234
+ process.env.OM_SKIP_EXTERNAL_SKILLS = '1'
235
+ jest.spyOn(console, 'log').mockImplementation()
236
+ jest.spyOn(console, 'warn').mockImplementation()
237
+ })
238
+
239
+ afterEach(() => {
240
+ if (previousSkipExternal === undefined) delete process.env.OM_SKIP_EXTERNAL_SKILLS
241
+ else process.env.OM_SKIP_EXTERNAL_SKILLS = previousSkipExternal
242
+ jest.restoreAllMocks()
243
+ rmSync(appDir, { recursive: true, force: true })
244
+ })
245
+
246
+ it('preserves local edits, recreates missing owned files, and reports incoming candidates', async () => {
247
+ const ask = async () => ''
248
+ await runAgenticSetup(appDir, ask, { tool: 'codex' })
249
+ const originalGuide = readFileSync(join(appDir, '.ai', 'guides', 'architecture.md'), 'utf8')
250
+ write(join(appDir, 'AGENTS.md'), '# My local harness rules\n')
251
+ write(join(appDir, '.ai', 'skills', 'my-private-skill', 'SKILL.md'), '# Private\n')
252
+ rmSync(join(appDir, '.ai', 'guides', 'architecture.md'))
253
+
254
+ await runAgenticSetup(appDir, ask, { tool: 'codex', updateHarness: true })
255
+
256
+ expect(readFileSync(join(appDir, 'AGENTS.md'), 'utf8')).toBe('# My local harness rules\n')
257
+ expect(readFileSync(join(appDir, 'AGENTS.md.incoming'), 'utf8')).toContain(
258
+ '<!-- CODEX_ENFORCEMENT_RULES_START -->',
259
+ )
260
+ expect(readFileSync(join(appDir, '.ai', 'guides', 'architecture.md'), 'utf8')).toBe(originalGuide)
261
+ expect(readFileSync(join(appDir, '.ai', 'skills', 'my-private-skill', 'SKILL.md'), 'utf8')).toBe(
262
+ '# Private\n',
263
+ )
264
+ expect(existsSync(join(appDir, '.ai', 'harness', 'manifest.json'))).toBe(true)
265
+ const manifest = JSON.parse(
266
+ readFileSync(join(appDir, '.ai', 'harness', 'manifest.json'), 'utf8'),
267
+ ) as { files: ManifestEntry[] }
268
+ expect(manifest.files.find((item) => item.path === '.ai/lessons.md')?.userEditable).toBe(true)
269
+ expect(manifest.files.find((item) => item.path === '.ai/lessons/_template.md')?.userEditable).toBe(true)
270
+ expect(existsSync(join(appDir, 'scripts', 'check-lessons.mjs'))).toBe(true)
271
+ if (process.platform !== 'win32') {
272
+ expect(statSync(join(appDir, 'scripts', 'install-skills.sh')).mode & 0o111).not.toBe(0)
273
+ }
274
+ })
275
+
276
+ it('keeps force semantics by replacing exact generated targets', async () => {
277
+ const ask = async () => ''
278
+ await runAgenticSetup(appDir, ask, { tool: 'codex' })
279
+ write(join(appDir, 'AGENTS.md'), '# My local harness rules\n')
280
+
281
+ await runAgenticSetup(appDir, ask, { tool: 'codex', force: true })
282
+
283
+ expect(readFileSync(join(appDir, 'AGENTS.md'), 'utf8')).toContain(
284
+ '<!-- CODEX_ENFORCEMENT_RULES_START -->',
285
+ )
286
+ expect(existsSync(join(appDir, 'AGENTS.md.incoming'))).toBe(false)
287
+ })
288
+
289
+ it.each([
290
+ ['top-level .ai', '.ai'],
291
+ ['top-level scripts', 'scripts'],
292
+ ['nested guides', join('.ai', 'guides')],
293
+ ])('rejects a symlinked %s destination on initial setup before writing outside the app', async (_label, linkedRelative) => {
294
+ if (process.platform === 'win32') return
295
+ const targetDir = join(appDir, linkedRelative.replaceAll('/', '-'))
296
+ const outsideDir = join(appDir, `outside-initial-${linkedRelative.replaceAll('/', '-')}`)
297
+ write(
298
+ join(targetDir, 'src', 'modules.ts'),
299
+ "export const enabledModules: Array<{ id: string }> = []\n",
300
+ )
301
+ write(join(outsideDir, 'sentinel.txt'), 'outside sentinel\n')
302
+ const linkedPath = join(targetDir, linkedRelative)
303
+ mkdirSync(dirname(linkedPath), { recursive: true })
304
+ symlinkSync(outsideDir, linkedPath, 'dir')
305
+
306
+ await expect(runAgenticSetup(targetDir, async () => '', { tool: 'codex' })).rejects.toThrow(
307
+ 'Harness-managed path contains a symbolic-link component',
308
+ )
309
+ expect(readFileSync(join(outsideDir, 'sentinel.txt'), 'utf8')).toBe('outside sentinel\n')
310
+ expect(readdirSync(outsideDir)).toEqual(['sentinel.txt'])
311
+ expect(existsSync(join(targetDir, 'AGENTS.md'))).toBe(false)
312
+ })
313
+
314
+ it.each([
315
+ ['top-level .ai', '.ai'],
316
+ ['top-level scripts', 'scripts'],
317
+ ['nested guides', join('.ai', 'guides')],
318
+ ])('rejects a symlinked %s destination on forced setup before writing outside the app', async (_label, linkedRelative) => {
319
+ if (process.platform === 'win32') return
320
+ const targetDir = join(appDir, `force-${linkedRelative.replaceAll('/', '-')}`)
321
+ const outsideDir = join(appDir, `outside-force-${linkedRelative.replaceAll('/', '-')}`)
322
+ write(
323
+ join(targetDir, 'src', 'modules.ts'),
324
+ "export const enabledModules: Array<{ id: string }> = []\n",
325
+ )
326
+ await runAgenticSetup(targetDir, async () => '', { tool: 'codex' })
327
+ const originalAgents = readFileSync(join(targetDir, 'AGENTS.md'), 'utf8')
328
+ write(join(outsideDir, 'sentinel.txt'), 'outside sentinel\n')
329
+ const linkedPath = join(targetDir, linkedRelative)
330
+ rmSync(linkedPath, { recursive: true, force: true })
331
+ mkdirSync(dirname(linkedPath), { recursive: true })
332
+ symlinkSync(outsideDir, linkedPath, 'dir')
333
+
334
+ await expect(
335
+ runAgenticSetup(targetDir, async () => '', { tool: 'codex', force: true }),
336
+ ).rejects.toThrow('Harness-managed path contains a symbolic-link component')
337
+ expect(readFileSync(join(outsideDir, 'sentinel.txt'), 'utf8')).toBe('outside sentinel\n')
338
+ expect(readdirSync(outsideDir)).toEqual(['sentinel.txt'])
339
+ expect(readFileSync(join(targetDir, 'AGENTS.md'), 'utf8')).toBe(originalAgents)
340
+ })
341
+
342
+ it('updates the harness without crashing when src/modules.ts is missing', async () => {
343
+ rmSync(join(appDir, 'src', 'modules.ts'))
344
+
345
+ await expect(runAgenticSetup(appDir, async () => '', {
346
+ tool: 'codex',
347
+ updateHarness: true,
348
+ })).resolves.toBeUndefined()
349
+
350
+ expect(existsSync(join(appDir, 'src', 'modules.ts'))).toBe(false)
351
+ expect(existsSync(join(appDir, '.ai', 'harness', 'manifest.json'))).toBe(true)
352
+ })
353
+
354
+ it('removes unmodified tool-specific assets when switching tools', async () => {
355
+ const ask = async () => ''
356
+ await runAgenticSetup(appDir, ask, { tool: 'claude-code' })
357
+ expect(existsSync(join(appDir, 'CLAUDE.md'))).toBe(true)
358
+ expect(existsSync(join(appDir, '.claude', 'settings.json'))).toBe(true)
359
+
360
+ await runAgenticSetup(appDir, ask, { tool: 'codex', updateHarness: true })
361
+
362
+ expect(existsSync(join(appDir, 'CLAUDE.md'))).toBe(false)
363
+ expect(existsSync(join(appDir, '.claude', 'settings.json'))).toBe(false)
364
+ expect(existsSync(join(appDir, '.codex', 'mcp.json.example'))).toBe(true)
365
+ })
366
+
367
+ it('preserves the persisted tool selection on a bare harness update without prompting', async () => {
368
+ const initialAsk = async () => ''
369
+ await runAgenticSetup(appDir, initialAsk, { tool: 'claude-code,codex' })
370
+ const ask = jest.fn(async () => {
371
+ throw new Error('bare update must not prompt for a new tool selection')
372
+ })
373
+
374
+ await runAgenticSetup(appDir, ask, { updateHarness: true })
375
+
376
+ expect(ask).not.toHaveBeenCalled()
377
+ expect(existsSync(join(appDir, 'CLAUDE.md'))).toBe(true)
378
+ expect(existsSync(join(appDir, '.claude', 'settings.json'))).toBe(true)
379
+ expect(existsSync(join(appDir, '.codex', 'mcp.json.example'))).toBe(true)
380
+ expect(existsSync(join(appDir, '.cursor', 'hooks.json'))).toBe(false)
381
+ const tiers = JSON.parse(readFileSync(join(appDir, '.ai', 'skills', 'tiers.json'), 'utf8')) as {
382
+ agents?: { ignore?: string[] }
383
+ }
384
+ expect(tiers.agents?.ignore).toEqual(['cursor'])
385
+ })
386
+ })
@@ -0,0 +1,22 @@
1
+ import { resolveCliBootstrapMode } from '../cli-bootstrap-mode'
2
+
3
+ describe('resolveCliBootstrapMode', () => {
4
+ it('uses the lightweight manifest only for the exact server dev command', () => {
5
+ expect(resolveCliBootstrapMode(['node', 'mercato', 'server', 'dev'])).toBe('dev-supervisor')
6
+ expect(resolveCliBootstrapMode(['node', 'mercato', 'server', 'dev', '--port', '4000'])).toBe('dev-supervisor')
7
+ })
8
+
9
+ it('keeps production, workers, scheduler, and module commands on the full bootstrap', () => {
10
+ expect(resolveCliBootstrapMode(['node', 'mercato', 'server', 'start'])).toBe('full')
11
+ expect(resolveCliBootstrapMode(['node', 'mercato', 'queue', 'worker', '--all'])).toBe('full')
12
+ expect(resolveCliBootstrapMode(['node', 'mercato', 'scheduler', 'start'])).toBe('full')
13
+ expect(resolveCliBootstrapMode(['node', 'mercato', 'customers', 'seed'])).toBe('full')
14
+ })
15
+
16
+ it('preserves existing bootstrap-free commands', () => {
17
+ expect(resolveCliBootstrapMode(['node', 'mercato', 'generate'])).toBe('none')
18
+ expect(resolveCliBootstrapMode(['node', 'mercato', 'db', 'generate'])).toBe('none')
19
+ expect(resolveCliBootstrapMode(['node', 'mercato', 'help'])).toBe('none')
20
+ expect(resolveCliBootstrapMode(['node', 'mercato'])).toBe('none')
21
+ })
22
+ })
@@ -0,0 +1,74 @@
1
+ import fs from 'node:fs'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+ import { createGeneratorResult, readChecksumRecord, writeGeneratedFile } from '../utils'
5
+
6
+ describe('content-stable generated writes', () => {
7
+ let tmpDir: string
8
+
9
+ beforeEach(() => {
10
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'generated-write-test-'))
11
+ })
12
+
13
+ afterEach(() => {
14
+ fs.rmSync(tmpDir, { recursive: true, force: true })
15
+ })
16
+
17
+ it('updates structure checksum state without touching byte-identical generated output', () => {
18
+ const outFile = path.join(tmpDir, 'modules.generated.ts')
19
+ const checksumFile = path.join(tmpDir, 'modules.generated.checksum')
20
+ const content = '// generated\nexport const modules = []\n'
21
+
22
+ writeGeneratedFile({
23
+ outFile,
24
+ checksumFile,
25
+ content,
26
+ structureChecksum: 'structure-a',
27
+ result: createGeneratorResult(),
28
+ quiet: true,
29
+ })
30
+
31
+ const oldTime = new Date(Date.now() - 60_000)
32
+ fs.utimesSync(outFile, oldTime, oldTime)
33
+ fs.utimesSync(checksumFile, oldTime, oldTime)
34
+ const outputMtimeBefore = fs.statSync(outFile).mtimeMs
35
+
36
+ const result = createGeneratorResult()
37
+ writeGeneratedFile({
38
+ outFile,
39
+ checksumFile,
40
+ content,
41
+ structureChecksum: 'structure-b',
42
+ result,
43
+ quiet: true,
44
+ })
45
+
46
+ expect(result.filesWritten).toEqual([])
47
+ expect(result.filesUnchanged).toEqual([outFile])
48
+ expect(fs.statSync(outFile).mtimeMs).toBe(outputMtimeBefore)
49
+ expect(fs.statSync(checksumFile).mtimeMs).toBeGreaterThan(oldTime.getTime())
50
+ expect(readChecksumRecord(checksumFile)?.structure).toBe('structure-b')
51
+ })
52
+
53
+ it('rewrites generated output when its actual bytes change', () => {
54
+ const outFile = path.join(tmpDir, 'modules.generated.ts')
55
+ const checksumFile = path.join(tmpDir, 'modules.generated.checksum')
56
+ fs.writeFileSync(outFile, 'stale output\n')
57
+
58
+ const result = createGeneratorResult()
59
+ const content = '// generated\nexport const modules = ["customers"]\n'
60
+ writeGeneratedFile({
61
+ outFile,
62
+ checksumFile,
63
+ content,
64
+ structureChecksum: 'structure-c',
65
+ result,
66
+ quiet: true,
67
+ })
68
+
69
+ expect(result.filesWritten).toEqual([outFile])
70
+ expect(result.filesUnchanged).toEqual([])
71
+ expect(fs.readFileSync(outFile, 'utf8')).toBe(content)
72
+ expect(readChecksumRecord(checksumFile)?.structure).toBe('structure-c')
73
+ })
74
+ })
@@ -0,0 +1,131 @@
1
+ import fs from 'node:fs'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+ import {
5
+ canUseLightweightDevSupervisor,
6
+ DEV_SUPERVISOR_MANIFEST_FILE,
7
+ getRegisteredDevSupervisorManifest,
8
+ loadDevSupervisorManifest,
9
+ registerDevSupervisorManifest,
10
+ resetDevSupervisorManifestForTests,
11
+ } from '../dev-supervisor-manifest'
12
+
13
+ function writeManifest(appDir: string, value: unknown): string {
14
+ const filePath = path.join(appDir, '.mercato', 'generated', DEV_SUPERVISOR_MANIFEST_FILE)
15
+ fs.mkdirSync(path.dirname(filePath), { recursive: true })
16
+ fs.writeFileSync(filePath, JSON.stringify(value), 'utf8')
17
+ return filePath
18
+ }
19
+
20
+ describe('dev supervisor manifest', () => {
21
+ let appDir: string
22
+
23
+ beforeEach(() => {
24
+ appDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mercato-dev-supervisor-'))
25
+ resetDevSupervisorManifestForTests()
26
+ })
27
+
28
+ afterEach(() => {
29
+ resetDevSupervisorManifestForTests()
30
+ fs.rmSync(appDir, { recursive: true, force: true })
31
+ })
32
+
33
+ it('loads and registers primitive worker and scheduler metadata', () => {
34
+ writeManifest(appDir, {
35
+ version: 1,
36
+ workers: [
37
+ {
38
+ id: 'events:workers:dispatch',
39
+ moduleId: 'events',
40
+ queue: 'events',
41
+ concurrency: 2,
42
+ },
43
+ ],
44
+ schedulerStartStatus: 'ok',
45
+ requiresFullBootstrap: false,
46
+ })
47
+
48
+ const manifest = loadDevSupervisorManifest(appDir)
49
+ expect(manifest).toEqual({
50
+ version: 1,
51
+ workers: [
52
+ {
53
+ id: 'events:workers:dispatch',
54
+ moduleId: 'events',
55
+ queue: 'events',
56
+ concurrency: 2,
57
+ },
58
+ ],
59
+ schedulerStartStatus: 'ok',
60
+ requiresFullBootstrap: false,
61
+ })
62
+
63
+ expect(canUseLightweightDevSupervisor(manifest)).toBe(true)
64
+ registerDevSupervisorManifest(manifest)
65
+ expect(getRegisteredDevSupervisorManifest()).toBe(manifest)
66
+ })
67
+
68
+ it('reports a missing generated manifest with regeneration guidance', () => {
69
+ expect(() => loadDevSupervisorManifest(appDir)).toThrow(
70
+ /dev-supervisor\.generated\.json.*Run `yarn generate`/,
71
+ )
72
+ })
73
+
74
+ it('rejects malformed JSON', () => {
75
+ const filePath = path.join(appDir, '.mercato', 'generated', DEV_SUPERVISOR_MANIFEST_FILE)
76
+ fs.mkdirSync(path.dirname(filePath), { recursive: true })
77
+ fs.writeFileSync(filePath, '{', 'utf8')
78
+
79
+ expect(() => loadDevSupervisorManifest(appDir)).toThrow(/JSON could not be parsed/)
80
+ })
81
+
82
+ it('rejects unsupported versions and invalid worker fields', () => {
83
+ writeManifest(appDir, {
84
+ version: 2,
85
+ workers: [],
86
+ schedulerStartStatus: 'missing-module',
87
+ })
88
+ expect(() => loadDevSupervisorManifest(appDir)).toThrow(/unsupported version 2/)
89
+
90
+ writeManifest(appDir, {
91
+ version: 1,
92
+ workers: [{ id: 'worker', queue: '', concurrency: 1 }],
93
+ schedulerStartStatus: 'missing-module',
94
+ })
95
+ expect(() => loadDevSupervisorManifest(appDir)).toThrow(/workers\[0\]\.queue/)
96
+ })
97
+
98
+ it('rejects unknown scheduler command states', () => {
99
+ writeManifest(appDir, {
100
+ version: 1,
101
+ workers: [],
102
+ schedulerStartStatus: 'maybe',
103
+ })
104
+ expect(() => loadDevSupervisorManifest(appDir)).toThrow(/schedulerStartStatus is invalid/)
105
+ })
106
+
107
+ it('preserves the full-bootstrap fallback marker', () => {
108
+ writeManifest(appDir, {
109
+ version: 1,
110
+ workers: [],
111
+ schedulerStartStatus: 'ok',
112
+ requiresFullBootstrap: true,
113
+ })
114
+
115
+ const manifest = loadDevSupervisorManifest(appDir)
116
+ expect(manifest.requiresFullBootstrap).toBe(true)
117
+ expect(canUseLightweightDevSupervisor(manifest)).toBe(false)
118
+ })
119
+
120
+ it('treats a legacy version-1 manifest without the safety marker as full-bootstrap only', () => {
121
+ writeManifest(appDir, {
122
+ version: 1,
123
+ workers: [],
124
+ schedulerStartStatus: 'ok',
125
+ })
126
+
127
+ const manifest = loadDevSupervisorManifest(appDir)
128
+ expect(manifest.requiresFullBootstrap).toBe(true)
129
+ expect(canUseLightweightDevSupervisor(manifest)).toBe(false)
130
+ })
131
+ })