@open-mercato/cli 0.6.7-develop.6677.1.beabb7ca12 → 0.6.7-develop.6686.1.bdda5ed397

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 (211) hide show
  1. package/.turbo/turbo-build.log +0 -1
  2. package/build.mjs +21 -5
  3. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +32 -88
  4. package/dist/agentic/codex/enforcement-rules.md +2 -16
  5. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +19 -50
  6. package/dist/agentic/cursor/rules/entity-guard.mdc +9 -21
  7. package/dist/agentic/cursor/rules/generated-guard.mdc +3 -5
  8. package/dist/agentic/cursor/rules/open-mercato.mdc +3 -29
  9. package/dist/agentic/guides/ai-workflows.md +76 -0
  10. package/dist/agentic/guides/architecture.md +107 -0
  11. package/dist/agentic/guides/backend-ui.md +80 -0
  12. package/dist/agentic/guides/contracts.md +92 -0
  13. package/dist/agentic/guides/extensions.md +104 -0
  14. package/dist/agentic/guides/integrations.md +81 -0
  15. package/dist/agentic/guides/module-facts.json +217 -81
  16. package/dist/agentic/guides/module-system.md +12 -127
  17. package/dist/agentic/guides/modules/ai_assistant.md +1 -1
  18. package/dist/agentic/guides/modules/api_docs.md +1 -1
  19. package/dist/agentic/guides/modules/api_keys.md +1 -1
  20. package/dist/agentic/guides/modules/attachments.md +2 -2
  21. package/dist/agentic/guides/modules/audit_logs.md +2 -2
  22. package/dist/agentic/guides/modules/auth.md +1 -1
  23. package/dist/agentic/guides/modules/business_rules.md +1 -1
  24. package/dist/agentic/guides/modules/catalog.md +5 -5
  25. package/dist/agentic/guides/modules/channel_gmail.md +1 -1
  26. package/dist/agentic/guides/modules/channel_imap.md +1 -1
  27. package/dist/agentic/guides/modules/checkout.md +1 -1
  28. package/dist/agentic/guides/modules/communication_channels.md +1 -1
  29. package/dist/agentic/guides/modules/configs.md +1 -1
  30. package/dist/agentic/guides/modules/content.md +1 -1
  31. package/dist/agentic/guides/modules/currencies.md +1 -1
  32. package/dist/agentic/guides/modules/customer_accounts.md +1 -1
  33. package/dist/agentic/guides/modules/customers.md +2 -2
  34. package/dist/agentic/guides/modules/dashboards.md +1 -1
  35. package/dist/agentic/guides/modules/data_sync.md +1 -1
  36. package/dist/agentic/guides/modules/dictionaries.md +1 -1
  37. package/dist/agentic/guides/modules/directory.md +1 -1
  38. package/dist/agentic/guides/modules/entities.md +2 -2
  39. package/dist/agentic/guides/modules/events.md +1 -1
  40. package/dist/agentic/guides/modules/feature_toggles.md +1 -1
  41. package/dist/agentic/guides/modules/gateway_stripe.md +1 -1
  42. package/dist/agentic/guides/modules/generators.md +1 -1
  43. package/dist/agentic/guides/modules/inbox_ops.md +1 -1
  44. package/dist/agentic/guides/modules/integrations.md +1 -1
  45. package/dist/agentic/guides/modules/messages.md +1 -1
  46. package/dist/agentic/guides/modules/notifications.md +1 -1
  47. package/dist/agentic/guides/modules/onboarding.md +1 -1
  48. package/dist/agentic/guides/modules/payment_gateways.md +1 -1
  49. package/dist/agentic/guides/modules/perspectives.md +1 -1
  50. package/dist/agentic/guides/modules/planner.md +1 -1
  51. package/dist/agentic/guides/modules/portal.md +1 -1
  52. package/dist/agentic/guides/modules/progress.md +1 -1
  53. package/dist/agentic/guides/modules/query_index.md +2 -2
  54. package/dist/agentic/guides/modules/record_locks.md +1 -1
  55. package/dist/agentic/guides/modules/resources.md +1 -1
  56. package/dist/agentic/guides/modules/sales.md +17 -17
  57. package/dist/agentic/guides/modules/scheduler.md +1 -1
  58. package/dist/agentic/guides/modules/search.md +1 -1
  59. package/dist/agentic/guides/modules/security.md +1 -1
  60. package/dist/agentic/guides/modules/shipping_carriers.md +1 -1
  61. package/dist/agentic/guides/modules/sso.md +1 -1
  62. package/dist/agentic/guides/modules/staff.md +2 -2
  63. package/dist/agentic/guides/modules/storage_s3.md +1 -1
  64. package/dist/agentic/guides/modules/sync_akeneo.md +1 -1
  65. package/dist/agentic/guides/modules/sync_excel.md +1 -1
  66. package/dist/agentic/guides/modules/system_status_overlays.md +1 -1
  67. package/dist/agentic/guides/modules/translations.md +1 -1
  68. package/dist/agentic/guides/modules/webhooks.md +2 -2
  69. package/dist/agentic/guides/modules/wms.md +2 -2
  70. package/dist/agentic/guides/modules/workflows.md +1 -1
  71. package/dist/agentic/guides/testing-debugging.md +74 -0
  72. package/dist/agentic/guides/upstream/AGENTS.md +297 -0
  73. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +303 -0
  74. package/dist/agentic/guides/upstream/manifest.json +8 -0
  75. package/dist/agentic/shared/AGENTS.md.template +123 -311
  76. package/dist/agentic/shared/ai/agentic.config.json +1 -0
  77. package/dist/agentic/shared/ai/harness/README.md +48 -0
  78. package/dist/agentic/shared/ai/harness/RELEASE.md +57 -0
  79. package/dist/agentic/shared/ai/harness/cases.json +13487 -0
  80. package/dist/agentic/shared/ai/harness/cases.schema.json +132 -0
  81. package/dist/agentic/shared/ai/harness/fixtures/index.json +49 -0
  82. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +162 -0
  83. package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +20 -0
  84. package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +43 -0
  85. package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +110 -0
  86. package/dist/agentic/shared/ai/harness/release-matrix.json +133 -0
  87. package/dist/agentic/shared/ai/harness/release-result.schema.json +219 -0
  88. package/dist/agentic/shared/ai/harness/result.schema.json +76 -0
  89. package/dist/agentic/shared/ai/harness/routing-response.schema.json +12 -0
  90. package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +73 -0
  91. package/dist/agentic/shared/ai/harness/validators.json +78 -0
  92. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +1768 -0
  93. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +1096 -0
  94. package/dist/agentic/shared/ai/review-checklist.md +36 -0
  95. package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +4 -0
  96. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +20 -241
  97. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +16 -0
  98. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +53 -0
  99. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +13 -0
  100. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +11 -0
  101. package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +30 -0
  102. package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +12 -0
  103. package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +13 -0
  104. package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +14 -0
  105. package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +28 -0
  106. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +12 -0
  107. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +22 -0
  108. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +12 -0
  109. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +19 -0
  110. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +16 -601
  111. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +19 -0
  112. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +13 -0
  113. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +14 -0
  114. package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +10 -0
  115. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +14 -310
  116. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +17 -0
  117. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +28 -0
  118. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +89 -0
  119. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +16 -0
  120. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +17 -0
  121. package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +25 -0
  122. package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +11 -0
  123. package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +13 -0
  124. package/dist/agentic/shared/ai/skills/om-help/SKILL.md +15 -97
  125. package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +17 -0
  126. package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +20 -0
  127. package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +15 -156
  128. package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +12 -0
  129. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +24 -708
  130. package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +16 -0
  131. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +14 -0
  132. package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +15 -0
  133. package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +12 -0
  134. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +20 -820
  135. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +19 -0
  136. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +107 -0
  137. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +12 -0
  138. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +83 -0
  139. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +21 -0
  140. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +11 -0
  141. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +22 -0
  142. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +13 -0
  143. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +19 -843
  144. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +18 -0
  145. package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +25 -0
  146. package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +13 -0
  147. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +38 -0
  148. package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +11 -0
  149. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +15 -63
  150. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +15 -0
  151. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +16 -467
  152. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +16 -0
  153. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +14 -0
  154. package/dist/agentic/shared/ai/skills/tiers.json +137 -20
  155. package/dist/agentic/shared/ai/skills/tiers.schema.json +52 -40
  156. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +174 -0
  157. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +2783 -0
  158. package/dist/agentic/shared/scripts/execution-sandbox.mjs +329 -0
  159. package/dist/agentic/shared/scripts/framework-context.mjs +894 -0
  160. package/dist/agentic/shared/scripts/install-skills.mjs +1029 -0
  161. package/dist/agentic/shared/scripts/install-skills.sh +4 -694
  162. package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +222 -0
  163. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +1761 -0
  164. package/dist/lib/__integration__/TC-INT-008.spec.js +87 -8
  165. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  166. package/dist/lib/agentic-init.js +13 -3
  167. package/dist/lib/agentic-init.js.map +2 -2
  168. package/dist/lib/agentic-setup.js +418 -186
  169. package/dist/lib/agentic-setup.js.map +2 -2
  170. package/dist/lib/generators/module-facts-discovery.js +41 -7
  171. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  172. package/dist/lib/generators/module-facts.js +65 -17
  173. package/dist/lib/generators/module-facts.js.map +2 -2
  174. package/dist/lib/generators/module-registry.js +5 -4
  175. package/dist/lib/generators/module-registry.js.map +2 -2
  176. package/dist/lib/resolver.js +2 -0
  177. package/dist/lib/resolver.js.map +2 -2
  178. package/package.json +5 -5
  179. package/src/lib/__integration__/TC-INT-008.spec.ts +103 -8
  180. package/src/lib/__tests__/agentic-init.test.ts +41 -5
  181. package/src/lib/__tests__/agentic-setup.ownership.test.ts +380 -0
  182. package/src/lib/agentic-init.ts +15 -3
  183. package/src/lib/agentic-setup.ts +526 -224
  184. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +6 -0
  185. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +1 -0
  186. package/src/lib/generators/__tests__/module-facts.discovery.test.ts +54 -10
  187. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +108 -0
  188. package/src/lib/generators/__tests__/module-subset.test.ts +18 -0
  189. package/src/lib/generators/module-facts-discovery.ts +58 -7
  190. package/src/lib/generators/module-facts.ts +84 -20
  191. package/src/lib/generators/module-registry.ts +8 -4
  192. package/src/lib/resolver.ts +3 -0
  193. package/dist/agentic/guides/cache.md +0 -45
  194. package/dist/agentic/guides/core.md +0 -453
  195. package/dist/agentic/guides/events.md +0 -117
  196. package/dist/agentic/guides/queue.md +0 -53
  197. package/dist/agentic/guides/search.md +0 -115
  198. package/dist/agentic/guides/shared.md +0 -108
  199. package/dist/agentic/guides/ui.md +0 -347
  200. package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +0 -27
  201. package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +0 -27
  202. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +0 -260
  203. package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +0 -146
  204. package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +0 -84
  205. package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +0 -193
  206. package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +0 -34
  207. package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +0 -762
  208. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +0 -50
  209. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +0 -97
  210. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +0 -271
  211. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +0 -101
