sinapse-ai 9.4.0 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/.claude/CLAUDE.md +10 -4
  2. package/.claude/hooks/enforce-architecture-first.py +197 -197
  3. package/.claude/hooks/enforce-git-push-authority.sh +25 -4
  4. package/.claude/hooks/mind-clone-governance.py +193 -193
  5. package/.claude/hooks/read-protection.py +152 -152
  6. package/.claude/hooks/sql-governance.py +183 -183
  7. package/.claude/hooks/verify-packages.cjs +83 -0
  8. package/.claude/hooks/write-path-validation.py +195 -195
  9. package/.claude/rules/hook-governance.md +1 -0
  10. package/.claude/rules/mandatory-delegation.md +24 -0
  11. package/.claude/rules/project-intelligence.md +63 -0
  12. package/.claude/rules/response-format.md +4 -0
  13. package/.claude/rules/safe-collaboration.md +4 -2
  14. package/.claude/rules/security-data-protection.md +18 -0
  15. package/.claude/rules/squad-awareness.md +93 -67
  16. package/.claude/rules/token-economy.md +148 -0
  17. package/.codex/agents/analyst.md +90 -0
  18. package/.codex/agents/architect.md +78 -0
  19. package/.codex/agents/data-engineer.md +38 -0
  20. package/.codex/agents/developer.md +97 -0
  21. package/.codex/agents/devops.md +121 -0
  22. package/.codex/agents/product-lead.md +27 -0
  23. package/.codex/agents/project-lead.md +28 -0
  24. package/.codex/agents/quality-gate.md +89 -0
  25. package/.codex/agents/sprint-lead.md +28 -0
  26. package/.codex/agents/squad-creator.md +58 -0
  27. package/.codex/agents/ux-design-expert.md +28 -0
  28. package/.sinapse-ai/core/code-intel/registry-syncer.js +56 -3
  29. package/.sinapse-ai/core/doctor/checks/agent-memory.js +5 -1
  30. package/.sinapse-ai/core/doctor/checks/claude-md.js +4 -1
  31. package/.sinapse-ai/core/doctor/checks/code-intel.js +5 -1
  32. package/.sinapse-ai/core/doctor/checks/commands-count.js +4 -1
  33. package/.sinapse-ai/core/doctor/checks/constitution-consistency.js +4 -1
  34. package/.sinapse-ai/core/doctor/checks/core-config.js +4 -1
  35. package/.sinapse-ai/core/doctor/checks/entity-registry.js +6 -1
  36. package/.sinapse-ai/core/doctor/checks/git-hooks.js +5 -1
  37. package/.sinapse-ai/core/doctor/checks/graph-dashboard.js +4 -1
  38. package/.sinapse-ai/core/doctor/checks/hooks-claude-count.js +5 -1
  39. package/.sinapse-ai/core/doctor/checks/ide-sync.js +4 -1
  40. package/.sinapse-ai/core/doctor/checks/node-version.js +4 -1
  41. package/.sinapse-ai/core/doctor/checks/npm-packages.js +4 -1
  42. package/.sinapse-ai/core/doctor/checks/rules-files.js +4 -1
  43. package/.sinapse-ai/core/doctor/checks/settings-json.js +4 -1
  44. package/.sinapse-ai/core/doctor/checks/skills-count.js +4 -1
  45. package/.sinapse-ai/core/doctor/index.js +157 -50
  46. package/.sinapse-ai/core/ids/registry-updater.js +6 -1
  47. package/.sinapse-ai/core/logger/index.js +319 -0
  48. package/.sinapse-ai/core/orchestration/terminal-spawner.js +2 -2
  49. package/.sinapse-ai/core/telemetry/index.js +247 -0
  50. package/.sinapse-ai/data/entity-registry.yaml +1384 -944
  51. package/.sinapse-ai/development/agents/architect.md +5 -0
  52. package/.sinapse-ai/development/agents/data-engineer.md +38 -0
  53. package/.sinapse-ai/development/agents/developer.md +28 -0
  54. package/.sinapse-ai/development/agents/devops.md +4 -0
  55. package/.sinapse-ai/development/agents/product-lead.md +27 -0
  56. package/.sinapse-ai/development/agents/project-lead.md +28 -0
  57. package/.sinapse-ai/development/agents/quality-gate.md +4 -0
  58. package/.sinapse-ai/development/agents/sprint-lead/MEMORY.md +8 -0
  59. package/.sinapse-ai/development/agents/sprint-lead.md +28 -0
  60. package/.sinapse-ai/development/agents/squad-creator.md +58 -0
  61. package/.sinapse-ai/development/agents/ux-design-expert.md +28 -0
  62. package/.sinapse-ai/development/knowledge-base/agent-communication-protocol.md +127 -0
  63. package/.sinapse-ai/development/knowledge-base/database-scaling-patterns.md +374 -0
  64. package/.sinapse-ai/development/knowledge-base/environment-deployment-patterns.md +353 -0
  65. package/.sinapse-ai/development/knowledge-base/gotchas-patterns.md +224 -0
  66. package/.sinapse-ai/development/knowledge-base/infrastructure-decision-framework.md +221 -0
  67. package/.sinapse-ai/development/knowledge-base/security-pre-deploy-checklist.md +410 -0
  68. package/.sinapse-ai/development/knowledge-base/software-architecture-patterns.md +299 -0
  69. package/.sinapse-ai/development/knowledge-base/token-economy-guide.md +198 -0
  70. package/.sinapse-ai/development/scripts/populate-entity-registry.js +5 -1
  71. package/.sinapse-ai/development/skills/captcha-handler.md +82 -0
  72. package/.sinapse-ai/development/skills/chrome-brain.md +81 -0
  73. package/.sinapse-ai/development/skills/deploy-readiness.md +93 -0
  74. package/.sinapse-ai/development/skills/model-router.md +92 -0
  75. package/.sinapse-ai/development/skills/sinapse-methodology.md +175 -0
  76. package/.sinapse-ai/development/skills/story-fast-track.md +71 -0
  77. package/.sinapse-ai/development/tasks/dev-develop-story.md +10 -0
  78. package/.sinapse-ai/development/tasks/environment-promotion-pipeline.md +582 -0
  79. package/.sinapse-ai/development/tasks/generate-agent-handoff.md +223 -0
  80. package/.sinapse-ai/development/tasks/infrastructure-assessment.md +432 -0
  81. package/.sinapse-ai/development/tasks/load-testing-setup.md +611 -0
  82. package/.sinapse-ai/development/tasks/observability-blueprint.md +562 -0
  83. package/.sinapse-ai/development/templates/legal/breach-notification-tmpl.md +113 -0
  84. package/.sinapse-ai/development/templates/legal/privacy-policy-tmpl.md +93 -0
  85. package/.sinapse-ai/development/templates/legal/terms-of-service-tmpl.md +85 -0
  86. package/.sinapse-ai/development/templates/service-template/README.md.hbs +159 -159
  87. package/.sinapse-ai/development/templates/service-template/__tests__/index.test.ts.hbs +238 -238
  88. package/.sinapse-ai/development/templates/service-template/client.ts.hbs +404 -404
  89. package/.sinapse-ai/development/templates/service-template/errors.ts.hbs +183 -183
  90. package/.sinapse-ai/development/templates/service-template/index.ts.hbs +121 -121
  91. package/.sinapse-ai/development/templates/service-template/package.json.hbs +88 -88
  92. package/.sinapse-ai/development/templates/service-template/types.ts.hbs +146 -146
  93. package/.sinapse-ai/development/templates/squad-template/LICENSE +22 -22
  94. package/.sinapse-ai/development/workflows/story-development-cycle.yaml +40 -1
  95. package/.sinapse-ai/hooks/ids-post-commit.js +22 -0
  96. package/.sinapse-ai/infrastructure/contracts/compatibility/README.md +42 -0
  97. package/.sinapse-ai/infrastructure/contracts/compatibility/sinapse-current.yaml +35 -0
  98. package/.sinapse-ai/infrastructure/scripts/llm-routing/templates/claude-free-tracked.cmd +127 -127
  99. package/.sinapse-ai/infrastructure/scripts/llm-routing/templates/deepseek-proxy.cmd +71 -71
  100. package/.sinapse-ai/infrastructure/scripts/llm-routing/templates/deepseek-usage.cmd +51 -51
  101. package/.sinapse-ai/infrastructure/scripts/pr-review-ai.js +16 -13
  102. package/.sinapse-ai/infrastructure/scripts/setup-project-infra.js +128 -0
  103. package/.sinapse-ai/infrastructure/scripts/test-discovery.js +8 -3
  104. package/.sinapse-ai/infrastructure/scripts/validate-manifest-parity.js +380 -0
  105. package/.sinapse-ai/infrastructure/scripts/validate-parity.js +76 -25
  106. package/.sinapse-ai/infrastructure/templates/coderabbit.yaml.template +280 -280
  107. package/.sinapse-ai/infrastructure/templates/config/env.example +16 -0
  108. package/.sinapse-ai/infrastructure/templates/config/gitignore-additions.tmpl +59 -0
  109. package/.sinapse-ai/infrastructure/templates/github/CODEOWNERS.template +12 -0
  110. package/.sinapse-ai/infrastructure/templates/github/PULL_REQUEST_TEMPLATE.md +29 -0
  111. package/.sinapse-ai/infrastructure/templates/github/ci-template.yml +77 -0
  112. package/.sinapse-ai/infrastructure/templates/github/issue-templates/bug_report.md +34 -0
  113. package/.sinapse-ai/infrastructure/templates/github/issue-templates/feature_request.md +19 -0
  114. package/.sinapse-ai/infrastructure/templates/github-workflows/ci.yml.template +170 -170
  115. package/.sinapse-ai/infrastructure/templates/github-workflows/pr-automation.yml.template +331 -331
  116. package/.sinapse-ai/infrastructure/templates/github-workflows/release.yml.template +197 -197
  117. package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +19 -19
  118. package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-node.tmpl +86 -86
  119. package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-python.tmpl +146 -146
  120. package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-sinapse-base.tmpl +64 -64
  121. package/.sinapse-ai/infrastructure/templates/sinapse-sync.yaml.template +183 -183
  122. package/.sinapse-ai/install-manifest.yaml +275 -140
  123. package/.sinapse-ai/local-config.yaml.template +65 -65
  124. package/.sinapse-ai/monitor/hooks/lib/__init__.py +2 -2
  125. package/.sinapse-ai/monitor/hooks/lib/enrich.py +59 -59
  126. package/.sinapse-ai/monitor/hooks/lib/send_event.py +48 -48
  127. package/.sinapse-ai/monitor/hooks/notification.py +30 -30
  128. package/.sinapse-ai/monitor/hooks/post_tool_use.py +46 -46
  129. package/.sinapse-ai/monitor/hooks/pre_compact.py +30 -30
  130. package/.sinapse-ai/monitor/hooks/pre_tool_use.py +41 -41
  131. package/.sinapse-ai/monitor/hooks/stop.py +30 -30
  132. package/.sinapse-ai/monitor/hooks/subagent_stop.py +30 -30
  133. package/.sinapse-ai/monitor/hooks/user_prompt_submit.py +39 -39
  134. package/.sinapse-ai/product/templates/adr.hbs +126 -126
  135. package/.sinapse-ai/product/templates/dbdr.hbs +242 -242
  136. package/.sinapse-ai/product/templates/epic.hbs +213 -213
  137. package/.sinapse-ai/product/templates/pmdr.hbs +187 -187
  138. package/.sinapse-ai/product/templates/prd-v2.0.hbs +217 -217
  139. package/.sinapse-ai/product/templates/prd.hbs +202 -202
  140. package/.sinapse-ai/product/templates/story-tmpl.yaml +59 -0
  141. package/.sinapse-ai/product/templates/story.hbs +264 -264
  142. package/.sinapse-ai/product/templates/task.hbs +171 -171
  143. package/.sinapse-ai/product/templates/tmpl-comment-on-examples.sql +159 -159
  144. package/.sinapse-ai/product/templates/tmpl-migration-script.sql +92 -92
  145. package/.sinapse-ai/product/templates/tmpl-rls-granular-policies.sql +105 -105
  146. package/.sinapse-ai/product/templates/tmpl-rls-kiss-policy.sql +11 -11
  147. package/.sinapse-ai/product/templates/tmpl-rls-roles.sql +136 -136
  148. package/.sinapse-ai/product/templates/tmpl-rls-simple.sql +78 -78
  149. package/.sinapse-ai/product/templates/tmpl-rls-tenant.sql +153 -153
  150. package/.sinapse-ai/product/templates/tmpl-rollback-script.sql +78 -78
  151. package/.sinapse-ai/product/templates/tmpl-seed-data.sql +141 -141
  152. package/.sinapse-ai/product/templates/tmpl-smoke-test.sql +17 -17
  153. package/.sinapse-ai/product/templates/tmpl-staging-copy-merge.sql +140 -140
  154. package/.sinapse-ai/product/templates/tmpl-stored-proc.sql +141 -141
  155. package/.sinapse-ai/product/templates/tmpl-trigger.sql +153 -153
  156. package/.sinapse-ai/product/templates/tmpl-view-materialized.sql +134 -134
  157. package/.sinapse-ai/product/templates/tmpl-view.sql +178 -178
  158. package/.sinapse-ai/scripts/diagnostics/health-dashboard/package-lock.json +427 -355
  159. package/LICENSE +34 -34
  160. package/README.en.md +167 -20
  161. package/README.md +190 -22
  162. package/bin/cli.js +510 -196
  163. package/bin/postinstall.js +564 -0
  164. package/bin/sinapse-cli +283 -283
  165. package/bin/sinapse-graph.js +9 -0
  166. package/bin/sinapse-init.js +36 -4
  167. package/bin/sinapse-minimal.js +20 -9
  168. package/bin/sinapse.js +202 -122
  169. package/bin/utils/deprecation-warning.js +46 -0
  170. package/bin/utils/pre-push-safety.js +14 -0
  171. package/docs/TELEMETRY.md +131 -0
  172. package/docs/chrome-brain-upgrade-plan.md +624 -0
  173. package/docs/framework/orqx-plan.md +1 -1
  174. package/docs/installation/chrome-brain.md +17 -7
  175. package/docs/mega-upgrade-orchestration-plan.md +71 -0
  176. package/docs/pt/contributing.md +20 -0
  177. package/docs/research-synthesis-for-upgrade.md +511 -0
  178. package/docs/security-audit-report.md +306 -0
  179. package/package.json +20 -8
  180. package/packages/installer/src/config/configure-environment.js +19 -44
  181. package/packages/installer/src/detection/detect-project-type.js +181 -63
  182. package/packages/installer/src/installer/manifest-signature.js +32 -17
  183. package/packages/installer/src/wizard/i18n.js +12 -0
  184. package/packages/installer/src/wizard/ide-config-generator.js +8 -39
  185. package/packages/installer/src/wizard/index.js +119 -14
  186. package/packages/installer/src/wizard/questions.js +2 -3
  187. package/packages/installer/tests/integration/environment-configuration.test.js +7 -5
  188. package/packages/installer/tests/unit/detection/detect-project-type.test.js +138 -1
  189. package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +3 -3
  190. package/packages/sinapse-install/bin/edmcp.js +0 -0
  191. package/packages/sinapse-install/bin/sinapse-install.js +0 -0
  192. package/packages/sinapse-pro-cli/bin/sinapse-pro.js +0 -0
  193. package/scripts/check-markdown-links.py +353 -353
  194. package/scripts/coverage-report-summary.js +169 -0
  195. package/scripts/generate-install-manifest.js +6 -2
  196. package/scripts/release-readiness.js +169 -0
  197. package/scripts/test-install-matrix-local.sh +153 -0
  198. package/scripts/validate-install-docs.js +394 -0
  199. package/scripts/validate-no-external-refs.js +376 -0
  200. package/scripts/validate-squad-orqx.js +302 -0
  201. package/scripts/validate-story-meta.js +263 -0
  202. package/squads/claude-code-mastery/CHANGELOG.md +1 -1
  203. package/squads/claude-code-mastery/README.md +2 -2
  204. package/squads/claude-code-mastery/squad.yaml +1 -1
  205. package/squads/squad-artdir/README.md +90 -0
  206. package/squads/squad-artdir/agents/accessibility-guardian.md +184 -0
  207. package/squads/squad-artdir/agents/artdir-orqx.md +145 -0
  208. package/squads/squad-artdir/agents/color-psychologist.md +166 -0
  209. package/squads/squad-artdir/agents/cro-persuasion.md +161 -0
  210. package/squads/squad-artdir/agents/design-system-architect.md +100 -0
  211. package/squads/squad-artdir/agents/ia-architect.md +169 -0
  212. package/squads/squad-artdir/agents/interaction-designer.md +162 -0
  213. package/squads/squad-artdir/agents/layout-engineer.md +163 -0
  214. package/squads/squad-artdir/agents/motion-architect.md +185 -0
  215. package/squads/squad-artdir/agents/platform-aesthetic-director.md +84 -0
  216. package/squads/squad-artdir/agents/premium-packaging-strategist.md +107 -0
  217. package/squads/squad-artdir/agents/product-surface-director.md +86 -0
  218. package/squads/squad-artdir/agents/type-systemist.md +138 -0
  219. package/squads/squad-artdir/agents/visual-strategist.md +127 -0
  220. package/squads/squad-artdir/checklists/seven-pillars-validation-checklist.md +172 -0
  221. package/squads/squad-artdir/knowledge-base/case-nyo-ia-reference.md +289 -0
  222. package/squads/squad-artdir/knowledge-base/deliverables-templates.md +457 -0
  223. package/squads/squad-artdir/knowledge-base/motion-technique-catalog.md +247 -0
  224. package/squads/squad-artdir/knowledge-base/premium-packaging-principles.md +133 -0
  225. package/squads/squad-artdir/knowledge-base/psychological-toolkit.md +229 -0
  226. package/squads/squad-artdir/knowledge-base/saas-art-direction-canon.md +242 -0
  227. package/squads/squad-artdir/knowledge-base/seven-pillars-framework.md +289 -0
  228. package/squads/squad-artdir/knowledge-base/ten-pillars-framework.md +221 -0
  229. package/squads/squad-artdir/package.json +20 -0
  230. package/squads/squad-artdir/squad.yaml +271 -0
  231. package/squads/squad-artdir/tasks/audit-conversion.md +97 -0
  232. package/squads/squad-artdir/tasks/audit-drift-multi-surface.md +55 -0
  233. package/squads/squad-artdir/tasks/consult-saas-canon.md +54 -0
  234. package/squads/squad-artdir/tasks/create-art-direction-brief.md +110 -0
  235. package/squads/squad-artdir/tasks/create-premium-packaging-brief.md +61 -0
  236. package/squads/squad-artdir/tasks/create-wireflow.md +84 -0
  237. package/squads/squad-artdir/tasks/design-color-system.md +81 -0
  238. package/squads/squad-artdir/tasks/design-product-surface.md +60 -0
  239. package/squads/squad-artdir/tasks/design-token-system.md +58 -0
  240. package/squads/squad-artdir/tasks/diagnose-visual-language.md +92 -0
  241. package/squads/squad-artdir/tasks/first-5-minutes-choreography.md +65 -0
  242. package/squads/squad-artdir/tasks/specify-motion-system.md +84 -0
  243. package/squads/squad-artdir/tasks/validate-against-pillars.md +143 -0
  244. package/squads/squad-artdir/templates/art-direction-brief-template.md +215 -0
  245. package/squads/squad-artdir/workflows/conversion-audit-cycle.yaml +78 -0
  246. package/squads/squad-artdir/workflows/full-art-direction-cycle.yaml +98 -0
  247. package/squads/squad-artdir/workflows/saas-platform-art-direction-cycle.yaml +174 -0
  248. package/squads/squad-brand/knowledge-base/ai-visual-generation-canon.md +234 -0
  249. package/squads/squad-brand/squad.yaml +20 -6
  250. package/squads/squad-claude/knowledge-base/context-window-optimization.md +1 -1
  251. package/squads/squad-claude/knowledge-base/swarm-orchestration-patterns.md +2 -2
  252. package/squads/squad-content/knowledge-base/ai-native-content-loop.md +220 -0
  253. package/squads/squad-content/knowledge-base/signal-intelligence-v2.md +234 -0
  254. package/squads/squad-content/knowledge-base/task-ownership-map.md +235 -0
  255. package/squads/squad-content/squad.yaml +187 -27
  256. package/squads/squad-copy/knowledge-base/ai-copy-human-loop-canon.md +235 -0
  257. package/squads/squad-copy/squad.yaml +19 -4
  258. package/squads/squad-design/knowledge-base/cross-surface-token-canon.md +209 -0
  259. package/squads/squad-design/squad.yaml +19 -4
  260. package/.sinapse-ai/core/registry/service-registry.json +0 -6346
  261. package/.sinapse-ai/data/registry-update-log.jsonl +0 -1323
  262. package/.sinapse-ai/manifests/agents.csv +0 -29
  263. package/.sinapse-ai/manifests/tasks.csv +0 -204
  264. package/.sinapse-ai/manifests/workers.csv +0 -196
  265. package/squads/squad-growth/tasks/calculate-sample-size.md +0 -121
  266. package/squads/squad-paidmedia/tasks/calculate-sample-size.md +0 -57
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * setup-project-infra.js — Apply gitflow & infrastructure templates to a project
4
+ *
5
+ * Checks which templates are missing from the target project and copies only
6
+ * missing ones (never overwrites). Run via: sinapse setup-infra [target-dir]
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ const C = {
13
+ reset: '\x1b[0m', green: '\x1b[32m', yellow: '\x1b[33m',
14
+ cyan: '\x1b[36m', bold: '\x1b[1m',
15
+ };
16
+
17
+ function log(color, symbol, msg) {
18
+ console.log(`${color}${symbol} ${msg}${C.reset}`);
19
+ }
20
+
21
+ const TEMPLATES_DIR = path.resolve(__dirname, '..', 'templates');
22
+
23
+ // Mapping: template source -> project destination
24
+ const TEMPLATE_MAP = [
25
+ // GitHub templates
26
+ { src: 'github/PULL_REQUEST_TEMPLATE.md', dest: '.github/PULL_REQUEST_TEMPLATE.md' },
27
+ { src: 'github/issue-templates/bug_report.md', dest: '.github/ISSUE_TEMPLATE/bug_report.md' },
28
+ { src: 'github/issue-templates/feature_request.md', dest: '.github/ISSUE_TEMPLATE/feature_request.md' },
29
+ { src: 'github/ci-template.yml', dest: '.github/workflows/ci.yml' },
30
+ { src: 'github/CODEOWNERS.template', dest: '.github/CODEOWNERS', transform: true },
31
+ // Config templates
32
+ { src: 'config/env.example', dest: '.env.example' },
33
+ ];
34
+
35
+ function ensureDir(filePath) {
36
+ const dir = path.dirname(filePath);
37
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
38
+ }
39
+
40
+ function detectProjectName(targetDir) {
41
+ const pkgPath = path.join(targetDir, 'package.json');
42
+ if (fs.existsSync(pkgPath)) {
43
+ try { return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).name || path.basename(targetDir); }
44
+ catch { /* fall through */ }
45
+ }
46
+ return path.basename(targetDir);
47
+ }
48
+
49
+ function detectMaintainer(targetDir) {
50
+ try {
51
+ const { execSync } = require('child_process');
52
+ const name = execSync('git config user.name', { cwd: targetDir, encoding: 'utf-8' }).trim();
53
+ return name.toLowerCase().replace(/\s+/g, '') || 'maintainer';
54
+ } catch { return 'maintainer'; }
55
+ }
56
+
57
+ function applyGitignoreAdditions(targetDir) {
58
+ const additions = fs.readFileSync(path.join(TEMPLATES_DIR, 'config', 'gitignore-additions.tmpl'), 'utf-8');
59
+ const gitignorePath = path.join(targetDir, '.gitignore');
60
+ const marker = '# SINAPSE Project Ignores';
61
+
62
+ if (fs.existsSync(gitignorePath)) {
63
+ const existing = fs.readFileSync(gitignorePath, 'utf-8');
64
+ if (existing.includes(marker)) {
65
+ log(C.yellow, '[SKIP]', '.gitignore already has SINAPSE patterns');
66
+ return;
67
+ }
68
+ fs.appendFileSync(gitignorePath, '\n' + additions);
69
+ log(C.green, '[ADD]', '.gitignore updated with SINAPSE patterns');
70
+ } else {
71
+ fs.writeFileSync(gitignorePath, additions);
72
+ log(C.green, '[NEW]', '.gitignore created');
73
+ }
74
+ }
75
+
76
+ function run(targetDir) {
77
+ targetDir = path.resolve(targetDir || process.cwd());
78
+ const projectName = detectProjectName(targetDir);
79
+ const maintainer = detectMaintainer(targetDir);
80
+
81
+ console.log(`\n${C.cyan}${C.bold}=== SINAPSE Project Infrastructure Setup ===${C.reset}`);
82
+ console.log(`Target: ${targetDir}`);
83
+ console.log(`Project: ${projectName}`);
84
+ console.log(`Maintainer: ${maintainer}\n`);
85
+
86
+ let added = 0, skipped = 0;
87
+
88
+ for (const entry of TEMPLATE_MAP) {
89
+ const destPath = path.join(targetDir, entry.dest);
90
+
91
+ if (fs.existsSync(destPath)) {
92
+ log(C.yellow, '[SKIP]', `${entry.dest} (already exists)`);
93
+ skipped++;
94
+ continue;
95
+ }
96
+
97
+ ensureDir(destPath);
98
+ let content = fs.readFileSync(path.join(TEMPLATES_DIR, entry.src), 'utf-8');
99
+
100
+ if (entry.transform) {
101
+ content = content
102
+ .replace(/\{\{PROJECT_NAME\}\}/g, projectName)
103
+ .replace(/\{\{MAINTAINER\}\}/g, maintainer);
104
+ }
105
+
106
+ // Replace common variables in all templates
107
+ content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
108
+ content = content.replace(/\{\{NODE_VERSION\}\}/g, '20');
109
+
110
+ fs.writeFileSync(destPath, content);
111
+ log(C.green, '[ADD]', entry.dest);
112
+ added++;
113
+ }
114
+
115
+ // Handle .gitignore separately (merge, don't overwrite)
116
+ applyGitignoreAdditions(targetDir);
117
+
118
+ console.log(`\n${C.cyan}${C.bold}=== Summary ===${C.reset}`);
119
+ console.log(`Added: ${added} | Skipped: ${skipped}`);
120
+ console.log(`\nRun again safely anytime — existing files are never overwritten.\n`);
121
+ }
122
+
123
+ // CLI entry
124
+ if (require.main === module) {
125
+ run(process.argv[2]);
126
+ }
127
+
128
+ module.exports = { run };
@@ -720,10 +720,15 @@ class CoverageAnalyzer {
720
720
  const match = content.match(/module\.exports\s*=\s*(\{[\s\S]*\})/);
721
721
  if (match) {
722
722
  try {
723
- // eslint-disable-next-line no-eval
724
- return eval('(' + match[1] + ')');
723
+ // Try JSON.parse first (safe — no code execution)
724
+ return JSON.parse(match[1]);
725
725
  } catch {
726
- return {};
726
+ // If not valid JSON (e.g., contains JS expressions), fall back to require()
727
+ try {
728
+ return require(fullPath);
729
+ } catch {
730
+ return {};
731
+ }
727
732
  }
728
733
  }
729
734
  }
