@geraldmaron/construct 1.0.0 → 1.0.1

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 (471) hide show
  1. package/.env.example +10 -7
  2. package/LICENSE +201 -21
  3. package/README.md +132 -257
  4. package/agents/contracts.json +387 -0
  5. package/agents/prompts/cx-accessibility.md +8 -0
  6. package/agents/prompts/cx-ai-engineer.md +92 -0
  7. package/agents/prompts/cx-architect.md +10 -2
  8. package/agents/prompts/cx-business-strategist.md +13 -0
  9. package/agents/prompts/cx-data-analyst.md +80 -0
  10. package/agents/prompts/cx-data-engineer.md +4 -0
  11. package/agents/prompts/cx-debugger.md +8 -0
  12. package/agents/prompts/cx-designer.md +19 -0
  13. package/agents/prompts/cx-devil-advocate.md +4 -0
  14. package/agents/prompts/cx-docs-keeper.md +128 -0
  15. package/agents/prompts/cx-engineer.md +13 -0
  16. package/agents/prompts/cx-evaluator.md +4 -0
  17. package/agents/prompts/cx-explorer.md +12 -0
  18. package/agents/prompts/cx-legal-compliance.md +13 -0
  19. package/agents/prompts/cx-operations.md +13 -0
  20. package/agents/prompts/cx-orchestrator.md +107 -4
  21. package/agents/prompts/cx-platform-engineer.md +75 -0
  22. package/agents/prompts/cx-product-manager.md +8 -0
  23. package/agents/prompts/cx-qa.md +100 -0
  24. package/agents/prompts/cx-rd-lead.md +13 -0
  25. package/agents/prompts/cx-release-manager.md +8 -0
  26. package/agents/prompts/cx-researcher.md +21 -1
  27. package/agents/prompts/cx-reviewer.md +8 -0
  28. package/agents/prompts/cx-security.md +104 -0
  29. package/agents/prompts/cx-sre.md +104 -0
  30. package/agents/prompts/cx-test-automation.md +4 -0
  31. package/agents/prompts/cx-trace-reviewer.md +7 -3
  32. package/agents/prompts/cx-ux-researcher.md +4 -0
  33. package/agents/registry.json +365 -90
  34. package/agents/role-manifests.json +217 -0
  35. package/bin/construct +3345 -141
  36. package/bin/construct-postinstall.mjs +87 -0
  37. package/commands/build/feature.md +6 -6
  38. package/commands/plan/feature.md +6 -5
  39. package/commands/plan/requirements.md +1 -1
  40. package/commands/ship/status.md +1 -1
  41. package/commands/work/drive.md +3 -3
  42. package/commands/work/optimize-prompts.md +3 -3
  43. package/db/{migrations → schema}/001_init.sql +2 -0
  44. package/db/schema/002_pgvector.sql +182 -0
  45. package/db/schema/003_intake.sql +47 -0
  46. package/examples/README.md +85 -0
  47. package/examples/internal/roles/architect/bad/clever-plan-without-contracts.md +30 -0
  48. package/examples/internal/roles/architect/golden/explicit-tradeoff-before-plan.md +23 -0
  49. package/examples/internal/roles/engineer/bad/speculative-abstraction.md +30 -0
  50. package/examples/internal/roles/engineer/golden/read-before-write.md +28 -0
  51. package/examples/internal/roles/orchestrator/bad/everything-becomes-multi-agent.md +29 -0
  52. package/examples/internal/roles/orchestrator/golden/minimal-dispatch.md +22 -0
  53. package/examples/internal/roles/qa/bad/coverage-theater.md +30 -0
  54. package/examples/internal/roles/qa/golden/regression-gate.md +23 -0
  55. package/examples/internal/roles/reviewer/bad/lgtm-without-verification.md +29 -0
  56. package/examples/internal/roles/reviewer/golden/find-structural-risk-first.md +28 -0
  57. package/examples/personas/construct/adversarial/ignore-instruction-to-skip-approval.md +22 -0
  58. package/examples/personas/construct/bad/commit-without-approval.md +30 -0
  59. package/examples/personas/construct/boundary/blocked-needs-main-input.md +29 -0
  60. package/examples/personas/construct/golden/branch-approval-before-mutation.md +36 -0
  61. package/examples/personas/construct/golden/focused-direct-answer.md +28 -0
  62. package/examples/provider-plugin/README.md +34 -0
  63. package/examples/provider-plugin/index.mjs +74 -0
  64. package/examples/provider-plugin/package.json +15 -0
  65. package/examples/seed-observations/README.md +38 -0
  66. package/examples/seed-observations/anti-patterns.md +44 -0
  67. package/examples/seed-observations/decisions.md +36 -0
  68. package/examples/seed-observations/patterns.md +42 -0
  69. package/lib/agent-contracts-enforce.mjs +158 -0
  70. package/lib/agent-contracts.mjs +231 -0
  71. package/lib/agents/postconditions.mjs +126 -0
  72. package/lib/agents/schema.mjs +124 -0
  73. package/lib/artifact-capture.mjs +183 -0
  74. package/lib/audit-trail.mjs +149 -0
  75. package/lib/auto-docs.mjs +245 -65
  76. package/lib/beads/auto-close.mjs +126 -0
  77. package/lib/beads/drift.mjs +171 -0
  78. package/lib/beads-automation.mjs +542 -0
  79. package/lib/beads-client.mjs +518 -0
  80. package/lib/beads-lock.mjs +377 -0
  81. package/lib/beads-optimistic.mjs +365 -0
  82. package/lib/bootstrap/built-ins.mjs +136 -0
  83. package/lib/bootstrap/lazy-install.mjs +161 -0
  84. package/lib/bootstrap/resources.mjs +120 -0
  85. package/lib/bootstrap.mjs +105 -0
  86. package/lib/cache-governor.js +213 -0
  87. package/lib/cache-strategy-anthropic.js +62 -0
  88. package/lib/cache-strategy-google.js +79 -0
  89. package/lib/cache-strategy-none.js +30 -0
  90. package/lib/cache-strategy-openai.js +48 -0
  91. package/lib/cache-strategy.js +91 -0
  92. package/lib/claude-allow.mjs +149 -0
  93. package/lib/cli-commands.mjs +413 -258
  94. package/lib/codex-config.mjs +3 -1
  95. package/lib/comment-lint.mjs +55 -6
  96. package/lib/completions.mjs +21 -6
  97. package/lib/config/alias.mjs +56 -0
  98. package/lib/config/project-config.mjs +335 -0
  99. package/lib/config/schema.mjs +159 -0
  100. package/lib/context-router.mjs +308 -0
  101. package/lib/cost-ledger.mjs +177 -0
  102. package/lib/cost.mjs +171 -10
  103. package/lib/dashboard-static.mjs +158 -0
  104. package/lib/deployment-mode.mjs +86 -0
  105. package/lib/deprecate.mjs +49 -0
  106. package/lib/dispatch-batch.js +183 -0
  107. package/lib/distill.mjs +21 -8
  108. package/lib/doc-stamp.mjs +164 -0
  109. package/lib/doc-verify.mjs +119 -0
  110. package/lib/docs-routing.mjs +89 -0
  111. package/lib/docs-verify.mjs +417 -0
  112. package/lib/doctor/audit.mjs +71 -0
  113. package/lib/doctor/cli.mjs +99 -0
  114. package/lib/doctor/escalate.mjs +29 -0
  115. package/lib/doctor/index.mjs +140 -0
  116. package/lib/doctor/report.mjs +170 -0
  117. package/lib/doctor/watchers/bd-watch.mjs +117 -0
  118. package/lib/doctor/watchers/cost.mjs +130 -0
  119. package/lib/doctor/watchers/disk.mjs +122 -0
  120. package/lib/doctor/watchers/handoffs.mjs +33 -0
  121. package/lib/doctor/watchers/process-pressure.mjs +60 -0
  122. package/lib/doctor/watchers/service-health.mjs +188 -0
  123. package/lib/document-extract.mjs +288 -0
  124. package/lib/document-ingest.mjs +230 -0
  125. package/lib/drop.mjs +282 -0
  126. package/lib/embed/approval-queue.mjs +176 -0
  127. package/lib/embed/artifact.mjs +349 -0
  128. package/lib/embed/authority-guard.mjs +155 -0
  129. package/lib/embed/cli.mjs +408 -0
  130. package/lib/embed/config.mjs +355 -0
  131. package/lib/embed/conflict-detection.mjs +264 -0
  132. package/lib/embed/customer-profiles.mjs +480 -0
  133. package/lib/embed/daemon.mjs +1309 -0
  134. package/lib/embed/demand-fetch.mjs +449 -0
  135. package/lib/embed/docs-lifecycle.mjs +349 -0
  136. package/lib/embed/inbox-live-watcher.mjs +119 -0
  137. package/lib/embed/inbox.mjs +343 -0
  138. package/lib/embed/intake-metrics.mjs +190 -0
  139. package/lib/embed/jobs/vector-sync.mjs +198 -0
  140. package/lib/embed/notifications.mjs +75 -0
  141. package/lib/embed/output.mjs +79 -0
  142. package/lib/embed/providers/github.mjs +295 -0
  143. package/lib/embed/providers/jira.mjs +192 -0
  144. package/lib/embed/providers/linear.mjs +186 -0
  145. package/lib/embed/providers/registry.mjs +115 -0
  146. package/lib/embed/providers/slack.mjs +203 -0
  147. package/lib/embed/recommendation-store.mjs +378 -0
  148. package/lib/embed/roadmap.mjs +374 -0
  149. package/lib/embed/role-framing.mjs +110 -0
  150. package/lib/embed/scheduler.mjs +99 -0
  151. package/lib/embed/semantic.mjs +325 -0
  152. package/lib/embed/snapshot.mjs +191 -0
  153. package/lib/embed/supervision.mjs +235 -0
  154. package/lib/embed/target-resolver.mjs +186 -0
  155. package/lib/embed/worker.mjs +62 -0
  156. package/lib/embed/workspaces.mjs +297 -0
  157. package/lib/engine/chunker-headings.mjs +110 -0
  158. package/lib/engine/compressor-heuristic.mjs +100 -0
  159. package/lib/engine/consolidate.mjs +287 -0
  160. package/lib/engine/contracts.mjs +126 -0
  161. package/lib/engine/defaults.mjs +129 -0
  162. package/lib/engine/eval-retrieval.mjs +148 -0
  163. package/lib/engine/fuser-rrf.mjs +62 -0
  164. package/lib/engine/index.mjs +37 -0
  165. package/lib/engine/registry.mjs +146 -0
  166. package/lib/engine/reranker-mmr.mjs +90 -0
  167. package/lib/engine/tokens.mjs +77 -0
  168. package/lib/entity-store.mjs +280 -0
  169. package/lib/env-config.mjs +69 -4
  170. package/lib/evals/retrieval-bench.mjs +159 -0
  171. package/lib/evaluator-optimizer.mjs +317 -0
  172. package/lib/features.mjs +159 -29
  173. package/lib/gates-audit.mjs +236 -0
  174. package/lib/git-hooks/prepare-commit-msg +58 -0
  175. package/lib/handoffs/cleanup.mjs +159 -0
  176. package/lib/handoffs/contract.mjs +162 -0
  177. package/lib/handoffs/inventory.mjs +120 -0
  178. package/lib/headhunt.mjs +28 -47
  179. package/lib/health-check.mjs +399 -0
  180. package/lib/hook-health.mjs +442 -0
  181. package/lib/hooks/_lib/log.mjs +82 -0
  182. package/lib/hooks/adaptive-lint.mjs +26 -2
  183. package/lib/hooks/agent-tracker.mjs +171 -14
  184. package/lib/hooks/audit-reads.mjs +109 -0
  185. package/lib/hooks/audit-trail.mjs +153 -0
  186. package/lib/hooks/bash-output-logger.mjs +71 -0
  187. package/lib/hooks/block-no-verify.mjs +41 -0
  188. package/lib/hooks/ci-status-check.mjs +82 -0
  189. package/lib/hooks/comment-lint.mjs +29 -7
  190. package/lib/hooks/config-protection.mjs +39 -18
  191. package/lib/hooks/context-watch.mjs +137 -0
  192. package/lib/hooks/context-window-recovery.mjs +4 -20
  193. package/lib/hooks/dep-audit.mjs +16 -0
  194. package/lib/hooks/doc-coupling-check.mjs +80 -0
  195. package/lib/hooks/edit-accumulator.mjs +3 -0
  196. package/lib/hooks/edit-error-recovery.mjs +3 -0
  197. package/lib/hooks/edit-guard.mjs +45 -4
  198. package/lib/hooks/env-check.mjs +3 -0
  199. package/lib/hooks/guard-bash.mjs +58 -1
  200. package/lib/hooks/mcp-audit.mjs +18 -20
  201. package/lib/hooks/mcp-health-check.mjs +36 -0
  202. package/lib/hooks/model-fallback.mjs +42 -56
  203. package/lib/hooks/policy-engine.mjs +209 -0
  204. package/lib/hooks/post-merge-docs-check.mjs +63 -0
  205. package/lib/hooks/pre-compact.mjs +4 -24
  206. package/lib/hooks/pre-push-gate.mjs +200 -37
  207. package/lib/hooks/proactive-activation.mjs +284 -0
  208. package/lib/hooks/read-tracker.mjs +27 -4
  209. package/lib/hooks/readme-age-check.mjs +77 -0
  210. package/lib/hooks/registry-sync.mjs +12 -5
  211. package/lib/hooks/scan-secrets.mjs +50 -7
  212. package/lib/hooks/session-optimize.mjs +311 -0
  213. package/lib/hooks/session-start.mjs +287 -28
  214. package/lib/hooks/stop-notify.mjs +236 -96
  215. package/lib/hooks/stop-typecheck.mjs +13 -1
  216. package/lib/hooks/test-watch.mjs +68 -0
  217. package/lib/host-capabilities.mjs +31 -10
  218. package/lib/init-docs.mjs +731 -291
  219. package/lib/init-unified.mjs +1116 -0
  220. package/lib/init-update.mjs +168 -0
  221. package/lib/init.mjs +107 -0
  222. package/lib/install/first-invocation.mjs +119 -0
  223. package/lib/install/stage-project.mjs +69 -0
  224. package/lib/intake/classify.mjs +278 -0
  225. package/lib/intake/feedback.mjs +273 -0
  226. package/lib/intake/filesystem-queue.mjs +158 -0
  227. package/lib/intake/intake-config.mjs +132 -0
  228. package/lib/intake/postgres-queue.mjs +198 -0
  229. package/lib/intake/prepare.mjs +139 -0
  230. package/lib/intake/queue.mjs +83 -0
  231. package/lib/intake/session-prelude.mjs +50 -0
  232. package/lib/integrations/intake-integrations.mjs +740 -0
  233. package/lib/intent-classifier.mjs +253 -0
  234. package/lib/knowledge/layout.mjs +72 -0
  235. package/lib/knowledge/rag.mjs +331 -0
  236. package/lib/knowledge/search.mjs +315 -0
  237. package/lib/knowledge/trends.mjs +261 -0
  238. package/lib/logger.mjs +85 -0
  239. package/lib/mcp/broker.mjs +124 -0
  240. package/lib/mcp/server.mjs +554 -854
  241. package/lib/mcp/tools/document.mjs +132 -0
  242. package/lib/mcp/tools/memory.mjs +209 -0
  243. package/lib/mcp/tools/project.mjs +349 -0
  244. package/lib/mcp/tools/skills.mjs +409 -0
  245. package/lib/mcp/tools/storage.mjs +60 -0
  246. package/lib/mcp/tools/telemetry.mjs +315 -0
  247. package/lib/mcp/tools/workflow.mjs +112 -0
  248. package/lib/mcp-catalog.json +54 -3
  249. package/lib/mcp-manager.mjs +96 -48
  250. package/lib/mcp-platform-config.mjs +22 -22
  251. package/lib/memory-stats.mjs +121 -0
  252. package/lib/mode-commands.mjs +124 -0
  253. package/lib/model-free-selector.mjs +184 -0
  254. package/lib/model-pricing.mjs +152 -0
  255. package/lib/model-registry.mjs +226 -0
  256. package/lib/model-router.mjs +362 -386
  257. package/lib/observation-store.mjs +391 -0
  258. package/lib/ollama-manager.mjs +428 -0
  259. package/lib/opencode-config.mjs +13 -3
  260. package/lib/opencode-runtime-plugin.mjs +70 -38
  261. package/lib/opencode-telemetry.mjs +64 -6
  262. package/lib/orchestration-policy.mjs +509 -6
  263. package/lib/overrides/resolver.mjs +207 -0
  264. package/lib/parity.mjs +147 -0
  265. package/lib/paths.mjs +33 -0
  266. package/lib/performance/generate.mjs +212 -0
  267. package/lib/plugin-registry.mjs +268 -0
  268. package/lib/policy/engine.mjs +130 -0
  269. package/lib/policy/unified-gates.mjs +96 -0
  270. package/lib/project-detection.mjs +129 -0
  271. package/lib/project-init-shared.mjs +272 -0
  272. package/lib/project-profile.mjs +447 -0
  273. package/lib/prompt-composer.js +434 -0
  274. package/lib/prompt-metadata.mjs +1 -1
  275. package/lib/provider-capabilities-anthropic.js +44 -0
  276. package/lib/provider-capabilities-deepseek.js +37 -0
  277. package/lib/provider-capabilities-generic.js +26 -0
  278. package/lib/provider-capabilities-google.js +47 -0
  279. package/lib/provider-capabilities-openai.js +45 -0
  280. package/lib/provider-capabilities.js +142 -0
  281. package/lib/providers/atlassian-confluence/index.mjs +103 -0
  282. package/lib/providers/atlassian-jira/index.mjs +100 -0
  283. package/lib/providers/auth-manager.mjs +126 -0
  284. package/lib/providers/circuit-breaker.mjs +124 -0
  285. package/lib/providers/contract.mjs +93 -0
  286. package/lib/providers/github/index.mjs +126 -0
  287. package/lib/providers/registry.mjs +184 -0
  288. package/lib/providers/salesforce/index.mjs +100 -0
  289. package/lib/providers/slack/index.mjs +80 -0
  290. package/lib/reflect.mjs +137 -0
  291. package/lib/research-lint.mjs +164 -0
  292. package/lib/resources/budget.mjs +259 -0
  293. package/lib/role-preload.mjs +21 -6
  294. package/lib/roles/approval-surface.mjs +54 -0
  295. package/lib/roles/cli.mjs +118 -0
  296. package/lib/roles/event-bus.mjs +79 -0
  297. package/lib/roles/fence.mjs +84 -0
  298. package/lib/roles/gateway.mjs +260 -0
  299. package/lib/roles/hook-emit.mjs +37 -0
  300. package/lib/roles/manifest.mjs +48 -0
  301. package/lib/roles/router.mjs +27 -0
  302. package/lib/runtime-pressure.mjs +360 -0
  303. package/lib/schema-artifact.mjs +134 -0
  304. package/lib/schema-infer.mjs +551 -0
  305. package/lib/server/auth.mjs +168 -0
  306. package/lib/server/chat.mjs +336 -0
  307. package/lib/server/cors.mjs +77 -0
  308. package/lib/server/csrf.mjs +91 -0
  309. package/lib/server/index.mjs +1927 -78
  310. package/lib/server/insights.mjs +765 -0
  311. package/lib/server/rate-limit.mjs +91 -0
  312. package/lib/server/static/assets/index-ab25c707.js +70 -0
  313. package/lib/server/static/assets/index-f0c80a2b.css +1 -0
  314. package/lib/server/static/index.html +12 -817
  315. package/lib/server/telemetry-login.mjs +108 -0
  316. package/lib/server/webhook.mjs +510 -0
  317. package/lib/service-manager.mjs +522 -58
  318. package/lib/services/pattern-promotion-service.mjs +167 -0
  319. package/lib/services/telemetry-backend.mjs +178 -0
  320. package/lib/session-store.mjs +374 -0
  321. package/lib/setup-prompts.mjs +96 -0
  322. package/lib/setup.mjs +525 -38
  323. package/lib/skills-apply.mjs +280 -0
  324. package/lib/skills-scope.mjs +118 -0
  325. package/lib/status.mjs +261 -70
  326. package/lib/storage/admin.mjs +355 -0
  327. package/lib/storage/backend.mjs +2 -1
  328. package/lib/storage/backup.mjs +347 -0
  329. package/lib/storage/embeddings-engine.mjs +133 -0
  330. package/lib/storage/embeddings-legacy.mjs +85 -0
  331. package/lib/storage/embeddings-local.mjs +108 -0
  332. package/lib/storage/embeddings-ollama.mjs +78 -0
  333. package/lib/storage/embeddings-openai.mjs +85 -0
  334. package/lib/storage/embeddings.mjs +92 -33
  335. package/lib/storage/file-lock.mjs +130 -0
  336. package/lib/storage/fusion.mjs +95 -0
  337. package/lib/storage/hybrid-query.mjs +34 -27
  338. package/lib/storage/migrations.mjs +187 -0
  339. package/lib/storage/postgres-backup.mjs +124 -0
  340. package/lib/storage/sql-store.mjs +5 -15
  341. package/lib/storage/state-source.mjs +12 -13
  342. package/lib/storage/store-version.mjs +115 -0
  343. package/lib/storage/sync.mjs +144 -35
  344. package/lib/storage/unified-storage.mjs +550 -0
  345. package/lib/storage/vector-client.mjs +286 -0
  346. package/lib/storage/vector-store.mjs +71 -30
  347. package/lib/task-graph/generate.mjs +135 -0
  348. package/lib/task-graph/schema.mjs +81 -0
  349. package/lib/task-graph/store.mjs +71 -0
  350. package/lib/telemetry/backends/local.mjs +62 -0
  351. package/lib/telemetry/backends/{langfuse.mjs → remote.mjs} +27 -14
  352. package/lib/telemetry/backfill.mjs +180 -0
  353. package/lib/telemetry/eval-datasets.mjs +203 -0
  354. package/lib/telemetry/{langfuse-ingest.mjs → ingest.mjs} +26 -20
  355. package/lib/telemetry/intent-verifications.mjs +86 -0
  356. package/lib/telemetry/llm-judge.mjs +350 -0
  357. package/lib/telemetry/model-pricing-catalog.mjs +557 -0
  358. package/lib/telemetry/setup.mjs +151 -0
  359. package/lib/telemetry/skill-calls.mjs +78 -0
  360. package/lib/telemetry/team-rollup.mjs +4 -4
  361. package/lib/token-engine.js +117 -0
  362. package/lib/token-estimator-anthropic.js +15 -0
  363. package/lib/token-estimator-deepseek.js +13 -0
  364. package/lib/token-estimator-default.js +13 -0
  365. package/lib/token-estimator-google.js +13 -0
  366. package/lib/token-estimator-openai.js +13 -0
  367. package/lib/toolkit-env.mjs +1 -1
  368. package/lib/tty-prompts.mjs +211 -0
  369. package/lib/uninstall/uninstall.mjs +423 -0
  370. package/lib/update.mjs +115 -0
  371. package/lib/upgrade.mjs +141 -0
  372. package/lib/validator.mjs +51 -2
  373. package/lib/validators/skills.mjs +142 -0
  374. package/lib/wireframe.mjs +422 -0
  375. package/lib/worker/entrypoint.mjs +241 -0
  376. package/lib/worker/evidence.mjs +107 -0
  377. package/lib/worker/run.mjs +154 -0
  378. package/lib/worker/trace.mjs +182 -0
  379. package/lib/workflow-state.mjs +14 -18
  380. package/package.json +21 -8
  381. package/personas/construct.md +53 -51
  382. package/platforms/claude/CLAUDE.md +1 -1
  383. package/platforms/claude/settings.template.json +115 -68
  384. package/platforms/opencode/config.template.json +3 -7
  385. package/rules/common/agents.md +11 -38
  386. package/rules/common/beads-hygiene.md +75 -0
  387. package/rules/common/code-review.md +11 -100
  388. package/rules/common/coding-style.md +5 -3
  389. package/rules/common/comments.md +30 -111
  390. package/rules/common/commit-approval.md +53 -0
  391. package/rules/common/cx-agent-routing.md +1 -0
  392. package/rules/common/development-workflow.md +23 -41
  393. package/rules/common/doc-ownership.md +54 -0
  394. package/rules/common/efficiency.md +57 -0
  395. package/rules/common/framing.md +76 -0
  396. package/rules/common/git-workflow.md +2 -4
  397. package/rules/common/patterns.md +3 -7
  398. package/rules/common/performance.md +15 -31
  399. package/rules/common/release-gates.md +69 -0
  400. package/rules/common/research.md +107 -0
  401. package/rules/common/security.md +6 -6
  402. package/rules/common/skill-composition.md +67 -0
  403. package/rules/common/testing.md +15 -27
  404. package/rules/golang/hooks.md +0 -4
  405. package/rules/policy/bootstrap.yaml +11 -0
  406. package/rules/policy/drive.yaml +8 -0
  407. package/rules/policy/task.yaml +9 -0
  408. package/rules/policy/workflow.yaml +9 -0
  409. package/rules/python/hooks.md +0 -4
  410. package/rules/swift/hooks.md +0 -4
  411. package/rules/typescript/hooks.md +0 -4
  412. package/rules/web/hooks.md +0 -2
  413. package/{sync-agents.mjs → scripts/sync-agents.mjs} +306 -61
  414. package/skills/ai/prompt-optimizer.md +7 -7
  415. package/skills/compliance/ai-disclosure.md +58 -0
  416. package/skills/compliance/data-privacy.md +46 -0
  417. package/skills/compliance/license-audit.md +40 -0
  418. package/skills/compliance/regulatory-review.md +61 -0
  419. package/skills/docs/document-ingest-workflow.md +52 -0
  420. package/skills/docs/evidence-ingest-workflow.md +9 -0
  421. package/skills/docs/init-docs.md +51 -18
  422. package/skills/docs/product-intelligence-workflow.md +12 -0
  423. package/skills/docs/product-signal-workflow.md +4 -0
  424. package/skills/docs/research-workflow.md +23 -8
  425. package/skills/docs/runbook-workflow.md +1 -1
  426. package/skills/operating/orchestration-reference.md +151 -0
  427. package/skills/roles/architect.md +5 -0
  428. package/skills/roles/engineer.md +32 -0
  429. package/skills/roles/operator.md +12 -0
  430. package/skills/roles/reviewer.md +23 -0
  431. package/skills/routing.md +1 -1
  432. package/templates/devcontainer/Dockerfile.devcontainer +38 -0
  433. package/templates/devcontainer/devcontainer.json +31 -0
  434. package/templates/distribution/bootstrap.ps1 +142 -0
  435. package/templates/distribution/bootstrap.sh +196 -0
  436. package/templates/distribution/run.mjs +187 -0
  437. package/templates/docs/adr.md +44 -8
  438. package/templates/docs/changelog-entry.md +43 -0
  439. package/templates/docs/construct_guide.md +149 -0
  440. package/templates/docs/evidence-brief.md +2 -2
  441. package/templates/docs/meta-prd.md +140 -19
  442. package/templates/docs/onboarding.md +57 -0
  443. package/templates/docs/prd.md +159 -15
  444. package/templates/docs/research-brief.md +4 -4
  445. package/templates/homebrew/construct.rb +67 -0
  446. package/langfuse/docker-compose.yml +0 -82
  447. package/lib/eval-harness.mjs +0 -59
  448. package/lib/hooks/bootstrap-guard.mjs +0 -90
  449. package/lib/hooks/console-warn.mjs +0 -43
  450. package/lib/hooks/continuation-enforcer.mjs +0 -72
  451. package/lib/hooks/drive-guard.mjs +0 -89
  452. package/lib/hooks/mcp-task-scope.mjs +0 -47
  453. package/lib/hooks/task-completed-guard.mjs +0 -43
  454. package/lib/hooks/teammate-idle-guard.mjs +0 -54
  455. package/lib/hooks/workflow-guard.mjs +0 -62
  456. package/lib/prompt-composer.mjs +0 -196
  457. package/lib/review.mjs +0 -429
  458. package/lib/server/static/app.js +0 -841
  459. package/lib/telemetry/langfuse-model-sync.mjs +0 -270
  460. package/rules/common/hooks.md +0 -35
  461. package/rules/zh/README.md +0 -113
  462. package/rules/zh/agents.md +0 -55
  463. package/rules/zh/code-review.md +0 -129
  464. package/rules/zh/coding-style.md +0 -53
  465. package/rules/zh/development-workflow.md +0 -49
  466. package/rules/zh/git-workflow.md +0 -29
  467. package/rules/zh/hooks.md +0 -35
  468. package/rules/zh/patterns.md +0 -36
  469. package/rules/zh/performance.md +0 -60
  470. package/rules/zh/security.md +0 -34
  471. package/rules/zh/testing.md +0 -34