@@ -1,10 +1,27 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, symlinkSync, lstatSync, unlinkSync, readdirSync } from "node:fs";
1
+ import {
2
+ chmodSync,
3
+ copyFileSync,
4
+ existsSync,
5
+ lstatSync,
6
+ mkdirSync,
7
+ mkdtempSync,
8
+ readFileSync,
9
+ readdirSync,
10
+ realpathSync,
11
+ renameSync,
12
+ rmSync,
13
+ statSync,
14
+ writeFileSync
15
+ } from "node:fs";
16
+ import { createHash } from "node:crypto";
2
17
  import { spawnSync } from "node:child_process";
3
- import { join, dirname, basename } from "node:path";
18
+ import { tmpdir } from "node:os";
19
+ import { join, dirname, basename, relative, resolve, sep } from "node:path";
4
20
  import { fileURLToPath } from "node:url";
5
21
  import { Project, SyntaxKind } from "ts-morph";
6
- const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const AGENTIC_DIR = join(__dirname, "..", "agentic");
22
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
23
+ const bundledAgenticDir = join(moduleDir, "..", "agentic");
24
+ const AGENTIC_DIR = existsSync(bundledAgenticDir) ? bundledAgenticDir : join(moduleDir, "..", "..", "..", "create-app", "agentic");
8
25
  const GUIDES_DIR = join(AGENTIC_DIR, "guides");