@@ -0,0 +1,380 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Validate Install Manifest Parity
4
+ *
5
+ * @script .sinapse-ai/infrastructure/scripts/validate-manifest-parity.js
6
+ * @story A.4 - Install Manifest Parity & Regeneration (epic-install-ux-hardening)
7
+ *
8
+ * Purpose:
9
+ * Catches drift between the real repository and the two source-of-truth
10
+ * files that track framework entities:
11
+ * - .sinapse-ai/install-manifest.yaml
12
+ * - .sinapse-ai/data/entity-registry.yaml
13
+ *
14
+ * Unlike scripts/validate-manifest.js (which hashes every file in the
15
+ * manifest), this script focuses on domain-level parity for the 4
16
+ * domains most likely to drift silently:
17
+ * - agents → .sinapse-ai/development/agents/*.md (top-level)
18
+ * - tasks → .sinapse-ai/development/tasks/*.md
19
+ * - templates → .sinapse-ai/development/templates/*.{md,yaml}
20
+ * - checklists → .sinapse-ai/development/checklists/*.md
21
+ *
22
+ * It also cross-validates that entity-registry.yaml agrees with the
23
+ * manifest and with reality on the number of agents and their names.
24
+ *
25
+ * Usage:
26
+ * node .sinapse-ai/infrastructure/scripts/validate-manifest-parity.js
27
+ * npm run validate:manifest:parity
28
+ *
29
+ * Options:
30
+ * --json Emit machine-readable JSON (for CI)
31
+ * --root <dir> Override the repo root (for tests)
32
+ *
33
+ * Exit codes:
34
+ * 0 - Parity OK
35
+ * 1 - Drift detected
36
+ * 2 - Script/internal error
37
+ */
38
+
39
+ 'use strict';
40
+
41
+ const fs = require('fs');
42
+ const path = require('path');
43
+ const yaml = require('js-yaml');
44
+
45
+ // Domains to check. Each entry describes one logical domain of entities
46
+ // and how to find its real files on disk.
47
+ const DOMAINS = [
48
+ {
49
+ name: 'agents',
50
+ dir: 'development/agents',
51
+ // Only top-level .md files count as "agents". Sub-folder MEMORY.md
52
+ // files belong to the agent's memory, not the agent roster.
53
+ recursive: false,
54
+ exts: ['.md'],
55
+ // Exclude internal files that live alongside agents but are not agents.
56
+ exclude: (name) => /^(README|CHANGELOG|INDEX|MEMORY)\.md$/i.test(name),
57
+ },
58
+ {
59
+ name: 'tasks',
60
+ dir: 'development/tasks',
61
+ recursive: false,
62
+ exts: ['.md'],
63
+ exclude: (name) => /^(README|CHANGELOG|INDEX)\.md$/i.test(name),
64
+ },
65
+ {
66
+ name: 'templates',
67
+ dir: 'development/templates',
68
+ recursive: false,
69
+ exts: ['.md', '.yaml', '.yml'],
70
+ exclude: (name) => /^README\.md$/i.test(name),
71
+ },
72
+ {
73
+ name: 'checklists',
74
+ dir: 'development/checklists',
75
+ recursive: false,
76
+ exts: ['.md'],
77
+ exclude: (name) => /^(README|INDEX)\.md$/i.test(name),
78
+ },
79
+ ];
80
+
81
+ /**
82
+ * Find every file on disk that belongs to a given domain.
83
+ * @param {string} sinapseCoreDir - Path to `.sinapse-ai/`
84
+ * @param {object} domain - Domain descriptor from DOMAINS
85
+ * @returns {Set<string>} Set of POSIX-style relative paths (e.g.
86
+ * `development/agents/developer.md`)
87
+ */
88
+ function scanDomain(sinapseCoreDir, domain) {
89
+ const domainDir = path.join(sinapseCoreDir, domain.dir);
90
+ const found = new Set();
91
+
92
+ if (!fs.existsSync(domainDir)) return found;
93
+
94
+ const entries = fs.readdirSync(domainDir, { withFileTypes: true });
95
+ for (const entry of entries) {
96
+ if (entry.isDirectory()) continue; // not recursing
97
+ if (!entry.isFile()) continue;
98
+ const ext = path.extname(entry.name).toLowerCase();
99
+ if (!domain.exts.includes(ext)) continue;
100
+ if (domain.exclude && domain.exclude(entry.name)) continue;
101
+ found.add(`${domain.dir}/${entry.name}`);
102
+ }
103
+ return found;
104
+ }
105
+
106
+ /**
107
+ * Collect every manifest entry whose path matches a given domain directory.
108
+ * @param {Array<{path:string}>} manifestFiles
109
+ * @param {object} domain
110
+ * @returns {Set<string>}
111
+ */
112
+ function collectManifestEntries(manifestFiles, domain) {
113
+ const set = new Set();
114
+ const prefix = `${domain.dir}/`;
115
+ for (const entry of manifestFiles) {
116
+ const p = String(entry.path || '').replace(/\\/g, '/');
117
+ if (!p.startsWith(prefix)) continue;
118
+ // Only keep top-level (same depth filter as scanDomain) unless
119
+ // the domain is configured as recursive.
120
+ const rest = p.slice(prefix.length);
121
+ if (!domain.recursive && rest.includes('/')) continue;
122
+ const ext = path.extname(rest).toLowerCase();
123
+ if (!domain.exts.includes(ext)) continue;
124
+ if (domain.exclude && domain.exclude(path.basename(rest))) continue;
125
+ set.add(p);
126
+ }
127
+ return set;
128
+ }
129
+
130
+ /**
131
+ * Diff two sets, returning extras and missings relative to disk.
132
+ * @param {Set<string>} onDisk
133
+ * @param {Set<string>} inManifest
134
+ * @returns {{missingFromManifest:string[], missingFromDisk:string[]}}
135
+ */
136
+ function diffSets(onDisk, inManifest) {
137
+ const missingFromManifest = [];
138
+ const missingFromDisk = [];
139
+ for (const p of onDisk) {
140
+ if (!inManifest.has(p)) missingFromManifest.push(p);
141
+ }
142
+ for (const p of inManifest) {
143
+ if (!onDisk.has(p)) missingFromDisk.push(p);
144
+ }
145
+ missingFromManifest.sort();
146
+ missingFromDisk.sort();
147
+ return { missingFromManifest, missingFromDisk };
148
+ }
149
+
150
+ /**
151
+ * Load a YAML file, returning null if it does not exist.
152
+ */
153
+ function loadYaml(filePath) {
154
+ if (!fs.existsSync(filePath)) return null;
155
+ const raw = fs.readFileSync(filePath, 'utf8');
156
+ return yaml.load(raw);
157
+ }
158
+
159
+ /**
160
+ * Extract the set of top-level agent names from entity-registry.yaml.
161
+ * Registry shape: entities.agents.{agentName}: { ... }
162
+ * @returns {Set<string>}
163
+ */
164
+ function getRegistryAgentNames(registry) {
165
+ const names = new Set();
166
+ const agents = registry && registry.entities && registry.entities.agents;
167
+ if (!agents || typeof agents !== 'object') return names;
168
+ for (const key of Object.keys(agents)) names.add(key);
169
+ return names;
170
+ }
171
+
172
+ /**
173
+ * Run the full parity check.
174
+ * @param {string} repoRoot - Repo root directory
175
+ * @returns {object} report
176
+ */
177
+ function runParityCheck(repoRoot) {
178
+ const sinapseCoreDir = path.join(repoRoot, '.sinapse-ai');
179
+ const manifestPath = path.join(sinapseCoreDir, 'install-manifest.yaml');
180
+ const registryPath = path.join(sinapseCoreDir, 'data', 'entity-registry.yaml');
181
+
182
+ const report = {
183
+ ok: true,
184
+ errors: [],
185
+ domains: {},
186
+ registry: {
187
+ checked: false,
188
+ agentCountManifest: 0,
189
+ agentCountRegistry: 0,
190
+ agentCountDisk: 0,
191
+ extraInRegistry: [],
192
+ missingInRegistry: [],
193
+ },
194
+ };
195
+
196
+ const manifest = loadYaml(manifestPath);
197
+ if (!manifest) {
198
+ report.ok = false;
199
+ report.errors.push(`install-manifest.yaml not found at ${manifestPath}`);
200
+ return report;
201
+ }
202
+ if (!Array.isArray(manifest.files)) {
203
+ report.ok = false;
204
+ report.errors.push('install-manifest.yaml has no `files` array');
205
+ return report;
206
+ }
207
+
208
+ for (const domain of DOMAINS) {
209
+ const onDisk = scanDomain(sinapseCoreDir, domain);
210
+ const inManifest = collectManifestEntries(manifest.files, domain);
211
+ const { missingFromManifest, missingFromDisk } = diffSets(onDisk, inManifest);
212
+
213
+ const domainOk = missingFromManifest.length === 0 && missingFromDisk.length === 0;
214
+ if (!domainOk) report.ok = false;
215
+
216
+ report.domains[domain.name] = {
217
+ ok: domainOk,
218
+ dir: domain.dir,
219
+ diskCount: onDisk.size,
220
+ manifestCount: inManifest.size,
221
+ missingFromManifest,
222
+ missingFromDisk,
223
+ };
224
+ }
225
+
226
+ // Cross-validate entity-registry.yaml against manifest + disk for agents.
227
+ const registry = loadYaml(registryPath);
228
+ if (!registry) {
229
+ report.ok = false;
230
+ report.errors.push(`entity-registry.yaml not found at ${registryPath}`);
231
+ } else {
232
+ report.registry.checked = true;
233
+ const agentsDomain = report.domains.agents;
234
+ const diskAgentNames = new Set(
235
+ Array.from(scanDomain(sinapseCoreDir, DOMAINS[0])).map((p) =>
236
+ path.basename(p, '.md'),
237
+ ),
238
+ );
239
+ const registryAgentNames = getRegistryAgentNames(registry);
240
+ report.registry.agentCountDisk = diskAgentNames.size;
241
+ report.registry.agentCountManifest = agentsDomain ? agentsDomain.manifestCount : 0;
242
+ report.registry.agentCountRegistry = registryAgentNames.size;
243
+
244
+ // Registry should mention exactly the same agent names as disk.
245
+ for (const name of registryAgentNames) {
246
+ if (!diskAgentNames.has(name)) report.registry.extraInRegistry.push(name);
247
+ }
248
+ for (const name of diskAgentNames) {
249
+ if (!registryAgentNames.has(name)) report.registry.missingInRegistry.push(name);
250
+ }
251
+ report.registry.extraInRegistry.sort();
252
+ report.registry.missingInRegistry.sort();
253
+
254
+ if (
255
+ report.registry.extraInRegistry.length > 0 ||
256
+ report.registry.missingInRegistry.length > 0 ||
257
+ report.registry.agentCountRegistry !== report.registry.agentCountDisk
258
+ ) {
259
+ report.ok = false;
260
+ }
261
+ }
262
+
263
+ return report;
264
+ }
265
+
266
+ /**
267
+ * Render a human-readable report.
268
+ */
269
+ function printReport(report) {
270
+ const line = '='.repeat(60);
271
+ console.log(line);
272
+ console.log('SINAPSE Install Manifest Parity Report');
273
+ console.log(line);
274
+
275
+ if (report.errors.length > 0) {
276
+ console.log('');
277
+ console.log('ERRORS:');
278
+ for (const err of report.errors) console.log(` - ${err}`);
279
+ }
280
+
281
+ console.log('');
282
+ console.log('Domains:');
283
+ for (const [name, info] of Object.entries(report.domains)) {
284
+ const status = info.ok ? 'OK' : 'DRIFT';
285
+ console.log(
286
+ ` ${name.padEnd(11)} ${status.padEnd(6)} disk=${info.diskCount} manifest=${info.manifestCount}`,
287
+ );
288
+ if (info.missingFromManifest.length > 0) {
289
+ console.log(` Missing from manifest (${info.missingFromManifest.length}):`);
290
+ for (const p of info.missingFromManifest.slice(0, 20)) console.log(` + ${p}`);
291
+ if (info.missingFromManifest.length > 20) {
292
+ console.log(` ... and ${info.missingFromManifest.length - 20} more`);
293
+ }
294
+ }
295
+ if (info.missingFromDisk.length > 0) {
296
+ console.log(` In manifest but missing on disk (${info.missingFromDisk.length}):`);
297
+ for (const p of info.missingFromDisk.slice(0, 20)) console.log(` - ${p}`);
298
+ if (info.missingFromDisk.length > 20) {
299
+ console.log(` ... and ${info.missingFromDisk.length - 20} more`);
300
+ }
301
+ }
302
+ }
303
+
304
+ if (report.registry.checked) {
305
+ const r = report.registry;
306
+ const status =
307
+ r.extraInRegistry.length === 0 &&
308
+ r.missingInRegistry.length === 0 &&
309
+ r.agentCountDisk === r.agentCountRegistry
310
+ ? 'OK'
311
+ : 'DRIFT';
312
+ console.log('');
313
+ console.log(`Entity Registry cross-check: ${status}`);
314
+ console.log(
315
+ ` agents: disk=${r.agentCountDisk} manifest=${r.agentCountManifest} registry=${r.agentCountRegistry}`,
316
+ );
317
+ if (r.extraInRegistry.length > 0) {
318
+ console.log(` Extra in registry (${r.extraInRegistry.length}):`);
319
+ for (const n of r.extraInRegistry) console.log(` ! ${n}`);
320
+ }
321
+ if (r.missingInRegistry.length > 0) {
322
+ console.log(` Missing from registry (${r.missingInRegistry.length}):`);
323
+ for (const n of r.missingInRegistry) console.log(` ? ${n}`);
324
+ }
325
+ }
326
+
327
+ console.log('');
328
+ console.log('-'.repeat(60));
329
+ if (report.ok) {
330
+ console.log('PARITY OK — manifest, disk, and registry agree.');
331
+ } else {
332
+ console.log('PARITY DRIFT — fix with:');
333
+ console.log(' 1. npm run generate:manifest');
334
+ console.log(' 2. review entity-registry.yaml agents section');
335
+ console.log(' 3. rerun npm run validate:manifest:parity');
336
+ }
337
+ console.log('-'.repeat(60));
338
+ }
339
+
340
+ function parseArgs(argv) {
341
+ const opts = { json: false, root: process.cwd() };
342
+ for (let i = 2; i < argv.length; i++) {
343
+ const a = argv[i];
344
+ if (a === '--json') opts.json = true;
345
+ else if (a === '--root' && argv[i + 1]) {
346
+ opts.root = argv[++i];
347
+ }
348
+ }
349
+ return opts;
350
+ }
351
+
352
+ function main() {
353
+ const opts = parseArgs(process.argv);
354
+ try {
355
+ const report = runParityCheck(opts.root);
356
+ if (opts.json) {
357
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
358
+ } else {
359
+ printReport(report);
360
+ }
361
+ process.exit(report.ok ? 0 : 1);
362
+ } catch (err) {
363
+ console.error('validate-manifest-parity: internal error:', err && err.message);
364
+ if (process.env.DEBUG) console.error(err && err.stack);
365
+ process.exit(2);
366
+ }
367
+ }
368
+
369
+ if (require.main === module) {
370
+ main();
371
+ }
372
+
373
+ module.exports = {
374
+ runParityCheck,
375
+ scanDomain,
376
+ collectManifestEntries,
377
+ diffSets,
378
+ getRegistryAgentNames,
379
+ DOMAINS,
380
+ };
@@ -20,34 +20,71 @@ function parseArgs(argv = process.argv.slice(2)) {
20
20
  return {
21
21
  quiet: args.has('--quiet') || args.has('-q'),
22
22
  json: args.has('--json'),
23
+ fast: args.has('--fast'),
23
24
  contractPath: contractArg ? contractArg.slice('--contract='.length) : null,
24
25
  diffPath: diffArg ? diffArg.slice('--diff='.length) : null,
25
26
  };
26
27
  }