@@ -0,0 +1,434 @@
1
+ /**
2
+ * lib/prompt-composer.js — Assemble the final agent prompt from core file, task packet, and context digest.
3
+ *
4
+ * Called by the orchestrator and MCP dispatch layer before invoking a specialist agent.
5
+ * Injects learned patterns from the observation store so agents improve over sessions.
6
+ * Produces a token-aware prompt structure with priority-based pruning.
7
+ *
8
+ * NEW contract (no backward compat):
9
+ * { metadata, fragments, system, messages, staticEndIndex, totalTokens }
10
+ *
11
+ * Fragment order: core → role-flavor → task-context → learned-patterns → task-packet → context-digest → host-constraints
12
+ * learned-patterns is inserted before the task packet so agents see what has been learned before reading the specific task.
13
+ */
14
+ import crypto from 'node:crypto';
15
+ import fs from 'node:fs';
16
+ import { homedir } from 'node:os';
17
+ import path from 'node:path';
18
+
19
+ import { buildContextDigest, readContextState } from './context-state.mjs';
20
+ import { resolveExecutionContractModelMetadata, selectModelTierForWorkCategory } from './model-router.mjs';
21
+ import { searchObservations } from './observation-store.mjs';
22
+ import { routeRequest } from './orchestration-policy.mjs';
23
+ import { resolvePromptEntry, resolvePromptMetadata } from './prompt-metadata.mjs';
24
+ import { readRoleFile } from './role-preload.mjs';
25
+ import { estimateTokens, estimatePromptTokens, estimateTokensSync } from './token-engine.js';
26
+
27
+ // Token limits (replacing char limits)
28
+ const LEARNED_PATTERNS_TOKEN_LIMIT = 200;
29
+ const MAX_OBSERVATIONS = 3; // matched to token budget
30
+ const MAX_CONTEXT_TOKENS = 3000; // total prompt token budget
31
+
32
+ // Priority tiers (1 = never drop, 5 = drop first)
33
+ const PRIORITY = {
34
+ 'core': 1,
35
+ 'task-packet': 1,
36
+ 'role-flavor': 2,
37
+ 'context-digest': 3,
38
+ 'learned-patterns': 4,
39
+ 'host-constraints': 5,
40
+ };
41
+
42
+ const AGENT_FLAVOR_MAP = {
43
+ architect: { classifierKey: 'architect', rolePrefix: 'architect' },
44
+ 'product-manager': { classifierKey: 'productManager', rolePrefix: 'product-manager' },
45
+ qa: { classifierKey: 'qa', rolePrefix: 'qa' },
46
+ security: { classifierKey: 'security', rolePrefix: 'security' },
47
+ 'data-analyst': { classifierKey: 'dataAnalyst', rolePrefix: 'data-analyst' },
48
+ 'data-engineer': { classifierKey: 'dataEngineer', rolePrefix: 'data-engineer' },
49
+ };
50
+
51
+ function compactTokens(text, tokenLimit = 300, { modelId = 'default' } = {}) {
52
+ if (!text) return '';
53
+ const normalized = String(text).trim();
54
+ const estimated = estimateTokensSync(normalized, { modelId });
55
+ if (estimated <= tokenLimit) return normalized;
56
+ // Approximate: drop ~25% and retry
57
+ const ratio = tokenLimit / estimated;
58
+ const cutIdx = Math.floor(normalized.length * ratio);
59
+ return `${normalized.slice(0, cutIdx)}…`;
60
+ }
61
+
62
+ function readPromptBody(promptFile, rootDir) {
63
+ const filePath = path.join(rootDir, promptFile);
64
+ if (!fs.existsSync(filePath)) return '';
65
+ return fs.readFileSync(filePath, 'utf8').trim();
66
+ }
67
+
68
+ function buildLearnedPatternsBlock(agentName, { intent = null, workCategory = null, project = null, modelId = 'default' } = {}) {
69
+ try {
70
+ const rootDir = homedir();
71
+ const query = [agentName, intent, workCategory].filter(Boolean).join(' ');
72
+ const shortName = String(agentName).replace(/^cx-/, '');
73
+ const results = searchObservations(rootDir, query, {
74
+ limit: MAX_OBSERVATIONS * 2,
75
+ project: project ?? null,
76
+ });
77
+
78
+ const relevant = results
79
+ .filter((o) => (o.confidence ?? 0) >= 0.7)
80
+ .filter((o) => ['pattern', 'anti-pattern', 'decision', 'insight'].includes(o.category))
81
+ .sort((a, b) => {
82
+ const aMatch = a.role === agentName || a.role === shortName || a.role === 'construct' ? 0 : 1;
83
+ const bMatch = b.role === agentName || b.role === shortName || b.role === 'construct' ? 0 : 1;
84
+ return aMatch - bMatch;
85
+ })
86
+ .slice(0, MAX_OBSERVATIONS);
87
+
88
+ if (!relevant.length) return { text: '', tokens: 0 };
89
+
90
+ const lines = ['## Learned patterns (from prior sessions)', ''];
91
+ let tokens = 0;
92
+ for (const obs of relevant) {
93
+ const prefix = obs.category === 'anti-pattern' ? '⚠ ' : obs.category === 'decision' ? '✓ ' : '• ';
94
+ const line = `${prefix}${obs.summary}`;
95
+ const lineTokens = estimateTokensSync(line + '\n', { modelId });
96
+ if (tokens + lineTokens > LEARNED_PATTERNS_TOKEN_LIMIT) break;
97
+ lines.push(line);
98
+ tokens += lineTokens + 1;
99
+ }
100
+ lines.push('');
101
+ return { text: lines.join('\n'), tokens };
102
+ } catch {
103
+ return { text: '', tokens: 0 };
104
+ }
105
+ }
106
+
107
+ export function resolveBasePrompt(entryOrName, {
108
+ rootDir = process.cwd(),
109
+ registry,
110
+ fallback = '',
111
+ } = {}) {
112
+ return resolvePromptContract(entryOrName, { rootDir, registry, fallback }).prompt;
113
+ }
114
+
115
+ export function resolvePromptContract(entryOrName, {
116
+ rootDir = process.cwd(),
117
+ registry,
118
+ fallback = '',
119
+ } = {}) {
120
+ const directEntry = entryOrName && typeof entryOrName === 'object' ? entryOrName : null;
121
+ if (directEntry?.prompt) {
122
+ return {
123
+ prompt: String(directEntry.prompt).trim(),
124
+ metadata: resolvePromptMetadata(directEntry.name || entryOrName, { rootDir, registry }),
125
+ };
126
+ }
127
+
128
+ const agentName = directEntry?.name || entryOrName;
129
+ if (!agentName) return { prompt: fallback, metadata: {} };
130
+
131
+ const composed = composePrompt(agentName, { rootDir, registry });
132
+ return {
133
+ prompt: composed.system || fallback,
134
+ metadata: composed.metadata || {},
135
+ };
136
+ }
137
+
138
+ export function composePrompt(agentName, {
139
+ rootDir = process.cwd(),
140
+ registry,
141
+ task = null,
142
+ contextState = null,
143
+ hostConstraints = null,
144
+ intent = null,
145
+ workCategory = null,
146
+ roleFlavors = null,
147
+ project = null,
148
+ injectLearnedPatterns = true,
149
+ modelId = 'default',
150
+ } = {}) {
151
+ const entry = resolvePromptEntry(agentName, { rootDir, registry });
152
+ if (!entry?.promptFile) return { metadata: {}, fragments: [], system: '', messages: [], staticEndIndex: -1, totalTokens: 0 };
153
+
154
+ const metadata = resolvePromptMetadata(agentName, { rootDir, registry });
155
+ const fragments = [];
156
+
157
+ // 1. Core (priority 1 — never dropped)
158
+ fragments.push({ type: 'core', priority: PRIORITY['core'], label: entry.name, content: readPromptBody(entry.promptFile, rootDir), tokenBudget: null });
159
+
160
+ // 2. Role flavor overlay (priority 2)
161
+ const shortName = String(agentName).replace(/^cx-/, '');
162
+ const flavorMapping = AGENT_FLAVOR_MAP[shortName];
163
+ if (flavorMapping && roleFlavors) {
164
+ const flavor = roleFlavors[flavorMapping.classifierKey];
165
+ if (flavor) {
166
+ const overlayBody = readRoleFile(rootDir, `${flavorMapping.rolePrefix}.${flavor}`, {
167
+ source: 'prompt-composer',
168
+ callerContext: agentName,
169
+ });
170
+ if (overlayBody) {
171
+ fragments.push({
172
+ type: 'role-flavor',
173
+ priority: PRIORITY['role-flavor'],
174
+ label: `${flavorMapping.rolePrefix}.${flavor}`,
175
+ content: `### ${flavor} domain guidance\n\n${overlayBody}`,
176
+ tokenBudget: null,
177
+ });
178
+ }
179
+ }
180
+ }
181
+
182
+ // 3. Task context (priority varies, part of static if present)
183
+ if (intent || workCategory) {
184
+ const text = `Intent: ${intent || 'unknown'}\nWork category: ${workCategory || 'unknown'}`;
185
+ fragments.push({
186
+ type: 'task-context',
187
+ priority: 2,
188
+ label: 'task-classification',
189
+ content: text,
190
+ tokenBudget: 50,
191
+ });
192
+ }
193
+
194
+ // 4. Learned patterns (priority 4)
195
+ if (injectLearnedPatterns) {
196
+ const { text: learnedBlock, tokens: learnedTokens } = buildLearnedPatternsBlock(agentName, {
197
+ intent, workCategory, project, modelId,
198
+ });
199
+ if (learnedBlock) {
200
+ fragments.push({
201
+ type: 'learned-patterns',
202
+ priority: PRIORITY['learned-patterns'],
203
+ label: 'observations',
204
+ content: learnedBlock,
205
+ tokenBudget: LEARNED_PATTERNS_TOKEN_LIMIT,
206
+ estimatedTokens: learnedTokens,
207
+ });
208
+ }
209
+ }
210
+
211
+ // 5. Task packet (priority 1 — never dropped)
212
+ if (task) {
213
+ const taskBlock = [
214
+ task.title ? `Task: ${task.title}` : null,
215
+ task.owner ? `Owner: ${task.owner}` : null,
216
+ Array.isArray(task.acceptanceCriteria) && task.acceptanceCriteria.length
217
+ ? `Acceptance: ${task.acceptanceCriteria.join('; ')}`
218
+ : null,
219
+ Array.isArray(task.readFirst) && task.readFirst.length
220
+ ? `Read first: ${task.readFirst.join(', ')}`
221
+ : null,
222
+ Array.isArray(task.doNotChange) && task.doNotChange.length
223
+ ? `Do not change: ${task.doNotChange.join(', ')}`
224
+ : null,
225
+ ].filter(Boolean).join('\n');
226
+
227
+ if (taskBlock) {
228
+ fragments.push({
229
+ type: 'task-packet',
230
+ priority: PRIORITY['task-packet'],
231
+ label: 'workflow-task',
232
+ content: compactTokens(taskBlock, 150, { modelId }),
233
+ tokenBudget: 150,
234
+ });
235
+ }
236
+ }
237
+
238
+ // 6. Context digest (priority 3)
239
+ const digest = buildContextDigest(contextState);
240
+ if (digest) {
241
+ const digestStr = JSON.stringify(digest);
242
+ fragments.push({
243
+ type: 'context-digest',
244
+ priority: PRIORITY['context-digest'],
245
+ label: 'context',
246
+ content: compactTokens(digestStr, 200, { modelId }),
247
+ tokenBudget: 200,
248
+ });
249
+ }
250
+
251
+ // 7. Host constraints (priority 5 — dropped first)
252
+ if (hostConstraints) {
253
+ fragments.push({
254
+ type: 'host-constraints',
255
+ priority: PRIORITY['host-constraints'],
256
+ label: 'host',
257
+ content: compactTokens(JSON.stringify(hostConstraints), 75, { modelId }),
258
+ tokenBudget: 75,
259
+ });
260
+ }
261
+
262
+ // Priority-based pruning
263
+ const pruned = pruneFragments(fragments, MAX_CONTEXT_TOKENS, modelId);
264
+ const staticEndIndex = findStaticEndIndex(pruned);
265
+
266
+ // Assemble outputs
267
+ const system = assembleSystemMessage(pruned, staticEndIndex);
268
+ const messages = assembleMessages(pruned, staticEndIndex);
269
+ const totalTokens = estimateTokensSync(system || '', { modelId }) +
270
+ (messages || []).reduce((sum, msg) => sum + estimateTokensSync(typeof msg?.content === 'string' ? msg.content : JSON.stringify(msg.content || ''), { modelId }), 0) +
271
+ (pruned || []).reduce((sum, f) => sum + estimateTokensSync(f.content || '', { modelId }), 0);
272
+
273
+ return { metadata, fragments: pruned, system, messages, staticEndIndex, totalTokens };
274
+ }
275
+
276
+ function pruneFragments(fragments, tokenBudget, modelId) {
277
+ // Calculate current token usage
278
+ let currentTokens = 0;
279
+ const estimated = fragments.map((f) => ({
280
+ ...f,
281
+ estimatedTokens: f.estimatedTokens ?? estimateTokensSync(f.content || '', { modelId }),
282
+ }));
283
+
284
+ for (const f of estimated) {
285
+ currentTokens += f.estimatedTokens;
286
+ }
287
+
288
+ if (currentTokens <= tokenBudget) return fragments;
289
+
290
+ // Drop lowest priority fragments first
291
+ const sorted = [...estimated].sort((a, b) => a.priority - b.priority);
292
+
293
+ const kept = [];
294
+ let remainingBudget = tokenBudget;
295
+
296
+ // Always keep priority 1
297
+ for (const f of sorted) {
298
+ if (f.priority === 1) {
299
+ kept.push(f);
300
+ remainingBudget -= f.estimatedTokens;
301
+ }
302
+ }
303
+
304
+ // Then add higher priorities if budget allows
305
+ for (const f of sorted) {
306
+ if (f.priority === 1) continue; // already added
307
+ if (f.estimatedTokens <= remainingBudget) {
308
+ kept.push(f);
309
+ remainingBudget -= f.estimatedTokens;
310
+ }
311
+ }
312
+
313
+ return kept.sort((a, b) => fragments.indexOf(a) - fragments.indexOf(b)); // preserve original order
314
+ }
315
+
316
+ function findStaticEndIndex(fragments) {
317
+ // Find the last fragment that is "static" (cacheable)
318
+ // Priority 1-2 are static, 3+ are dynamic
319
+ let lastStatic = -1;
320
+ for (let i = 0; i < fragments.length; i++) {
321
+ if (fragments[i].priority <= 2) {
322
+ lastStatic = i;
323
+ }
324
+ }
325
+ return lastStatic;
326
+ }
327
+
328
+ function assembleSystemMessage(fragments, staticEndIndex) {
329
+ const staticFragments = staticEndIndex >= 0 ? fragments.slice(0, staticEndIndex + 1) : [];
330
+ const parts = staticFragments.map((f) => f.content).filter(Boolean);
331
+ return parts.join('\n\n');
332
+ }
333
+
334
+ function assembleMessages(fragments, staticEndIndex) {
335
+ const dynamicFragments = staticEndIndex >= 0 ? fragments.slice(staticEndIndex + 1) : fragments;
336
+ const systemContent = staticEndIndex >= 0
337
+ ? assembleSystemMessage(fragments, staticEndIndex)
338
+ : '';
339
+
340
+ const messages = [];
341
+ if (systemContent) {
342
+ messages.push({ role: 'system', content: systemContent });
343
+ }
344
+
345
+ const dynamicContent = dynamicFragments.map((f) => f.content).filter(Boolean).join('\n\n');
346
+ if (dynamicContent) {
347
+ messages.push({ role: 'user', content: dynamicContent });
348
+ }
349
+
350
+ return messages;
351
+ }
352
+
353
+ export function summarizePromptComposition(agentName, options = {}) {
354
+ const route = options.route || (options.request ? routeRequest({ request: options.request }) : null);
355
+ const executionContractModel = options.executionContractModel
356
+ || resolveExecutionContractModelMetadata({
357
+ envValues: options.envValues || {},
358
+ registryModels: options.registryModels || {},
359
+ requestedTier: options.requestedTier || selectModelTierForWorkCategory(route?.workCategory),
360
+ workCategory: route?.workCategory || null,
361
+ });
362
+ const composed = composePrompt(agentName, {
363
+ ...options,
364
+ intent: options.intent || route?.intent || null,
365
+ workCategory: options.workCategory || route?.workCategory || null,
366
+ roleFlavors: options.roleFlavors || route?.roleFlavors || null,
367
+ });
368
+ const fragmentTypes = composed.fragments.map((fragment) => fragment.type);
369
+ const composedPromptHash = composed.system
370
+ ? crypto.createHash('sha256').update(composed.system).digest('hex')
371
+ : null;
372
+ const task = options.task || null;
373
+
374
+ return {
375
+ ...composed.metadata,
376
+ ...(composedPromptHash ? {
377
+ composedPromptHash,
378
+ composedPromptVersion: composedPromptHash.slice(0, 12),
379
+ } : {}),
380
+ promptFragmentCount: composed.fragments.length,
381
+ promptFragmentTypes: fragmentTypes,
382
+ promptHasTaskPacket: fragmentTypes.includes('task-packet'),
383
+ promptHasContextDigest: fragmentTypes.includes('context-digest'),
384
+ promptHasHostConstraints: fragmentTypes.includes('host-constraints'),
385
+ ...(task?.key ? { taskPacketKey: task.key } : {}),
386
+ ...(task?.owner ? { taskPacketOwner: task.owner } : {}),
387
+ ...(task?.phase ? { taskPacketPhase: task.phase } : {}),
388
+ ...(route ? {
389
+ routeIntent: route.intent,
390
+ routeTrack: route.track,
391
+ routeWorkCategory: route.workCategory,
392
+ routeSpecialists: route.specialists,
393
+ routeDispatchPlan: route.dispatchPlan,
394
+ ...(route.roleFlavors ? { routeRoleFlavors: route.roleFlavors } : {}),
395
+ } : {}),
396
+ promptHasRoleFlavor: fragmentTypes.includes('role-flavor'),
397
+ promptHasLearnedPatterns: fragmentTypes.includes('learned-patterns'),
398
+ executionContractModel,
399
+ totalTokens: composed.totalTokens,
400
+ staticEndIndex: composed.staticEndIndex,
401
+ };
402
+ }
403
+
404
+ export function resolveRuntimePromptMetadata(agentName, {
405
+ rootDir = process.cwd(),
406
+ registry,
407
+ task = null,
408
+ contextState = null,
409
+ request = null,
410
+ route = null,
411
+ registryModels = {},
412
+ envValues = {},
413
+ executionContractModel = null,
414
+ hostConstraints = null,
415
+ } = {}) {
416
+ const resolvedTask = task ?? null;
417
+ const resolvedContextState = contextState ?? readContextState(rootDir);
418
+ const resolvedRoute = route || (request ? routeRequest({ request }) : null);
419
+
420
+ return summarizePromptComposition(agentName, {
421
+ rootDir,
422
+ registry,
423
+ task: resolvedTask,
424
+ contextState: resolvedContextState,
425
+ request,
426
+ route: resolvedRoute,
427
+ registryModels,
428
+ envValues,
429
+ executionContractModel,
430
+ hostConstraints,
431
+ intent: resolvedRoute?.intent || null,
432
+ workCategory: resolvedRoute?.workCategory || null,
433
+ });
434
+ }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Resolves an agent or persona name to its git-owned prompt file and returns a
5
5
  * stable hash/version fingerprint. Telemetry should send these identifiers to