9
26
  function resolvePlaceholders(content, config) {
10
27
  return content.replace(/\{\{PROJECT_NAME\}\}/g, config.projectName);
@@ -25,13 +42,13 @@ function copyFile(srcDir, srcRelative, destPath) {
25
42
  copyFileSync(srcPath, destPath);
26
43
  }
27
44
  function readEnabledModuleIds(modulesPath) {
28
- if (!existsSync(modulesPath)) return [];
45
+ if (!existsSync(modulesPath)) return { parsed: false, ids: [] };
29
46
  try {
30
47
  const project = new Project({ useInMemoryFileSystem: true });
31
48
  const sourceFile = project.createSourceFile("modules.ts", readFileSync(modulesPath, "utf-8"));
32
49
  const declaration = sourceFile.getVariableDeclaration("enabledModules");
33
50
  const arrayLiteral = declaration?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression);
34
- if (!arrayLiteral) return [];
51
+ if (!arrayLiteral) return { parsed: false, ids: [] };
35
52
  const ids = [];
36
53
  for (const element of arrayLiteral.getElements()) {
37
54
  const objectLiteral = element.asKind(SyntaxKind.ObjectLiteralExpression);
@@ -40,44 +57,31 @@ function readEnabledModuleIds(modulesPath) {
40
57
  const idValue = idProperty?.getInitializerIfKind(SyntaxKind.StringLiteral)?.getLiteralValue();
41
58
  if (idValue) ids.push(idValue);
42
59
  }
43
- return ids;
60
+ return { parsed: true, ids };
44
61
  } catch {
45
- return [];
62
+ return { parsed: false, ids: [] };
46
63
  }
47
64
  }
48
65
  function selectModuleFactSheets(targetDir, modulesSubdir) {
49
66
  const available = existsSync(modulesSubdir) ? readdirSync(modulesSubdir).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")) : [];
50
67
  if (available.length === 0) return [];
51
- const enabled = new Set(readEnabledModuleIds(join(targetDir, "src", "modules.ts")));
68
+ const parsed = readEnabledModuleIds(join(targetDir, "src", "modules.ts"));
69
+ if (!parsed.parsed) return available;
70
+ const enabled = new Set(parsed.ids);
52
71
  const selected = available.filter((moduleId) => enabled.has(moduleId));
53
- return selected.length > 0 ? selected : available;
72
+ return selected;
54
73
  }
55
74
  const MODULE_GUIDES_START = "<!-- om:module-guides:start -->";
56
75
  const MODULE_GUIDES_END = "<!-- om:module-guides:end -->";
57
- function readModuleGuideLabels(guidesDir) {
58
- const factsPath = join(guidesDir, "module-facts.json");
59
- if (!existsSync(factsPath)) return {};
60
- try {
61
- const parsed = JSON.parse(readFileSync(factsPath, "utf-8"));
62
- const labels = {};
63
- for (const [moduleId, entry] of Object.entries(parsed)) {
64
- const label = entry && typeof entry.description === "string" && entry.description || entry && typeof entry.title === "string" && entry.title || "";
65
- if (label) labels[moduleId] = label;
66
- }
67
- return labels;
68
- } catch {
69
- return {};
70
- }
71
- }
72
- function renderModuleGuidesBlock(selected, labels) {
76
+ function renderModuleGuidesBlock(selected) {
73
77
  if (selected.length === 0) return "_No module fact-sheets are bundled for this app._";
74
- const rows = selected.map((moduleId) => {
75
- const label = labels[moduleId] ?? `Use the ${moduleId} module`;
76
- return `| ${label} | \`.ai/guides/modules/${moduleId}.md\` |`;
77
- });
78
- return ["| Task | Load |", "|---|---|", ...rows].join("\n");
78
+ return [
79
+ `Enabled module facts: ${selected.map((moduleId) => `\`${moduleId}\``).join(",")}.`,
80
+ "",
81
+ "Load `.ai/guides/modules/<id>.md` only when `<id>` is explicitly named or is the targeted installed module/host; never preload all module facts."
82
+ ].join("\n");
79
83
  }
80
- function injectModuleGuides(agentsMdPath, selected, labels = {}) {
84
+ function injectModuleGuides(agentsMdPath, selected) {
81
85
  if (!existsSync(agentsMdPath)) return;
82
86
  const content = readFileSync(agentsMdPath, "utf-8");
83
87
  const startIndex = content.indexOf(MODULE_GUIDES_START);
@@ -91,154 +95,333 @@ function injectModuleGuides(agentsMdPath, selected, labels = {}) {
91
95
  const before = content.slice(0, startIndex + MODULE_GUIDES_START.length);
92
96
  const after = content.slice(endIndex);
93
97
  const next = `${before}
94
- ${renderModuleGuidesBlock(selected, labels)}
98
+ ${renderModuleGuidesBlock(selected)}
95
99
  ${after}`;
96
100
  if (next !== content) writeFileSync(agentsMdPath, next);
97
101
  }
98
- function generateShared(config) {
102
+ const TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".cjs", ".json", ".md", ".mdc", ".mjs", ".sh", ".ts", ".txt"]);
103
+ function isTextAsset(path) {
104
+ const dot = path.lastIndexOf(".");
105
+ return dot === -1 || TEXT_EXTENSIONS.has(path.slice(dot));
106
+ }
107
+ function listFiles(root) {
108
+ if (!existsSync(root)) return [];
109
+ const files = [];
110
+ for (const entry of readdirSync(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
111
+ const absolute = join(root, entry.name);
112
+ if (entry.isDirectory()) files.push(...listFiles(absolute));
113
+ else if (entry.isFile()) files.push(absolute);
114
+ }
115
+ return files;
116
+ }
117
+ function copyTree(sourceRoot, destinationRoot, config) {
118
+ for (const sourcePath of listFiles(sourceRoot)) {
119
+ const destinationPath = join(destinationRoot, relative(sourceRoot, sourcePath));
120
+ ensureDir(destinationPath);
121
+ if (isTextAsset(sourcePath)) {
122
+ writeFileSync(destinationPath, resolvePlaceholders(readFileSync(sourcePath, "utf8"), config));
123
+ } else {
124
+ copyFileSync(sourcePath, destinationPath);
125
+ }
126
+ if (process.platform !== "win32") chmodSync(destinationPath, statSync(sourcePath).mode & 511);
127
+ }
128
+ }
129
+ function hashFile(path) {
130
+ return createHash("sha256").update(readFileSync(path)).digest("hex");
131
+ }
132
+ function canonicalHarnessRoot(root) {
133
+ const resolved = resolve(root);
134
+ if (!existsSync(resolved)) {
135
+ throw new Error(`Harness root must be an existing directory: ${resolved}`);
136
+ }
137
+ const canonical = realpathSync(resolved);
138
+ const entry = lstatSync(canonical, { throwIfNoEntry: false });
139
+ if (!entry || !entry.isDirectory()) {
140
+ throw new Error(`Harness root must be an existing directory: ${resolved}`);
141
+ }
142
+ return canonical;
143
+ }
144
+ function assertManagedPath(root, path, options = {}) {
145
+ const absolutePath = resolve(path);
146
+ const normalizedRelative = relative(root, absolutePath);
147
+ if (normalizedRelative === ".." || normalizedRelative.startsWith(`..${sep}`)) {
148
+ throw new Error(`Harness-managed path escapes its root: ${absolutePath}`);
149
+ }
150
+ if (normalizedRelative === "") return;
151
+ const components = normalizedRelative.split(sep).filter(Boolean);
152
+ let current = root;
153
+ for (let index = 0; index < components.length; index += 1) {
154
+ current = join(current, components[index]);
155
+ const entry = lstatSync(current, { throwIfNoEntry: false });
156
+ if (!entry) return;
157
+ if (entry.isSymbolicLink()) {
158
+ throw new Error(`Harness-managed path contains a symbolic-link component: ${current}`);
159
+ }
160
+ const isLeaf = index === components.length - 1;
161
+ if (!isLeaf && !entry.isDirectory()) {
162
+ throw new Error(`Harness-managed path component is not a directory: ${current}`);
163
+ }
164
+ if (isLeaf && options.leaf === "file" && !entry.isFile()) {
165
+ throw new Error(`Harness-managed file is not a regular file: ${current}`);
166
+ }
167
+ }
168
+ }
169
+ function ensureManagedParent(root, filePath) {
170
+ const parent = dirname(filePath);
171
+ const normalizedRelative = relative(root, parent);
172
+ if (normalizedRelative === ".." || normalizedRelative.startsWith(`..${sep}`)) {
173
+ throw new Error(`Harness-managed path escapes its root: ${filePath}`);
174
+ }
175
+ let current = root;
176
+ for (const component of normalizedRelative.split(sep).filter(Boolean)) {
177
+ assertManagedPath(root, current);
178
+ current = join(current, component);
179
+ const entry = lstatSync(current, { throwIfNoEntry: false });
180
+ if (!entry) mkdirSync(current);
181
+ const created = lstatSync(current, { throwIfNoEntry: false });
182
+ if (!created || created.isSymbolicLink() || !created.isDirectory()) {
183
+ throw new Error(`Harness-managed path component is not a real directory: ${current}`);
184
+ }
185
+ }
186
+ }
187
+ function harnessGeneratorId() {
188
+ try {
189
+ const upstream = JSON.parse(readFileSync(join(GUIDES_DIR, "upstream", "manifest.json"), "utf8"));
190
+ const generator = typeof upstream.generator === "string" ? upstream.generator : "";
191
+ const version = generator.slice(generator.lastIndexOf("@") + 1);
192
+ if (version && version !== generator) return `open-mercato-agentic@${version}`;
193
+ } catch {
194
+ }
195
+ return "open-mercato-agentic@unknown";
196
+ }
197
+ function externalSkillNames(targetDir) {
198
+ try {
199
+ const tiers = JSON.parse(readFileSync(join(targetDir, ".ai", "skills", "tiers.json"), "utf8"));
200
+ return new Set(Array.isArray(tiers.external?.skills) ? tiers.external.skills.filter((name) => typeof name === "string") : []);
201
+ } catch {
202
+ return /* @__PURE__ */ new Set();
203
+ }
204
+ }
205
+ function atomicCopyFile(sourcePath, destinationPath, managedRoot) {
206
+ if (managedRoot) {
207
+ assertManagedPath(managedRoot, destinationPath, { leaf: "file" });
208
+ ensureManagedParent(managedRoot, destinationPath);
209
+ } else {
210
+ ensureDir(destinationPath);
211
+ }
212
+ const temporaryPath = `${destinationPath}.tmp-${process.pid}-${Date.now()}`;
213
+ try {
214
+ if (managedRoot) assertManagedPath(managedRoot, temporaryPath, { leaf: "file" });
215
+ copyFileSync(sourcePath, temporaryPath);
216
+ if (process.platform !== "win32") chmodSync(temporaryPath, statSync(sourcePath).mode & 511);
217
+ if (managedRoot) {
218
+ assertManagedPath(managedRoot, dirname(destinationPath));
219
+ assertManagedPath(managedRoot, destinationPath, { leaf: "file" });
220
+ assertManagedPath(managedRoot, temporaryPath, { leaf: "file" });
221
+ }
222
+ renameSync(temporaryPath, destinationPath);
223
+ } finally {
224
+ if (!managedRoot || !lstatSync(temporaryPath, { throwIfNoEntry: false })?.isSymbolicLink()) {
225
+ rmSync(temporaryPath, { force: true });
226
+ }
227
+ }
228
+ }
229
+ function atomicWriteFile(destinationPath, content, managedRoot) {
230
+ if (managedRoot) {
231
+ assertManagedPath(managedRoot, destinationPath, { leaf: "file" });
232
+ ensureManagedParent(managedRoot, destinationPath);
233
+ } else {
234
+ ensureDir(destinationPath);
235
+ }
236
+ const temporaryPath = `${destinationPath}.tmp-${process.pid}-${Date.now()}`;
237
+ try {
238
+ if (managedRoot) assertManagedPath(managedRoot, temporaryPath, { leaf: "file" });
239
+ writeFileSync(temporaryPath, content);
240
+ if (managedRoot) {
241
+ assertManagedPath(managedRoot, dirname(destinationPath));
242
+ assertManagedPath(managedRoot, destinationPath, { leaf: "file" });
243
+ assertManagedPath(managedRoot, temporaryPath, { leaf: "file" });
244
+ }
245
+ renameSync(temporaryPath, destinationPath);
246
+ } finally {
247
+ if (!managedRoot || !lstatSync(temporaryPath, { throwIfNoEntry: false })?.isSymbolicLink()) {
248
+ rmSync(temporaryPath, { force: true });
249
+ }
250
+ }
251
+ }
252
+ function resolveManifestPath(root, relativePath) {
253
+ if (!relativePath || relativePath.includes("\0")) return null;
254
+ const absolutePath = resolve(root, relativePath);
255
+ const normalizedRelative = relative(root, absolutePath);
256
+ if (normalizedRelative === "" || normalizedRelative === ".." || normalizedRelative.startsWith(`..${sep}`)) {
257
+ return null;
258
+ }
259
+ return absolutePath;
260
+ }
261
+ function normalizeManifestRelativePath(path) {
262
+ return path.replace(/\\/g, "/");
263
+ }
264
+ function readHarnessManifest(manifestPath) {
265
+ if (!existsSync(manifestPath)) return null;
266
+ try {
267
+ const parsed = JSON.parse(readFileSync(manifestPath, "utf8"));
268
+ if (typeof parsed.version !== "number" || typeof parsed.generator !== "string" || !Array.isArray(parsed.files)) {
269
+ return null;
270
+ }
271
+ const files = [];
272
+ for (const entry of parsed.files) {
273
+ if (!entry || typeof entry.path !== "string" || typeof entry.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(entry.sha256) || typeof entry.source !== "string" || typeof entry.userEditable !== "boolean") {
274
+ return null;
275
+ }
276
+ files.push({ ...entry, path: normalizeManifestRelativePath(entry.path) });
277
+ }
278
+ return { version: parsed.version, generator: parsed.generator, files };
279
+ } catch {
280
+ return null;
281
+ }
282
+ }
283
+ function targetPathsForTree(sourceRoot, destinationRoot) {
284
+ return listFiles(sourceRoot).map((sourcePath) => join(destinationRoot, relative(sourceRoot, sourcePath)));
285
+ }
286
+ function finalizeHarnessManifest(config, selectedTools) {
99
287
  const { targetDir } = config;
100
288
  const srcDir = join(AGENTIC_DIR, "shared");
101
289
  const selectedModules = selectModuleFactSheets(targetDir, join(GUIDES_DIR, "modules"));
102
- const moduleGuideLabels = readModuleGuideLabels(GUIDES_DIR);
103
- writeTemplate(srcDir, "AGENTS.md.template", join(targetDir, "AGENTS.md"), config);
104
- injectModuleGuides(join(targetDir, "AGENTS.md"), selectedModules, moduleGuideLabels);
105
- writeTemplate(srcDir, "ai/specs/README.md", join(targetDir, ".ai", "specs", "README.md"), config);
106
- copyFile(srcDir, "ai/specs/SPEC-000-template.md", join(targetDir, ".ai", "specs", "SPEC-000-template.md"));
107
- copyFile(srcDir, "ai/lessons.md", join(targetDir, ".ai", "lessons.md"));
108
- copyFile(srcDir, "ai/skills/tiers.json", join(targetDir, ".ai", "skills", "tiers.json"));
109
- copyFile(srcDir, "ai/skills/tiers.schema.json", join(targetDir, ".ai", "skills", "tiers.schema.json"));
110
- copyFile(srcDir, "ai/agentic.config.json", join(targetDir, ".ai", "agentic.config.json"));
111
- copyFile(srcDir, "ai/trackers/github.md", join(targetDir, ".ai", "trackers", "github.md"));
112
- copyFile(srcDir, "scripts/install-skills.sh", join(targetDir, "scripts", "install-skills.sh"));
113
- copyFile(
114
- srcDir,
115
- "ai/skills/om-backend-ui-design/SKILL.md",
116
- join(targetDir, ".ai", "skills", "om-backend-ui-design", "SKILL.md")
117
- );
118
- copyFile(
119
- srcDir,
120
- "ai/skills/om-backend-ui-design/references/ui-components.md",
121
- join(targetDir, ".ai", "skills", "om-backend-ui-design", "references", "ui-components.md")
122
- );
123
- copyFile(srcDir, "ai/skills/om-integration-builder/SKILL.md", join(targetDir, ".ai", "skills", "om-integration-builder", "SKILL.md"));
124
- if (existsSync(join(srcDir, "ai", "skills", "om-integration-builder", "STANDALONE.md"))) {
125
- copyFile(
126
- srcDir,
127
- "ai/skills/om-integration-builder/STANDALONE.md",
128
- join(targetDir, ".ai", "skills", "om-integration-builder", "STANDALONE.md")
129
- );
290
+ const paths = /* @__PURE__ */ new Set([
291
+ join(targetDir, "AGENTS.md"),
292
+ ...targetPathsForTree(join(srcDir, "ai"), join(targetDir, ".ai")),
293
+ ...targetPathsForTree(join(srcDir, "scripts"), join(targetDir, "scripts"))
294
+ ]);
295
+ for (const file of readdirSync(GUIDES_DIR)) {
296
+ if (file.endsWith(".md") || file === "module-facts.json") paths.add(join(targetDir, ".ai", "guides", file));
130
297
  }
131
- copyFile(
132
- srcDir,
133
- "ai/skills/om-integration-builder/references/adapter-contracts.md",
134
- join(targetDir, ".ai", "skills", "om-integration-builder", "references", "adapter-contracts.md")
135
- );
136
- if (existsSync(join(srcDir, "ai", "skills", "om-integration-builder", "STANDALONE.md"))) {
137
- copyFile(
138
- srcDir,
139
- "ai/skills/om-integration-builder/STANDALONE.md",
140
- join(targetDir, ".ai", "skills", "om-integration-builder", "STANDALONE.md")
141
- );
298
+ for (const file of listFiles(join(GUIDES_DIR, "upstream"))) {
299
+ paths.add(join(targetDir, ".ai", "guides", "upstream", relative(join(GUIDES_DIR, "upstream"), file)));
142
300
  }
143
- copyFile(
144
- srcDir,
145
- "ai/skills/om-system-extension/SKILL.md",
146
- join(targetDir, ".ai", "skills", "om-system-extension", "SKILL.md")
147
- );
148
- copyFile(
149
- srcDir,
150
- "ai/skills/om-system-extension/references/extension-contracts.md",
151
- join(targetDir, ".ai", "skills", "om-system-extension", "references", "extension-contracts.md")
152
- );
153
- copyFile(
154
- srcDir,
155
- "ai/skills/om-module-scaffold/SKILL.md",
156
- join(targetDir, ".ai", "skills", "om-module-scaffold", "SKILL.md")
157
- );
158
- copyFile(
159
- srcDir,
160
- "ai/skills/om-module-scaffold/references/naming-conventions.md",
161
- join(targetDir, ".ai", "skills", "om-module-scaffold", "references", "naming-conventions.md")
162
- );
163
- copyFile(
164
- srcDir,
165
- "ai/skills/om-module-scaffold/references/navigation-patterns.md",
166
- join(targetDir, ".ai", "skills", "om-module-scaffold", "references", "navigation-patterns.md")
167
- );
168
- copyFile(
169
- srcDir,
170
- "ai/skills/om-troubleshooter/SKILL.md",
171
- join(targetDir, ".ai", "skills", "om-troubleshooter", "SKILL.md")
172
- );
173
- copyFile(
174
- srcDir,
175
- "ai/skills/om-troubleshooter/references/diagnostic-commands.md",
176
- join(targetDir, ".ai", "skills", "om-troubleshooter", "references", "diagnostic-commands.md")
177
- );
178
- copyFile(
179
- srcDir,
180
- "ai/skills/om-eject-and-customize/SKILL.md",
181
- join(targetDir, ".ai", "skills", "om-eject-and-customize", "SKILL.md")
182
- );
183
- copyFile(
184
- srcDir,
185
- "ai/skills/om-data-model-design/SKILL.md",
186
- join(targetDir, ".ai", "skills", "om-data-model-design", "SKILL.md")
187
- );
188
- copyFile(
189
- srcDir,
190
- "ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md",
191
- join(targetDir, ".ai", "skills", "om-data-model-design", "references", "mikro-orm-cheatsheet.md")
192
- );
193
- copyFile(
194
- srcDir,
195
- "ai/skills/om-implement-spec/SKILL.md",
196
- join(targetDir, ".ai", "skills", "om-implement-spec", "SKILL.md")
197
- );
198
- copyFile(
199
- srcDir,
200
- "ai/skills/om-help/SKILL.md",
201
- join(targetDir, ".ai", "skills", "om-help", "SKILL.md")
202
- );
203
- copyFile(
204
- srcDir,
205
- "ai/skills/om-help/references/skills-catalog.md",
206
- join(targetDir, ".ai", "skills", "om-help", "references", "skills-catalog.md")
207
- );
208
- copyFile(
209
- srcDir,
210
- "ai/skills/om-help/references/workflow-sequences.md",
211
- join(targetDir, ".ai", "skills", "om-help", "references", "workflow-sequences.md")
212
- );
213
- copyFile(
214
- srcDir,
215
- "ai/skills/om-auto-upgrade-0.4.10-to-0.5.0/SKILL.md",
216
- join(targetDir, ".ai", "skills", "om-auto-upgrade-0.4.10-to-0.5.0", "SKILL.md")
301
+ for (const moduleId of selectedModules) paths.add(join(targetDir, ".ai", "guides", "modules", `${moduleId}.md`));
302
+ if (selectedTools.includes("claude-code")) {
303
+ paths.add(join(targetDir, "CLAUDE.md"));
304
+ paths.add(join(targetDir, ".claude", "settings.json"));
305
+ paths.add(join(targetDir, ".claude", "hooks", "entity-migration-check.ts"));
306
+ paths.add(join(targetDir, ".mcp.json.example"));
307
+ }
308
+ if (selectedTools.includes("codex")) paths.add(join(targetDir, ".codex", "mcp.json.example"));
309
+ if (selectedTools.includes("cursor")) {
310
+ for (const file of listFiles(join(AGENTIC_DIR, "cursor"))) {
311
+ const rel = relative(join(AGENTIC_DIR, "cursor"), file);
312
+ paths.add(join(targetDir, ".cursor", rel));
313
+ }
314
+ }
315
+ const manifestPath = join(targetDir, ".ai", "harness", "manifest.json");
316
+ const externalSkills = externalSkillNames(targetDir);
317
+ paths.delete(manifestPath);
318
+ const files = [...paths].filter((path) => existsSync(path)).sort((left, right) => left.localeCompare(right)).map((path) => {
319
+ const relativePath = normalizeManifestRelativePath(relative(targetDir, path));
320
+ const skillName = relativePath.match(/^\.ai\/skills\/([^/]+)\//)?.[1];
321
+ return {
322
+ path: relativePath,
323
+ sha256: hashFile(path),
324
+ source: skillName ? externalSkills.has(skillName) ? "external-override" : "local-skill" : "generated",
325
+ userEditable: relativePath === "AGENTS.md" || relativePath === ".ai/agentic.config.json" || relativePath === ".ai/lessons.md"
326
+ };
327
+ });
328
+ atomicWriteFile(
329
+ manifestPath,
330
+ `${JSON.stringify({ version: 1, generator: harnessGeneratorId(), files }, null, 2)}
331
+ `
217
332
  );
218
- for (const autoSkill of [
219
- "om-auto-create-pr",
220
- "om-auto-continue-pr",
221
- "om-auto-create-pr-loop",
222
- "om-auto-continue-pr-loop",
223
- "om-auto-review-pr",
224
- "om-auto-fix-issue",
225
- "om-prepare-test-env"
226
- ]) {
227
- if (!existsSync(join(srcDir, "ai", "skills", autoSkill, "SKILL.md"))) {
333
+ }
334
+ function preserveIncomingCandidate(targetRoot, destinationPath, sourcePath, relativePath) {
335
+ for (let suffix = 1; ; suffix += 1) {
336
+ const label = suffix === 1 ? ".incoming" : `.incoming.${suffix}`;
337
+ const candidatePath = `${destinationPath}${label}`;
338
+ assertManagedPath(targetRoot, candidatePath, { leaf: "file" });
339
+ if (!existsSync(candidatePath)) {
340
+ atomicCopyFile(sourcePath, candidatePath, targetRoot);
341
+ return `${relativePath}${label}`;
342
+ }
343
+ if (hashFile(candidatePath) === hashFile(sourcePath)) return `${relativePath}${label}`;
344
+ }
345
+ }
346
+ function applyHarnessUpdate(targetDir, stagingDir, options = {}) {
347
+ const targetRoot = canonicalHarnessRoot(targetDir);
348
+ const stagingRoot = canonicalHarnessRoot(stagingDir);
349
+ const targetManifestPath = join(targetRoot, ".ai", "harness", "manifest.json");
350
+ const stagingManifestPath = join(stagingRoot, ".ai", "harness", "manifest.json");
351
+ assertManagedPath(targetRoot, targetManifestPath, { leaf: "file" });
352
+ assertManagedPath(stagingRoot, stagingManifestPath, { leaf: "file" });
353
+ const candidateManifest = readHarnessManifest(stagingManifestPath);
354
+ if (!candidateManifest) {
355
+ throw new Error("Generated harness candidate has a missing or invalid ownership manifest.");
356
+ }
357
+ const previousManifest = readHarnessManifest(targetManifestPath);
358
+ const previousFiles = new Map(previousManifest?.files.map((entry) => [entry.path, entry]) ?? []);
359
+ const candidatePaths = new Set(candidateManifest.files.map((entry) => entry.path));
360
+ const candidates = [];
361
+ for (const entry of candidateManifest.files) {
362
+ const sourcePath = resolveManifestPath(stagingRoot, entry.path);
363
+ const destinationPath = resolveManifestPath(targetRoot, entry.path);
364
+ if (sourcePath) assertManagedPath(stagingRoot, sourcePath, { leaf: "file" });
365
+ if (destinationPath) assertManagedPath(targetRoot, destinationPath, { leaf: "file" });
366
+ if (!sourcePath || !destinationPath || !existsSync(sourcePath) || hashFile(sourcePath) !== entry.sha256) {
367
+ throw new Error(`Generated harness candidate is invalid for ${JSON.stringify(entry.path)}.`);
368
+ }
369
+ candidates.push({ entry, sourcePath, destinationPath });
370
+ }
371
+ const conflicts = [];
372
+ for (const { entry, sourcePath, destinationPath } of candidates) {
373
+ if (options.force) {
374
+ atomicCopyFile(sourcePath, destinationPath, targetRoot);
228
375
  continue;
229
376
  }
230
- copyFile(
231
- srcDir,
232
- `ai/skills/${autoSkill}/SKILL.md`,
233
- join(targetDir, ".ai", "skills", autoSkill, "SKILL.md")
234
- );
377
+ if (!existsSync(destinationPath)) {
378
+ atomicCopyFile(sourcePath, destinationPath, targetRoot);
379
+ continue;
380
+ }
381
+ const currentHash = hashFile(destinationPath);
382
+ const previousEntry = previousFiles.get(entry.path);
383
+ if (currentHash === entry.sha256 || previousEntry && currentHash === previousEntry.sha256) {
384
+ atomicCopyFile(sourcePath, destinationPath, targetRoot);
385
+ continue;
386
+ }
387
+ const candidate = preserveIncomingCandidate(targetRoot, destinationPath, sourcePath, entry.path);
388
+ conflicts.push({ path: entry.path, candidate });
235
389
  }
236
- copyFile(
237
- srcDir,
238
- "ai/skills/om-trim-unused-modules/SKILL.md",
239
- join(targetDir, ".ai", "skills", "om-trim-unused-modules", "SKILL.md")
240
- );
241
- copyFile(srcDir, "ai/qa/tests/playwright.config.ts", join(targetDir, ".ai", "qa", "tests", "playwright.config.ts"));
390
+ const retainedRetiredEntries = [];
391
+ for (const previousEntry of previousManifest?.files ?? []) {
392
+ if (candidatePaths.has(previousEntry.path)) continue;
393
+ const destinationPath = resolveManifestPath(targetRoot, previousEntry.path);
394
+ if (!destinationPath || !existsSync(destinationPath)) continue;
395
+ assertManagedPath(targetRoot, destinationPath, { leaf: "file" });
396
+ let unchanged = false;
397
+ try {
398
+ unchanged = hashFile(destinationPath) === previousEntry.sha256;
399
+ } catch {
400
+ }
401
+ if (unchanged) {
402
+ assertManagedPath(targetRoot, dirname(destinationPath));
403
+ assertManagedPath(targetRoot, destinationPath, { leaf: "file" });
404
+ rmSync(destinationPath, { force: true });
405
+ } else {
406
+ retainedRetiredEntries.push(previousEntry);
407
+ conflicts.push({ path: previousEntry.path, candidate: null });
408
+ }
409
+ }
410
+ const finalManifest = {
411
+ ...candidateManifest,
412
+ files: [...candidateManifest.files, ...retainedRetiredEntries]
413
+ };
414
+ atomicWriteFile(targetManifestPath, `${JSON.stringify(finalManifest, null, 2)}
415
+ `, targetRoot);
416
+ return conflicts;
417
+ }
418
+ function generateShared(config) {
419
+ const { targetDir } = config;
420
+ const srcDir = join(AGENTIC_DIR, "shared");
421
+ const selectedModules = selectModuleFactSheets(targetDir, join(GUIDES_DIR, "modules"));
422
+ writeTemplate(srcDir, "AGENTS.md.template", join(targetDir, "AGENTS.md"), config);
423
+ copyTree(join(srcDir, "ai"), join(targetDir, ".ai"), config);
424
+ copyTree(join(srcDir, "scripts"), join(targetDir, "scripts"), config);
242
425
  if (existsSync(GUIDES_DIR)) {
243
426
  const guidesDestDir = join(targetDir, ".ai", "guides");
244
427
  for (const file of readdirSync(GUIDES_DIR)) {
@@ -254,6 +437,7 @@ function generateShared(config) {
254
437
  ensureDir(destPath);
255
438
  copyFileSync(moduleFactsPath, destPath);
256
439
  }
440
+ copyTree(join(GUIDES_DIR, "upstream"), join(guidesDestDir, "upstream"), config);
257
441
  const modulesSubdir = join(GUIDES_DIR, "modules");
258
442
  for (const moduleId of selectedModules) {
259
443
  const destPath = join(guidesDestDir, "modules", `${moduleId}.md`);
@@ -261,6 +445,7 @@ function generateShared(config) {
261
445
  copyFileSync(join(modulesSubdir, `${moduleId}.md`), destPath);
262
446
  }
263
447
  }
448
+ injectModuleGuides(join(targetDir, "AGENTS.md"), selectedModules);
264
449
  }
265
450
  function generateClaudeCode(config) {
266
451
  const { targetDir } = config;
@@ -269,7 +454,6 @@ function generateClaudeCode(config) {
269
454
  copyFile(srcDir, "settings.json", join(targetDir, ".claude", "settings.json"));
270
455
  copyFile(srcDir, "hooks/entity-migration-check.ts", join(targetDir, ".claude", "hooks", "entity-migration-check.ts"));
271
456
  copyFile(srcDir, "mcp.json.example", join(targetDir, ".mcp.json.example"));
272
- ensureSkillsLink(join(targetDir, ".claude", "skills"), join("..", ".ai", "skills"));
273
457
  }
274
458
  function generateCodex(config) {
275
459
  const { targetDir } = config;
@@ -308,16 +492,6 @@ function generateCursor(config) {
308
492
  copyFile(srcDir, "hooks/entity-migration-check.mjs", join(targetDir, ".cursor", "hooks", "entity-migration-check.mjs"));
309
493
  copyFile(srcDir, "mcp.json.example", join(targetDir, ".cursor", "mcp.json.example"));
310
494
  }
311
- function ensureSkillsLink(linkPath, target) {
312
- ensureDir(linkPath);
313
- if (existsSync(linkPath) && !lstatSync(linkPath).isSymbolicLink()) {
314
- return;
315
- }
316
- if (lstatSync(linkPath, { throwIfNoEntry: false })?.isSymbolicLink()) {
317
- unlinkSync(linkPath);
318
- }
319
- symlinkSync(target, linkPath);
320
- }
321
495
  const TOOLS = [
322
496
  { key: "1", label: "Claude Code (Anthropic)", id: "claude-code" },
323
497
  { key: "2", label: "Codex (OpenAI)", id: "codex" },
@@ -326,6 +500,30 @@ const TOOLS = [
326
500
  { key: "5", label: "Skip \u2014 set up manually later", id: "skip" }
327
501
  ];
328
502
  const SELECTABLE = TOOLS.filter((t) => t.id !== "multiple" && t.id !== "skip");
503
+ function persistedAgentSelection(targetDir) {
504
+ const selectableIds = SELECTABLE.map((tool) => tool.id);
505
+ const tiersPath = join(targetDir, ".ai", "skills", "tiers.json");
506
+ try {
507
+ const tiers = JSON.parse(readFileSync(tiersPath, "utf8"));
508
+ if (Array.isArray(tiers.agents?.ignore) && tiers.agents.ignore.every((id) => typeof id === "string")) {
509
+ const ignored = new Set(tiers.agents.ignore);
510
+ return selectableIds.filter((id) => !ignored.has(id));
511
+ }
512
+ } catch {
513
+ }
514
+ const manifest = readHarnessManifest(join(targetDir, ".ai", "harness", "manifest.json"));
515
+ const owned = new Set(manifest?.files.map((entry) => entry.path) ?? []);
516
+ const selected = SELECTABLE.filter((tool) => {
517
+ if (tool.id === "claude-code") {
518
+ return owned.has("CLAUDE.md") || owned.has(".claude/settings.json") || existsSync(join(targetDir, "CLAUDE.md")) || existsSync(join(targetDir, ".claude", "settings.json"));
519
+ }
520
+ if (tool.id === "codex") {
521
+ return owned.has(".codex/mcp.json.example") || existsSync(join(targetDir, ".codex", "mcp.json.example"));
522
+ }
523
+ return owned.has(".cursor/hooks.json") || existsSync(join(targetDir, ".cursor", "hooks.json"));
524
+ }).map((tool) => tool.id);
525
+ return selected.length > 0 ? selected : null;
526
+ }
329
527
  async function promptSelection(ask) {
330
528
  console.log("");
331
529
  console.log("\u{1F916} Agentic workflow setup");
@@ -362,6 +560,8 @@ async function runAgenticSetup(targetDir, ask, options) {
362
560
  let selectedIds;
363
561
  if (options?.tool) {
364
562
  selectedIds = options.tool.split(",").map((t) => t.trim());
563
+ } else if (options?.updateHarness) {
564
+ selectedIds = persistedAgentSelection(targetDir) ?? await promptSelection(ask);
365
565
  } else {
366
566
  selectedIds = await promptSelection(ask);
367
567
  }
@@ -375,11 +575,33 @@ async function runAgenticSetup(targetDir, ask, options) {
375
575
  projectName: basename(targetDir),
376
576
  targetDir
377
577
  };
378
- generateShared(config);
379
- if (selectedIds.includes("claude-code")) generateClaudeCode(config);
380
- if (selectedIds.includes("codex")) generateCodex(config);
381
- if (selectedIds.includes("cursor")) generateCursor(config);
382
- persistAgentSelection(targetDir, selectedIds);
578
+ const stagingDir = mkdtempSync(join(tmpdir(), "open-mercato-harness-"));
579
+ try {
580
+ const modulesSourcePath = join(targetDir, "src", "modules.ts");
581
+ const modulesCandidatePath = join(stagingDir, "src", "modules.ts");
582
+ if (existsSync(modulesSourcePath)) {
583
+ ensureDir(modulesCandidatePath);
584
+ copyFileSync(modulesSourcePath, modulesCandidatePath);
585
+ }
586
+ const stagingConfig = {
587
+ projectName: config.projectName,
588
+ targetDir: stagingDir
589
+ };
590
+ generateHarness(stagingConfig, selectedIds);
591
+ const conflicts = applyHarnessUpdate(targetDir, stagingDir, {
592
+ force: options?.force
593
+ });
594
+ if (conflicts.length > 0) {
595
+ console.warn("");
596
+ console.warn(" \u26A0 Preserved locally modified harness files:");
597
+ for (const conflict of conflicts) {
598
+ const detail = conflict.candidate ? `candidate: ${conflict.candidate}` : "retired asset kept because it has local changes";
599
+ console.warn(` \u2022 ${conflict.path} (${detail})`);
600
+ }
601
+ }
602
+ } finally {
603
+ rmSync(stagingDir, { recursive: true, force: true });
604
+ }
383
605
  installSkills(targetDir);
384
606
  console.log("");
385
607
  console.log(" Agentic setup complete:");
@@ -399,6 +621,14 @@ async function runAgenticSetup(targetDir, ask, options) {
399
621
  console.log(" refresh the external open-mercato/skills subset.");
400
622
  console.log("");
401
623
  }
624
+ function generateHarness(config, selectedIds) {
625
+ generateShared(config);
626
+ if (selectedIds.includes("claude-code")) generateClaudeCode(config);
627
+ if (selectedIds.includes("codex")) generateCodex(config);
628
+ if (selectedIds.includes("cursor")) generateCursor(config);
629
+ persistAgentSelection(config.targetDir, selectedIds);
630
+ finalizeHarnessManifest(config, selectedIds);
631
+ }
402
632
  function persistAgentSelection(targetDir, selectedIds) {
403
633
  const manifestPath = join(targetDir, ".ai", "skills", "tiers.json");
404
634
  if (!existsSync(manifestPath)) return;
@@ -413,16 +643,18 @@ function persistAgentSelection(targetDir, selectedIds) {
413
643
  `);
414
644
  }
415
645
  function installSkills(targetDir) {
416
- const installScript = join(targetDir, "scripts", "install-skills.sh");
646
+ const installScript = join(targetDir, "scripts", "install-skills.mjs");
417
647
  if (!existsSync(installScript)) return;
418
648
  console.log("");
419
649
  console.log(" Installing agent skills (local tiers + external open-mercato/skills subset)...");
420
- const result = spawnSync("sh", [installScript], { cwd: targetDir, stdio: "inherit" });
650
+ const result = spawnSync(process.execPath, [installScript], { cwd: targetDir, stdio: "inherit" });
421
651
  if (result.error || result.status !== 0) {
422
652
  console.log(" \u26A0 Skill installation did not complete; run `yarn install-skills` inside the app when online.");
423
653
  }
424
654
  }
425
655
  export {
656
+ applyHarnessUpdate,
657
+ renderModuleGuidesBlock,
426
658
  runAgenticSetup
427
659
  };
428
660
  //# sourceMappingURL=agentic-setup.js.map