27
28
 
29
+ function extractSyncFailureDetails(rawOutput) {
30
+ if (!rawOutput || typeof rawOutput !== 'string') {
31
+ return [];
32
+ }
33
+ const details = [];
34
+ const driftMatch = rawOutput.match(/\|\s*Drift\s*\|\s*(\d+)\s*\|/i);
35
+ const missingMatch = rawOutput.match(/\|\s*Missing\s*\|\s*(\d+)\s*\|/i);
36
+ const orphanedMatch = rawOutput.match(/\|\s*Orphaned\s*\|\s*(\d+)\s*\|/i);
37
+ if (driftMatch && Number(driftMatch[1]) > 0) {
38
+ details.push(`${driftMatch[1]} file(s) drifted from source-of-truth`);
39
+ }
40
+ if (missingMatch && Number(missingMatch[1]) > 0) {
41
+ details.push(`${missingMatch[1]} file(s) missing from IDE mirror`);
42
+ }
43
+ if (orphanedMatch && Number(orphanedMatch[1]) > 0) {
44
+ details.push(`${orphanedMatch[1]} orphaned file(s) in IDE mirror`);
45
+ }
46
+ const fileBlocks = rawOutput.match(/(?:^|\n)-\s+`?([^`\n]+\.md)`?/g);
47
+ if (fileBlocks) {
48
+ for (const block of fileBlocks.slice(0, 5)) {
49
+ const name = block.replace(/^[\s\-`]+|`+\s*$/g, '');
50
+ if (name) details.push(` - ${name}`);
51
+ }
52
+ }
53
+ return details;
54
+ }
55
+
28
56
  function runSyncValidate(ide, projectRoot) {
29
57
  const script = path.join('.sinapse-ai', 'infrastructure', 'scripts', 'ide-sync', 'index.js');
30
58
  const result = spawnSync('node', [script, 'validate', '--ide', ide, '--strict'], {
31
59
  cwd: projectRoot,
32
60
  encoding: 'utf8',
33
61
  });
34
- return {
35
- ok: result.status === 0,
36
- errors: result.status === 0 ? [] : [`Sync validation failed for ${ide}`],
37
- warnings: [],
38
- raw: result.stdout || result.stderr || '',
39
- };
62
+ const raw = result.stdout || result.stderr || '';
63
+ if (result.status === 0) {
64
+ return { ok: true, errors: [], warnings: [], raw };
65
+ }
66
+ const details = extractSyncFailureDetails(raw);
67
+ const errors = [
68
+ `Sync validation failed for ${ide}`,
69
+ ...details,
70
+ `Run \`npm run sync:ide\` then re-stage the .claude/ and .codex/ changes`,
71
+ ];
72
+ return { ok: false, errors, warnings: [], raw };
40
73
  }