6
- * Langfuse instead of embedding full production prompt text in every trace.
6
+ * the telemetry backend instead of embedding full production prompt text in every trace.
7
7
  */
8
8
  import crypto from 'node:crypto';
9
9
  import fs from 'node:fs';
@@ -0,0 +1,44 @@
1
+ /**
2
+ * lib/provider-capabilities-anthropic.js — Anthropic provider capabilities.
3
+ *
4
+ * Anthropic supports cache_control annotations on system messages and tool definitions.
5
+ * Header required: anthropic-version: 2024-10-22
6
+ * Cache TTL: 5m and 1h (ephemeral variants)
7
+ */
8
+ import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
9
+
10
+ export function anthropicCapabilities(modelId = '') {
11
+ // Resolve context window based on model
12
+ const contextWindow = resolveContextWindow(modelId);
13
+
14
+ return {
15
+ cacheControl: true,
16
+ cacheMechanism: 'annotation',
17
+ cacheTTL: { '5m': 300_000, '1h': 1_200_000 }, // tokens
18
+ structuredOutput: true,
19
+ maxContextWindow: contextWindow,
20
+ tokenRatio: 3.5, // ~3.5 chars per token for Claude
21
+ annotationFormat: 'anthropic',
22
+ annotationHeaders: {
23
+ 'anthropic-version': '2024-10-22'
24
+ },
25
+ cacheAnchoring: 'system-message', // where to place the cache_control
26
+ breakpointPlacement: 'after-static', // place breakpoint after static content
27
+ };
28
+ }
29
+
30
+ function resolveContextWindow(modelId) {
31
+ const id = String(modelId || '').toLowerCase();
32
+ // Claude 3.5/4.x models
33
+ if (id.includes('opus-4')) return 200_000;
34
+ if (id.includes('sonnet-4')) return 200_000;
35
+ if (id.includes('haiku-4')) return 200_000;
36
+ if (id.includes('3-5-sonnet')) return 200_000;
37
+ if (id.includes('3-5-haiku')) return 200_000;
38
+ // Default for modern Claude
39
+ return 200_000;
40
+ }
41
+
42
+ export async function capabilities(modelId) {
43
+ return anthropicCapabilities(modelId);
44
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * lib/provider-capabilities-deepseek.js — DeepSeek provider capabilities.
3
+ *
4
+ * DeepSeek does not support prompt caching via standard APIs.
5
+ * Uses ~3 chars per token (similar to Claude).
6
+ * Max context: 64k for DeepSeek-V3, 32k for older models.
7
+ */
8
+ import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
9
+
10
+ export function deepseekCapabilities(modelId = '') {
11
+ const contextWindow = resolveDeepSeekContextWindow(modelId);
12
+
13
+ return {
14
+ cacheControl: false,
15
+ cacheMechanism: 'none',
16
+ cacheTTL: null,
17
+ structuredOutput: false,
18
+ maxContextWindow: contextWindow,
19
+ tokenRatio: 3, // ~3 chars per token for DeepSeek
20
+ annotationFormat: 'none',
21
+ annotationHeaders: null,
22
+ cacheAnchoring: 'none',
23
+ notes: 'DeepSeek has no prompt caching support via standard APIs',
24
+ };
25
+ }
26
+
27
+ function resolveDeepSeekContextWindow(modelId) {
28
+ const id = String(modelId || '').toLowerCase();
29
+ if (id.includes('deepseek-v3') || id.includes('deepseek-chat')) return 64_000;
30
+ if (id.includes('deepseek-coder')) return 16_000;
31
+ if (id.includes('deepseek-r1')) return 64_000;
32
+ return 64_000; // default
33
+ }
34
+
35
+ export async function capabilities(modelId) {
36
+ return deepseekCapabilities(modelId);
37
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * lib/provider-capabilities-generic.js — Generic/fallback provider capabilities.
3
+ *
4
+ * Used when provider is unknown or not explicitly supported.
5
+ * Provides safe defaults.
6
+ */
7
+ import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
8
+
9
+ export function genericCapabilities(modelId = '') {
10
+ return {
11
+ cacheControl: false,
12
+ cacheMechanism: 'none',
13
+ cacheTTL: null,
14
+ structuredOutput: false,
15
+ maxContextWindow: 200_000, // conservative default
16
+ tokenRatio: 4, // ~4 chars per token (conservative)
17
+ annotationFormat: 'none',
18
+ annotationHeaders: null,
19
+ cacheAnchoring: 'none',
20
+ notes: 'Generic provider — no special capabilities detected',
21
+ };
22
+ }
23
+
24
+ export async function capabilities(modelId) {
25
+ return genericCapabilities(modelId);
26
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * lib/provider-capabilities-google.js — Google Gemini provider capabilities.
3
+ *
4
+ * Gemini uses cachedContent API (resource-based caching, not annotations).
5
+ * Cache TTL: 1h via cachedContent resource.
6
+ * Max context window: 1M for Gemini 1.5+, 30k for older models.
7
+ */
8
+ import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
9
+
10
+ export function googleCapabilities(modelId = '') {
11
+ const contextWindow = resolveGoogleContextWindow(modelId);
12
+
13
+ return {
14
+ cacheControl: true,
15
+ cacheMechanism: 'resource', // uses cachedContent API
16
+ cacheTTL: { '5m': null, '1h': 1_200_000 }, // Gemini uses 1h TTL
17
+ structuredOutput: true,
18
+ maxContextWindow: contextWindow,
19
+ tokenRatio: 4, // ~4 chars per token for Gemini
20
+ annotationFormat: 'google',
21
+ annotationHeaders: null, // Gemini uses separate API endpoint
22
+ cachedContentEndpoint: 'https://generativelanguage.googleapis.com/v1',
23
+ cacheAnchoring: 'cached-content', // reference by name
24
+ };
25
+ }
26
+
27
+ function resolveGoogleContextWindow(modelId) {
28
+ const id = String(modelId || '').toLowerCase();
29
+ // Gemini 1.5+ models
30
+ if (id.includes('gemini-1.5-pro')) return 1_000_000;
31
+ if (id.includes('gemini-1.5-flash')) return 1_000_000;
32
+ if (id.includes('gemini-2.0-pro')) return 1_000_000;
33
+ if (id.includes('gemini-2.0-flash')) return 1_000_000;
34
+ if (id.includes('gemini-2.5-pro')) return 1_000_000;
35
+ if (id.includes('gemini-2.5-flash')) return 1_000_000;
36
+ // Older models
37
+ if (id.includes('gemini-pro')) return 30_000;
38
+ if (id.includes('gemini-flash')) return 30_000;
39
+ // Gemma (smaller context)
40
+ if (id.includes('gemma')) return 8_000;
41
+ // Default
42
+ return 1_000_000;
43
+ }
44
+
45
+ export async function capabilities(modelId) {
46
+ return googleCapabilities(modelId);
47
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * lib/provider-capabilities-openai.js — OpenAI provider capabilities.
3
+ *
4
+ * OpenAI uses automatic prefix caching (no annotations needed).
5
+ * Cache is invisible — triggered by stable prefixes in the prompt.
6
+ * Structured output via response_format parameter.
7
+ * Max context: 128k for GPT-4o, 8k for older models.
8
+ */
9
+ import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
10
+
11
+ export function openaiCapabilities(modelId = '') {
12
+ const contextWindow = resolveOpenAIContextWindow(modelId);
13
+
14
+ return {
15
+ cacheControl: false, // automatic — no annotations
16
+ cacheMechanism: 'automatic',
17
+ cacheTTL: null, // invisible to caller
18
+ structuredOutput: true,
19
+ maxContextWindow: contextWindow,
20
+ tokenRatio: 4, // ~4 chars per token for GPT models
21
+ annotationFormat: 'openai',
22
+ annotationHeaders: null,
23
+ cacheAnchoring: 'prefix', // stable prefix caching
24
+ notes: 'OpenAI caches repeated prefixes automatically; no explicit cache_control needed',
25
+ };
26
+ }
27
+
28
+ function resolveOpenAIContextWindow(modelId) {
29
+ const id = String(modelId || '').toLowerCase();
30
+ // GPT-4o series
31
+ if (id.includes('gpt-4o') || id.includes('gpt-4.1')) return 128_000;
32
+ if (id.includes('gpt-4-turbo')) return 128_000;
33
+ if (id.includes('gpt-4')) return 8_000;
34
+ if (id.includes('gpt-3.5')) return 16_000;
35
+ if (id.includes('gpt-3')) return 4_000;
36
+ // o1 series
37
+ if (id.includes('o1')) return 200_000;
38
+ if (id.includes('o3')) return 200_000;
39
+ // Default
40
+ return 128_000;
41
+ }
42
+
43
+ export async function capabilities(modelId) {
44
+ return openaiCapabilities(modelId);
45
+ }