41
74
 
42
75
  function getDefaultContractPath(projectRoot = process.cwd()) {
43
- return path.join(
76
+ const contractsDir = path.join(
44
77
  projectRoot,
45
78
  '.sinapse-ai',
46
79
  'infrastructure',
47
80
  'contracts',
48
81
  'compatibility',
49
- 'sinapse-4.0.4.yaml',
50
82
  );
83
+ const currentPath = path.join(contractsDir, 'sinapse-current.yaml');
84
+ if (fs.existsSync(currentPath)) {
85
+ return currentPath;
86
+ }
87
+ return path.join(contractsDir, 'sinapse-4.0.4.yaml');
51
88
  }
52
89
 
53
90
  function loadCompatibilityContract(contractPath) {
@@ -218,6 +255,7 @@ function diffCompatibilityContracts(currentContract, previousContract) {
218
255
 
219
256
  function runParityValidation(options = {}, deps = {}) {
220
257
  const projectRoot = options.projectRoot || process.cwd();
258
+ const fast = Boolean(options.fast);
221
259
  const runSync = deps.runSyncValidate || runSyncValidate;
222
260
  const runCodexSync = deps.validateCodexSync || validateCodexSync;
223
261
  const runClaudeIntegration = deps.validateClaudeIntegration || validateClaudeIntegration;
@@ -228,12 +266,12 @@ function runParityValidation(options = {}, deps = {}) {
228
266
  ? path.resolve(projectRoot, options.contractPath)
229
267
  : getDefaultContractPath(projectRoot);
230
268
  const loadContract = deps.loadCompatibilityContract || loadCompatibilityContract;
231
- const contract = loadContract(resolvedContractPath);
269
+ const contract = fast ? null : loadContract(resolvedContractPath);
232
270
  const resolvedDiffPath = options.diffPath ? path.resolve(projectRoot, options.diffPath) : null;
233
- const previousContract = resolvedDiffPath ? loadContract(resolvedDiffPath) : null;
271
+ const previousContract = resolvedDiffPath && !fast ? loadContract(resolvedDiffPath) : null;
234
272
  const docsPath = path.join(projectRoot, 'docs', 'ide-integration.md');
235
273
  const docsPathRelative = path.relative(projectRoot, docsPath);
236
- const checks = [
274
+ const fullChecks = [
237
275
  { id: 'claude-sync', exec: () => runSync('claude-code', projectRoot) },
238
276
  { id: 'claude-integration', exec: () => runClaudeIntegration({ projectRoot }) },
239
277
  { id: 'codex-sync', exec: () => runCodexSync({ projectRoot, quiet: true }) },
@@ -241,31 +279,42 @@ function runParityValidation(options = {}, deps = {}) {
241
279
  { id: 'codex-skills', exec: () => runCodexSkills({ projectRoot, strict: true, quiet: true }) },
242
280
  { id: 'paths', exec: () => runPaths({ projectRoot }) },
243
281
  ];
282
+ const fastChecks = [
283
+ { id: 'claude-sync', exec: () => runSync('claude-code', projectRoot) },
284
+ { id: 'codex-sync', exec: () => runCodexSync({ projectRoot, quiet: true }) },
285
+ { id: 'paths', exec: () => runPaths({ projectRoot }) },
286
+ ];
287
+ const checks = fast ? fastChecks : fullChecks;
244
288
 
245
289
  const results = checks.map((check) => {
246
290
  const normalized = normalizeResult(check.exec());
247
291
  return { id: check.id, ...normalized };
248
292
  });
249
293
  const resultById = Object.fromEntries(results.map((r) => [r.id, r]));
250
- const contractViolations = validateCompatibilityContract(contract, resultById, {
251
- docsPath,
252
- docsPathRelative,
253
- });
254
- const contractSummary = contract
255
- ? {
256
- release: contract.release || null,
257
- path: path.relative(projectRoot, resolvedContractPath),
258
- }
259
- : {
260
- release: null,
261
- path: path.relative(projectRoot, resolvedContractPath),
262
- };
294
+ const contractViolations = fast
295
+ ? []
296
+ : validateCompatibilityContract(contract, resultById, {
297
+ docsPath,
298
+ docsPathRelative,
299
+ });
300
+ const contractSummary = fast
301
+ ? null
302
+ : contract
303
+ ? {
304
+ release: contract.release || null,
305
+ path: path.relative(projectRoot, resolvedContractPath),
306
+ }
307
+ : {
308
+ release: null,
309
+ path: path.relative(projectRoot, resolvedContractPath),
310
+ };
263
311
 
264
312
  return {
265
313
  ok: results.every((r) => r.ok) && contractViolations.length === 0,
314
+ fast,
266
315
  checks: results,
267
316
  contract: contractSummary,
268
- contractDiff: diffCompatibilityContracts(contract, previousContract),
317
+ contractDiff: fast ? null : diffCompatibilityContracts(contract, previousContract),
269
318
  contractViolations,
270
319
  };
271
320
  }
@@ -347,4 +396,6 @@ module.exports = {
347
396
  normalizeResult,
348
397
  formatHumanReport,
349
398
  diffCompatibilityContracts,
399
+ getDefaultContractPath,
400
+ extractSyncFailureDetails,
350
401
  };