@webpresso/agent-kit 3.1.30 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/README.md +53 -15
  2. package/bin/_run.js +183 -20
  3. package/catalog/AGENTS.md.tpl +3 -3
  4. package/catalog/agent/agents/implementer.md +45 -0
  5. package/catalog/agent/rules/parallel-orchestration-quota.md +100 -0
  6. package/catalog/agent/rules/pre-implementation.md +28 -1
  7. package/catalog/agent/rules/supported-agent-clis.md +9 -0
  8. package/catalog/agent/rules/typescript-version.md +50 -0
  9. package/catalog/agent/skills/codex/SKILL.md +25 -1
  10. package/catalog/agent/skills/verify/SKILL.md +4 -2
  11. package/catalog/docs/templates/blueprint.md +1 -1
  12. package/dist/esm/audit/agents.js +1 -0
  13. package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +12 -0
  14. package/dist/esm/audit/blueprint-lifecycle-sql.js +23 -2
  15. package/dist/esm/audit/blueprint-pr-coverage.js +4 -0
  16. package/dist/esm/audit/blueprint-trust.js +14 -10
  17. package/dist/esm/audit/changeset-evidence.d.ts +23 -0
  18. package/dist/esm/audit/changeset-evidence.js +30 -0
  19. package/dist/esm/audit/changeset-required.d.ts +10 -0
  20. package/dist/esm/audit/changeset-required.js +73 -0
  21. package/dist/esm/audit/ci-guardrails-detection.d.ts +27 -0
  22. package/dist/esm/audit/ci-guardrails-detection.js +68 -0
  23. package/dist/esm/audit/ci-guardrails-wiring.d.ts +22 -0
  24. package/dist/esm/audit/ci-guardrails-wiring.js +80 -0
  25. package/dist/esm/audit/ci-test-perf.js +30 -0
  26. package/dist/esm/audit/command-surface.js +4 -2
  27. package/dist/esm/audit/host-substitution-risk.d.ts +2 -0
  28. package/dist/esm/audit/host-substitution-risk.js +256 -0
  29. package/dist/esm/audit/registry.d.ts +4 -4
  30. package/dist/esm/audit/registry.js +13 -1
  31. package/dist/esm/audit/toolchain-isolation.js +1 -2
  32. package/dist/esm/audit/typescript-version.d.ts +2 -0
  33. package/dist/esm/audit/typescript-version.js +123 -0
  34. package/dist/esm/blueprint/core/parser.js +101 -2
  35. package/dist/esm/blueprint/core/schema.d.ts +1 -0
  36. package/dist/esm/blueprint/core/schema.js +26 -0
  37. package/dist/esm/blueprint/core/validation/criteria.d.ts +14 -0
  38. package/dist/esm/blueprint/core/validation/criteria.js +41 -2
  39. package/dist/esm/blueprint/core/validation/state.js +25 -14
  40. package/dist/esm/blueprint/db/migrations/run.js +4 -2
  41. package/dist/esm/blueprint/lifecycle/audit.d.ts +20 -0
  42. package/dist/esm/blueprint/lifecycle/audit.js +87 -20
  43. package/dist/esm/blueprint/lifecycle/canonicalize.d.ts +6 -0
  44. package/dist/esm/blueprint/lifecycle/canonicalize.js +135 -0
  45. package/dist/esm/blueprint/lifecycle/local.d.ts +2 -1
  46. package/dist/esm/blueprint/lifecycle/local.js +19 -3
  47. package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
  48. package/dist/esm/blueprint/lifecycle/review-provenance.js +68 -12
  49. package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +97 -0
  50. package/dist/esm/blueprint/lifecycle/rollout-anchor.js +161 -0
  51. package/dist/esm/blueprint/service/BlueprintCreationService.d.ts +1 -0
  52. package/dist/esm/blueprint/service/BlueprintCreationService.js +53 -4
  53. package/dist/esm/blueprint/service/blueprint-tech-debt-links.js +16 -2
  54. package/dist/esm/blueprint/sync/client.d.ts +2 -1
  55. package/dist/esm/blueprint/sync/client.js +1 -1
  56. package/dist/esm/blueprint/trust/command-runner.d.ts +26 -0
  57. package/dist/esm/blueprint/trust/command-runner.js +148 -0
  58. package/dist/esm/blueprint/trust/dossier.d.ts +1 -0
  59. package/dist/esm/blueprint/trust/dossier.js +40 -6
  60. package/dist/esm/blueprint/trust/gates.d.ts +7 -0
  61. package/dist/esm/blueprint/trust/gates.js +20 -0
  62. package/dist/esm/blueprint/trust/promotion.d.ts +8 -0
  63. package/dist/esm/blueprint/trust/promotion.js +74 -112
  64. package/dist/esm/blueprint/trust/scaffold.d.ts +19 -0
  65. package/dist/esm/blueprint/trust/scaffold.js +64 -0
  66. package/dist/esm/blueprint/trust/validator.d.ts +1 -0
  67. package/dist/esm/blueprint/trust/validator.js +7 -1
  68. package/dist/esm/blueprint/utils/archive.js +27 -5
  69. package/dist/esm/blueprint/utils/document-paths.d.ts +18 -0
  70. package/dist/esm/blueprint/utils/document-paths.js +45 -12
  71. package/dist/esm/build/cli-mcp-parity.js +15 -0
  72. package/dist/esm/cli/bundle/commands/blueprint.d.ts +1 -0
  73. package/dist/esm/cli/bundle/commands/blueprint.js +1 -0
  74. package/dist/esm/cli/cli.d.ts +1 -1
  75. package/dist/esm/cli/cli.js +43 -0
  76. package/dist/esm/cli/commands/agent-launch.d.ts +8 -1
  77. package/dist/esm/cli/commands/agent-launch.js +93 -4
  78. package/dist/esm/cli/commands/audit-core.d.ts +1 -1
  79. package/dist/esm/cli/commands/audit-core.js +8 -1
  80. package/dist/esm/cli/commands/audit.js +21 -5
  81. package/dist/esm/cli/commands/blueprint/execution.js +18 -6
  82. package/dist/esm/cli/commands/blueprint/mutations.js +59 -40
  83. package/dist/esm/cli/commands/blueprint/router-dispatch.d.ts +2 -1
  84. package/dist/esm/cli/commands/blueprint/router-dispatch.js +20 -1
  85. package/dist/esm/cli/commands/blueprint/router-output.d.ts +21 -0
  86. package/dist/esm/cli/commands/blueprint/router-output.js +48 -17
  87. package/dist/esm/cli/commands/blueprint/router.d.ts +7 -0
  88. package/dist/esm/cli/commands/blueprint/router.js +13 -1
  89. package/dist/esm/cli/commands/ci-preflight.d.ts +13 -2
  90. package/dist/esm/cli/commands/ci-preflight.js +30 -6
  91. package/dist/esm/cli/commands/compile.js +5 -31
  92. package/dist/esm/cli/commands/dash/delivery-status.d.ts +1 -0
  93. package/dist/esm/cli/commands/dash/delivery-status.js +11 -4
  94. package/dist/esm/cli/commands/doctor.js +5 -0
  95. package/dist/esm/cli/commands/hook.d.ts +5 -0
  96. package/dist/esm/cli/commands/hook.js +15 -9
  97. package/dist/esm/cli/commands/init/config.js +2 -2
  98. package/dist/esm/cli/commands/init/convergence-apply.d.ts +88 -0
  99. package/dist/esm/cli/commands/init/convergence-apply.js +325 -0
  100. package/dist/esm/cli/commands/init/convergence-state.d.ts +13 -0
  101. package/dist/esm/cli/commands/init/convergence-state.js +54 -0
  102. package/dist/esm/cli/commands/init/convergence.d.ts +23 -0
  103. package/dist/esm/cli/commands/init/convergence.js +57 -0
  104. package/dist/esm/cli/commands/init/git-convergence.d.ts +19 -0
  105. package/dist/esm/cli/commands/init/git-convergence.js +168 -0
  106. package/dist/esm/cli/commands/init/gitignore-patcher.js +2 -0
  107. package/dist/esm/cli/commands/init/grok-privacy-smoke.d.ts +52 -0
  108. package/dist/esm/cli/commands/init/grok-privacy-smoke.js +91 -0
  109. package/dist/esm/cli/commands/init/host-native-proof.d.ts +18 -0
  110. package/dist/esm/cli/commands/init/host-native-proof.js +88 -0
  111. package/dist/esm/cli/commands/init/host-visibility.d.ts +2 -2
  112. package/dist/esm/cli/commands/init/host-visibility.js +94 -5
  113. package/dist/esm/cli/commands/init/index.d.ts +3 -0
  114. package/dist/esm/cli/commands/init/index.js +326 -173
  115. package/dist/esm/cli/commands/init/mcp-spec.d.ts +57 -0
  116. package/dist/esm/cli/commands/init/mcp-spec.js +273 -0
  117. package/dist/esm/cli/commands/init/merge.d.ts +1 -0
  118. package/dist/esm/cli/commands/init/merge.js +30 -1
  119. package/dist/esm/cli/commands/init/package-root.d.ts +7 -0
  120. package/dist/esm/cli/commands/init/package-root.js +10 -2
  121. package/dist/esm/cli/commands/init/plugin-cache-prune.d.ts +0 -3
  122. package/dist/esm/cli/commands/init/plugin-cache-prune.js +0 -1
  123. package/dist/esm/cli/commands/init/scaffold-agents-md.d.ts +1 -1
  124. package/dist/esm/cli/commands/init/scaffold-agents-md.js +9 -3
  125. package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +20 -0
  126. package/dist/esm/cli/commands/init/scaffold-base-kit.js +61 -5
  127. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.d.ts +14 -8
  128. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.js +41 -24
  129. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +2 -0
  130. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +54 -24
  131. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +12 -31
  132. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +288 -202
  133. package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +22 -0
  134. package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +230 -0
  135. package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.d.ts +38 -0
  136. package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.js +73 -0
  137. package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.d.ts +21 -0
  138. package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.js +559 -63
  139. package/dist/esm/cli/commands/init/untracked-collision.d.ts +14 -0
  140. package/dist/esm/cli/commands/init/untracked-collision.js +42 -0
  141. package/dist/esm/cli/commands/opencode-probe.d.ts +107 -0
  142. package/dist/esm/cli/commands/opencode-probe.js +323 -0
  143. package/dist/esm/cli/commands/opencode-rotate.d.ts +72 -0
  144. package/dist/esm/cli/commands/opencode-rotate.js +147 -0
  145. package/dist/esm/cli/commands/public-readiness.d.ts +11 -0
  146. package/dist/esm/cli/commands/public-readiness.js +42 -0
  147. package/dist/esm/cli/commands/quality-runner.js +3 -3
  148. package/dist/esm/cli/commands/review.d.ts +35 -3
  149. package/dist/esm/cli/commands/review.js +751 -209
  150. package/dist/esm/cli/commands/run-wait.d.ts +2 -0
  151. package/dist/esm/cli/commands/run-wait.js +122 -0
  152. package/dist/esm/cli/commands/rust-check.d.ts +24 -0
  153. package/dist/esm/cli/commands/rust-check.js +110 -0
  154. package/dist/esm/cli/commands/setup-help.js +1 -1
  155. package/dist/esm/cli/commands/setup-repair-help.js +1 -1
  156. package/dist/esm/cli/commands/typecheck.d.ts +1 -0
  157. package/dist/esm/cli/commands/typecheck.js +30 -1
  158. package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +6 -0
  159. package/dist/esm/cli/commands/worktree/router-dispatch.js +126 -9
  160. package/dist/esm/cli/commands/worktree/router.js +10 -0
  161. package/dist/esm/cli/direct-provider-launch.js +9 -5
  162. package/dist/esm/cli/optional-tool-freshness.d.ts +27 -0
  163. package/dist/esm/cli/optional-tool-freshness.js +219 -9
  164. package/dist/esm/daemon/domains/git-actions.d.ts +29 -0
  165. package/dist/esm/daemon/domains/git-actions.js +136 -0
  166. package/dist/esm/daemon/domains/run-registry-interop.d.ts +16 -0
  167. package/dist/esm/daemon/domains/run-registry-interop.js +45 -0
  168. package/dist/esm/daemon/domains/sessions-search.d.ts +11 -0
  169. package/dist/esm/daemon/domains/sessions-search.js +29 -0
  170. package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +5 -0
  171. package/dist/esm/daemon/handlers/blueprints-handlers.js +36 -0
  172. package/dist/esm/daemon/handlers/deploy-handlers.d.ts +6 -0
  173. package/dist/esm/daemon/handlers/deploy-handlers.js +5 -0
  174. package/dist/esm/daemon/handlers/fleet-handlers.d.ts +15 -0
  175. package/dist/esm/daemon/handlers/fleet-handlers.js +91 -0
  176. package/dist/esm/daemon/handlers/git-handlers.d.ts +6 -0
  177. package/dist/esm/daemon/handlers/git-handlers.js +27 -0
  178. package/dist/esm/daemon/handlers/product.d.ts +7 -0
  179. package/dist/esm/daemon/handlers/product.js +10 -119
  180. package/dist/esm/daemon/handlers/sessions-handlers.d.ts +12 -0
  181. package/dist/esm/daemon/handlers/sessions-handlers.js +31 -0
  182. package/dist/esm/daemon/handlers/shared.d.ts +5 -0
  183. package/dist/esm/daemon/handlers/shared.js +16 -0
  184. package/dist/esm/daemon/methods.d.ts +8 -1
  185. package/dist/esm/daemon/methods.js +7 -0
  186. package/dist/esm/daemon/protocol/contract.d.ts +43 -0
  187. package/dist/esm/daemon/protocol/contract.js +26 -0
  188. package/dist/esm/daemon/protocol/index.d.ts +1 -1
  189. package/dist/esm/daemon/protocol/index.js +1 -1
  190. package/dist/esm/hooks/__conformance__/matrix.d.ts +6 -0
  191. package/dist/esm/hooks/__conformance__/matrix.js +137 -0
  192. package/dist/esm/hooks/conformance/batch.js +5 -0
  193. package/dist/esm/hooks/doctor.d.ts +5 -3
  194. package/dist/esm/hooks/doctor.js +67 -16
  195. package/dist/esm/hooks/permission-request/index.js +9 -3
  196. package/dist/esm/hooks/permission-request/policy.d.ts +9 -0
  197. package/dist/esm/hooks/permission-request/policy.js +13 -0
  198. package/dist/esm/hooks/pretool-guard/dev-routing.d.ts +9 -0
  199. package/dist/esm/hooks/pretool-guard/dev-routing.js +41 -5
  200. package/dist/esm/hooks/pretool-guard/logger.d.ts +7 -0
  201. package/dist/esm/hooks/pretool-guard/logger.js +16 -0
  202. package/dist/esm/hooks/pretool-guard/runner.d.ts +6 -0
  203. package/dist/esm/hooks/pretool-guard/runner.js +68 -16
  204. package/dist/esm/hooks/pretool-guard/validators/blueprint.js +30 -10
  205. package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +54 -0
  206. package/dist/esm/hooks/pretool-guard/validators/index.d.ts +7 -0
  207. package/dist/esm/hooks/pretool-guard/validators/index.js +21 -0
  208. package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +19 -0
  209. package/dist/esm/hooks/pretool-guard/validators/path-contract.js +25 -8
  210. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +6 -1
  211. package/dist/esm/hooks/shared/guard-outcome.d.ts +149 -0
  212. package/dist/esm/hooks/shared/guard-outcome.js +215 -0
  213. package/dist/esm/hooks/shared/hook-bootstrap.d.ts +26 -0
  214. package/dist/esm/hooks/shared/hook-bootstrap.js +64 -1
  215. package/dist/esm/hooks/shared/types.d.ts +8 -5
  216. package/dist/esm/hooks/shared/types.js +50 -9
  217. package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
  218. package/dist/esm/hooks/stop/qa-changed-files.js +92 -0
  219. package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
  220. package/dist/esm/mcp/blueprint/handlers/document-mutations.d.ts +5 -0
  221. package/dist/esm/mcp/blueprint/handlers/document-mutations.js +109 -11
  222. package/dist/esm/mcp/blueprint/handlers/review-log.js +8 -1
  223. package/dist/esm/mcp/blueprint/handlers/task-advance.js +113 -54
  224. package/dist/esm/mcp/blueprint/handlers/task-verify.js +39 -19
  225. package/dist/esm/mcp/blueprint/registration.js +3 -2
  226. package/dist/esm/mcp/server.d.ts +1 -0
  227. package/dist/esm/mcp/server.js +27 -0
  228. package/dist/esm/mcp/tools/_names.d.ts +1 -1
  229. package/dist/esm/mcp/tools/_names.js +5 -0
  230. package/dist/esm/mcp/tools/_registry.js +10 -0
  231. package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
  232. package/dist/esm/mcp/tools/_shared/result.js +44 -1
  233. package/dist/esm/mcp/tools/audit.d.ts +5 -1
  234. package/dist/esm/mcp/tools/audit.js +42 -0
  235. package/dist/esm/mcp/tools/audits.d.ts +4 -0
  236. package/dist/esm/mcp/tools/ci-preflight.d.ts +50 -0
  237. package/dist/esm/mcp/tools/ci-preflight.js +183 -0
  238. package/dist/esm/mcp/tools/pr-upsert.js +39 -1
  239. package/dist/esm/mcp/tools/review-run.d.ts +28 -0
  240. package/dist/esm/mcp/tools/review-run.js +216 -0
  241. package/dist/esm/mcp/tools/run-wait.d.ts +86 -0
  242. package/dist/esm/mcp/tools/run-wait.js +593 -0
  243. package/dist/esm/mcp/tools/session-fetch-and-index.d.ts +1 -1
  244. package/dist/esm/mcp/tools/session-id.d.ts +3 -0
  245. package/dist/esm/mcp/tools/session-id.js +45 -0
  246. package/dist/esm/mcp/tools/session-info.d.ts +3 -0
  247. package/dist/esm/mcp/tools/session-info.js +49 -0
  248. package/dist/esm/output-transforms/audit.d.ts +23 -0
  249. package/dist/esm/output-transforms/audit.js +47 -0
  250. package/dist/esm/output-transforms/index.js +2 -0
  251. package/dist/esm/package.json +2 -0
  252. package/dist/esm/platform/client.d.ts +7 -1
  253. package/dist/esm/platform/github-contract-source.d.ts +2 -1
  254. package/dist/esm/pr-description/contract.js +24 -4
  255. package/dist/esm/review/authority.js +27 -14
  256. package/dist/esm/review/availability.d.ts +33 -0
  257. package/dist/esm/review/availability.js +122 -20
  258. package/dist/esm/review/delivery-verifier.d.ts +27 -0
  259. package/dist/esm/review/delivery-verifier.js +115 -0
  260. package/dist/esm/review/events.d.ts +3 -2
  261. package/dist/esm/review/events.js +23 -3
  262. package/dist/esm/review/execution/adapters.d.ts +11 -1
  263. package/dist/esm/review/execution/adapters.js +36 -10
  264. package/dist/esm/review/execution/artifacts.d.ts +1 -1
  265. package/dist/esm/review/execution/output-policy.d.ts +2 -0
  266. package/dist/esm/review/execution/output-policy.js +12 -0
  267. package/dist/esm/review/execution/review-checkout.js +121 -21
  268. package/dist/esm/review/execution/supervisor.js +36 -4
  269. package/dist/esm/review/execution/types.d.ts +9 -0
  270. package/dist/esm/review/opencode-account.d.ts +24 -0
  271. package/dist/esm/review/opencode-account.js +68 -0
  272. package/dist/esm/review/opencode-auth.d.ts +33 -0
  273. package/dist/esm/review/opencode-auth.js +79 -0
  274. package/dist/esm/review/subject.d.ts +25 -0
  275. package/dist/esm/review/subject.js +116 -26
  276. package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
  277. package/dist/esm/runtime/opencode-account-materializer.js +33 -0
  278. package/dist/esm/rust/affected-manifests.d.ts +38 -0
  279. package/dist/esm/rust/affected-manifests.js +69 -0
  280. package/dist/esm/session-memory/fetch-index.d.ts +1 -1
  281. package/dist/esm/status/snapshot.js +32 -25
  282. package/dist/esm/test/duration-sequencer.d.ts +20 -0
  283. package/dist/esm/test/duration-sequencer.js +54 -0
  284. package/dist/esm/test/shard-durations.json +922 -0
  285. package/dist/esm/test/worker-budget.d.ts +6 -0
  286. package/dist/esm/test/worker-budget.js +59 -1
  287. package/dist/esm/test-helpers/global-setup.d.ts +1 -2
  288. package/dist/esm/test-helpers/global-setup.js +79 -32
  289. package/dist/esm/typecheck/planner.d.ts +9 -1
  290. package/dist/esm/typecheck/planner.js +31 -0
  291. package/dist/esm/ultragoal/service.d.ts +7 -0
  292. package/dist/esm/ultragoal/service.js +42 -2
  293. package/dist/esm/utils/write-json-file.js +3 -1
  294. package/dist/esm/worktrees/codex-project-trust.d.ts +15 -0
  295. package/dist/esm/worktrees/codex-project-trust.js +56 -0
  296. package/dist/esm/worktrees/orphan-scan.d.ts +50 -0
  297. package/dist/esm/worktrees/orphan-scan.js +211 -0
  298. package/dist/esm/worktrees/registry.d.ts +8 -0
  299. package/dist/esm/worktrees/registry.js +18 -3
  300. package/package.json +18 -13
@@ -11,6 +11,7 @@
11
11
  */
12
12
  import { existsSync, readdirSync, readFileSync } from "node:fs";
13
13
  import { join, relative } from "node:path";
14
+ import { parse as parseYaml } from "yaml";
14
15
  const WORKFLOW_DIR = ".github/workflows";
15
16
  const WORKFLOW_FILE_PATTERN = /\.(ya?ml)$/iu;
16
17
  /** Steps/jobs that intentionally serialize workers (mutation, Stryker, etc.). */
@@ -26,6 +27,16 @@ const SETUP_VP_CACHE_TRUE_PATTERN = /^\s*cache:\s*(?:true|pnpm|npm)\b/mu;
26
27
  const PACKAGE_INSTALL_PATTERN = /(?:\bvp\s+install\b|\bpnpm\s+install\b|\bnpm\s+ci\b|\bnpm\s+install\b|\bbun\s+install\b)/u;
27
28
  // Opt-out: put this comment on the same line or the line immediately above a serial force.
28
29
  const ALLOW_COMMENT_PATTERN = /ci-test-perf-allow(?:-serial)?\b/iu;
30
+ const AGENT_KIT_SELF_CI_PATH = ".github/workflows/ci.agent-kit.yml";
31
+ function readWorkflowJob(content, jobName) {
32
+ try {
33
+ const parsed = parseYaml(content);
34
+ return parsed.jobs?.[jobName];
35
+ }
36
+ catch {
37
+ return;
38
+ }
39
+ }
29
40
  function walkWorkflowFiles(dir) {
30
41
  if (!existsSync(dir))
31
42
  return [];
@@ -112,6 +123,25 @@ function findViolations(root, file) {
112
123
  `Cold installs inflate test job wall-clock without adding coverage.`,
113
124
  });
114
125
  }
126
+ if (relPath === AGENT_KIT_SELF_CI_PATH) {
127
+ const testJob = readWorkflowJob(content, "test");
128
+ const supervisorSteps = testJob?.steps?.filter((step) => typeof step.run === "string" && step.run.includes("bun scripts/run-vitest-projects.ts"));
129
+ const hasConcurrentLane = content.includes("WP_VITEST_PROJECT_MODE: concurrent");
130
+ const hasSerialLane = content.includes("WP_VITEST_PROJECT_MODE: serial");
131
+ if ((supervisorSteps?.length ?? 0) < 2 || !hasConcurrentLane || !hasSerialLane) {
132
+ violations.push({
133
+ file: relPath,
134
+ message: `${relPath}: agent-kit self-CI must run concurrent Test shards and the isolated serial lane ` +
135
+ `through scripts/run-vitest-projects.ts so project concurrency and owned-process cleanup stay enforced.`,
136
+ });
137
+ }
138
+ if (testJob?.["timeout-minutes"] !== 5) {
139
+ violations.push({
140
+ file: relPath,
141
+ message: `${relPath}: agent-kit self-CI Test job must declare timeout-minutes: 5.`,
142
+ });
143
+ }
144
+ }
115
145
  return violations;
116
146
  }
117
147
  export function auditCiTestPerf(rootDirectory = process.cwd()) {
@@ -23,6 +23,7 @@ export function auditCommandSurface(root) {
23
23
  const verifySkillPath = path.join(root, "catalog/agent/skills/verify/SKILL.md");
24
24
  const codexVerifyPath = path.join(root, "packages/codex-plugin/skills/verify/SKILL.md");
25
25
  const claudeVerifyPath = path.join(root, "packages/claude-plugin/skills/verify/SKILL.md");
26
+ const opencodeVerifyPath = path.join(root, "packages/opencode-plugin/skills/verify/SKILL.md");
26
27
  const hookGuidancePath = path.join(root, "src/hooks/pretool-guard/dev-routing.ts");
27
28
  const requiredFiles = [
28
29
  cliPath,
@@ -35,6 +36,7 @@ export function auditCommandSurface(root) {
35
36
  verifySkillPath,
36
37
  codexVerifyPath,
37
38
  claudeVerifyPath,
39
+ opencodeVerifyPath,
38
40
  hookGuidancePath,
39
41
  ];
40
42
  for (const file of requiredFiles) {
@@ -65,8 +67,8 @@ export function auditCommandSurface(root) {
65
67
  if (!contains(mcpPrWaitPath, "buildStatusSnapshot")) {
66
68
  findings.push(violation("src/mcp/tools/pr-wait.ts", "MCP ready wait does not use the shared snapshot evaluator"));
67
69
  }
68
- for (const file of [verifySkillPath, codexVerifyPath, claudeVerifyPath]) {
69
- if (!contains(file, "wp review gate <blueprint-slug> --target HEAD --json")) {
70
+ for (const file of [verifySkillPath, codexVerifyPath, claudeVerifyPath, opencodeVerifyPath]) {
71
+ if (!contains(file, "wp review gate <blueprint-slug> --purpose delivery --target HEAD --json")) {
70
72
  findings.push(violation(path.relative(root, file), "missing executable review-gate lifecycle guidance"));
71
73
  }
72
74
  }
@@ -0,0 +1,2 @@
1
+ import type { RepoAuditResult } from "./repo-guardrails.js";
2
+ export declare function auditHostSubstitutionRisk(rootDirectory?: string): RepoAuditResult;
@@ -0,0 +1,256 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { parse as parseYaml } from "yaml";
4
+ import { readTrustedJsonFile } from "#shared-utils/read-json-file.js";
5
+ const AGENT_KIT_PACKAGE = "@webpresso/agent-kit";
6
+ const SCORECARD_REL = "docs/host-substitution-scorecard.md";
7
+ const BASELINES_REL = "maintainers/host-baselines";
8
+ const SCORECARD_HEADERS = [
9
+ "capability",
10
+ "class",
11
+ "substitution risk",
12
+ "observed native overlap",
13
+ "repo-owned invariant",
14
+ "independent verifier",
15
+ "blueprints",
16
+ "investment",
17
+ ];
18
+ const CLASS_VALUES = new Set(["core", "bridge", "utility"]);
19
+ const RISK_VALUES = new Set(["low", "medium", "high"]);
20
+ const INVESTMENT_VALUES = new Set(["invest", "maintain", "minimize"]);
21
+ const SURFACE_STATUS_VALUES = new Set(["full", "partial", "none", "in-progress"]);
22
+ function violation(file, message) {
23
+ return { file, message };
24
+ }
25
+ function isRecord(value) {
26
+ return typeof value === "object" && value !== null && !Array.isArray(value);
27
+ }
28
+ function packageName(root) {
29
+ try {
30
+ const parsed = readTrustedJsonFile(path.join(root, "package.json"));
31
+ return typeof parsed.name === "string" ? parsed.name : null;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ function splitMarkdownRow(line) {
38
+ return line
39
+ .trim()
40
+ .replace(/^\|/u, "")
41
+ .replace(/\|$/u, "")
42
+ .split("|")
43
+ .map((cell) => cell.trim());
44
+ }
45
+ function parseScorecard(markdown) {
46
+ const lines = markdown.split(/\r?\n/u);
47
+ const rows = [];
48
+ const errors = [];
49
+ for (let index = 0; index < lines.length - 1; index += 1) {
50
+ const header = splitMarkdownRow(lines[index] ?? "").map((cell) => cell.toLowerCase());
51
+ if (header.join("|") !== SCORECARD_HEADERS.join("|"))
52
+ continue;
53
+ const separator = lines[index + 1] ?? "";
54
+ if (!/^\s*\|[\s:|-]+\|\s*$/u.test(separator)) {
55
+ errors.push("scorecard table header must be followed by a markdown separator row");
56
+ continue;
57
+ }
58
+ for (let rowIndex = index + 2; rowIndex < lines.length; rowIndex += 1) {
59
+ const line = lines[rowIndex] ?? "";
60
+ if (!line.trimStart().startsWith("|"))
61
+ break;
62
+ const cells = splitMarkdownRow(line);
63
+ if (cells.length !== SCORECARD_HEADERS.length) {
64
+ errors.push(`scorecard row ${rowIndex + 1} has ${cells.length} cells`);
65
+ continue;
66
+ }
67
+ rows.push(Object.fromEntries(SCORECARD_HEADERS.map((key, i) => [key, cells[i] ?? ""])));
68
+ }
69
+ return { rows, errors };
70
+ }
71
+ errors.push(`missing scorecard table with headers: ${SCORECARD_HEADERS.join(" | ")}`);
72
+ return { rows, errors };
73
+ }
74
+ function cleanToken(value) {
75
+ return value.replace(/^`|`$/gu, "").trim();
76
+ }
77
+ function splitCellList(value) {
78
+ return value
79
+ .split(/(?:<br\s*\/?>|,)/iu)
80
+ .map((item) => cleanToken(item))
81
+ .filter((item) => item.length > 0 && item !== "--" && item.toLowerCase() !== "none");
82
+ }
83
+ function scanBlueprintIds(root) {
84
+ const ids = new Set();
85
+ const base = path.join(root, "blueprints");
86
+ if (!existsSync(base))
87
+ return ids;
88
+ for (const status of ["draft", "planned", "in-progress", "completed", "archived", "parked"]) {
89
+ const statusDir = path.join(base, status);
90
+ if (!existsSync(statusDir))
91
+ continue;
92
+ for (const entry of readdirSync(statusDir)) {
93
+ if (entry.startsWith("."))
94
+ continue;
95
+ const absolute = path.join(statusDir, entry);
96
+ if (entry.endsWith(".md")) {
97
+ ids.add(`${status}/${entry.replace(/\.md$/u, "")}`);
98
+ continue;
99
+ }
100
+ if (statSync(absolute).isDirectory() && existsSync(path.join(absolute, "_overview.md"))) {
101
+ ids.add(`${status}/${entry}`);
102
+ }
103
+ }
104
+ }
105
+ return ids;
106
+ }
107
+ function activeBlueprintIds(root) {
108
+ const ids = new Set();
109
+ for (const id of scanBlueprintIds(root)) {
110
+ if (id.startsWith("planned/") || id.startsWith("in-progress/"))
111
+ ids.add(id);
112
+ }
113
+ return ids;
114
+ }
115
+ function validateScorecard(root) {
116
+ const scorecardPath = path.join(root, SCORECARD_REL);
117
+ if (!existsSync(scorecardPath)) {
118
+ return { checked: 1, violations: [violation(SCORECARD_REL, "scorecard file is missing")] };
119
+ }
120
+ const parsed = parseScorecard(readFileSync(scorecardPath, "utf8"));
121
+ const violations = parsed.errors.map((message) => violation(SCORECARD_REL, message));
122
+ const seenCapabilities = new Set();
123
+ const allBlueprintIds = scanBlueprintIds(root);
124
+ const active = activeBlueprintIds(root);
125
+ const activeCoverage = new Map();
126
+ for (const id of active)
127
+ activeCoverage.set(id, 0);
128
+ for (const [index, row] of parsed.rows.entries()) {
129
+ const rowLabel = `row ${index + 1}`;
130
+ const capability = cleanToken(row.capability ?? "");
131
+ if (!/^[a-z][a-z0-9-]*$/u.test(capability)) {
132
+ violations.push(violation(SCORECARD_REL, `${rowLabel}: capability must be a stable kebab-case id`));
133
+ }
134
+ else if (seenCapabilities.has(capability)) {
135
+ violations.push(violation(SCORECARD_REL, `${rowLabel}: duplicate capability ${capability}`));
136
+ }
137
+ seenCapabilities.add(capability);
138
+ const kind = cleanToken(row.class ?? "");
139
+ if (!CLASS_VALUES.has(kind)) {
140
+ violations.push(violation(SCORECARD_REL, `${rowLabel}: class must be core, bridge, or utility`));
141
+ }
142
+ const risk = cleanToken(row["substitution risk"] ?? "");
143
+ if (!RISK_VALUES.has(risk)) {
144
+ violations.push(violation(SCORECARD_REL, `${rowLabel}: substitution risk must be low, medium, or high`));
145
+ }
146
+ const investment = cleanToken(row.investment ?? "");
147
+ if (!INVESTMENT_VALUES.has(investment)) {
148
+ violations.push(violation(SCORECARD_REL, `${rowLabel}: investment must be invest, maintain, or minimize`));
149
+ }
150
+ for (const verifier of splitCellList(row["independent verifier"] ?? "")) {
151
+ if (!verifier.startsWith("repo:"))
152
+ continue;
153
+ const verifierPath = verifier.slice("repo:".length);
154
+ if (!existsSync(path.join(root, verifierPath))) {
155
+ violations.push(violation(SCORECARD_REL, `${rowLabel}: verifier path missing: ${verifier}`));
156
+ }
157
+ }
158
+ for (const blueprint of splitCellList(row.blueprints ?? "")) {
159
+ if (!/^(?:draft|planned|in-progress|completed|archived|parked)\/[a-z0-9][a-z0-9-]*$/u.test(blueprint)) {
160
+ violations.push(violation(SCORECARD_REL, `${rowLabel}: invalid blueprint reference ${blueprint}`));
161
+ continue;
162
+ }
163
+ if (!allBlueprintIds.has(blueprint)) {
164
+ violations.push(violation(SCORECARD_REL, `${rowLabel}: blueprint reference does not exist: ${blueprint}`));
165
+ }
166
+ if (activeCoverage.has(blueprint)) {
167
+ activeCoverage.set(blueprint, (activeCoverage.get(blueprint) ?? 0) + 1);
168
+ }
169
+ }
170
+ }
171
+ for (const [id, count] of activeCoverage) {
172
+ if (count !== 1) {
173
+ violations.push(violation(SCORECARD_REL, `active blueprint ${id} must be covered exactly once in scorecard blueprints column; found ${count}`));
174
+ }
175
+ }
176
+ return { checked: parsed.rows.length + activeCoverage.size, violations };
177
+ }
178
+ function validateHostBaseline(root, relPath) {
179
+ const violations = [];
180
+ const value = parseYaml(readFileSync(path.join(root, relPath), "utf8"));
181
+ if (!isRecord(value))
182
+ return [violation(relPath, "host baseline must be a YAML object")];
183
+ for (const key of ["host", "cli"]) {
184
+ if (typeof value[key] !== "string" || value[key].trim() === "") {
185
+ violations.push(violation(relPath, `${key} must be a non-empty string`));
186
+ }
187
+ }
188
+ for (const key of ["last_implemented", "last_observed"]) {
189
+ const record = value[key];
190
+ if (!isRecord(record)) {
191
+ violations.push(violation(relPath, `${key} must be an object`));
192
+ continue;
193
+ }
194
+ for (const child of ["version", "date"]) {
195
+ if (typeof record[child] !== "string" || record[child].trim() === "") {
196
+ violations.push(violation(relPath, `${key}.${child} must be a non-empty string`));
197
+ }
198
+ }
199
+ }
200
+ const sources = value.sources;
201
+ const docs = isRecord(sources) ? sources.docs : undefined;
202
+ if (!Array.isArray(docs) ||
203
+ docs.some((item) => typeof item !== "string" || !item.startsWith("https://"))) {
204
+ violations.push(violation(relPath, "sources.docs must be an array of https URLs"));
205
+ }
206
+ const surfaces = value.surfaces;
207
+ if (!isRecord(surfaces)) {
208
+ violations.push(violation(relPath, "surfaces must be an object"));
209
+ }
210
+ else {
211
+ for (const [name, raw] of Object.entries(surfaces)) {
212
+ if (!isRecord(raw)) {
213
+ violations.push(violation(relPath, `surfaces.${name} must be an object`));
214
+ continue;
215
+ }
216
+ if (typeof raw.status !== "string" || !SURFACE_STATUS_VALUES.has(raw.status)) {
217
+ violations.push(violation(relPath, `surfaces.${name}.status must be full, partial, none, or in-progress`));
218
+ }
219
+ if (typeof raw.notes !== "string" || raw.notes.trim() === "") {
220
+ violations.push(violation(relPath, `surfaces.${name}.notes must be a non-empty string`));
221
+ }
222
+ }
223
+ }
224
+ return violations;
225
+ }
226
+ function validateHostBaselines(root) {
227
+ const dir = path.join(root, BASELINES_REL);
228
+ if (!existsSync(dir)) {
229
+ return {
230
+ checked: 1,
231
+ violations: [violation(BASELINES_REL, "host baselines directory missing")],
232
+ };
233
+ }
234
+ const files = readdirSync(dir)
235
+ .filter((entry) => entry.endsWith(".yaml"))
236
+ .map((entry) => path.join(BASELINES_REL, entry));
237
+ return {
238
+ checked: files.length,
239
+ violations: files.flatMap((file) => validateHostBaseline(root, file)),
240
+ };
241
+ }
242
+ export function auditHostSubstitutionRisk(rootDirectory = process.cwd()) {
243
+ const root = path.resolve(rootDirectory);
244
+ const title = "Host substitution risk";
245
+ if (packageName(root) !== AGENT_KIT_PACKAGE)
246
+ return { ok: true, title, checked: 0, violations: [] };
247
+ const scorecard = validateScorecard(root);
248
+ const baselines = validateHostBaselines(root);
249
+ const violations = [...scorecard.violations, ...baselines.violations];
250
+ return {
251
+ ok: violations.length === 0,
252
+ title,
253
+ checked: scorecard.checked + baselines.checked,
254
+ violations,
255
+ };
256
+ }
@@ -14,11 +14,11 @@ export interface AuditKindDescriptor {
14
14
  }
15
15
  export declare const SCRIPT_AUDIT_KIND_IDS: readonly ["test-smells", "tph-e2e"];
16
16
  export declare const SPECIAL_AUDIT_KIND_IDS: readonly ["bundle-budget", "commit-message", "blueprint-pr-coverage", "mutation", "guardrails", "quality"];
17
- export declare const REPO_AUDIT_KIND_IDS: readonly ["catalog-drift", "package-surface", "command-surface", "reference-parity-matrix", "blueprint-readme-drift", "blueprint-lifecycle", "blueprint-trust", "roadmap-links", "docs-frontmatter", "agents", "vision", "tech-debt", "supported-agent-clis", "no-relative-parent-imports", "no-link-protocol", "no-relative-package-scripts", "test-isolation", "bucket-boundary", "skill-sizes", "broken-refs", "memory-rotation", "gitignore-agent-surfaces", "memory-unified", "compile-drift", "no-legacy-cli-bin", "architecture-drift", "cloudflare-deploy-contract", "toolchain-isolation", "absolute-path-policy", "no-first-party-mjs", "outside-review-runtime-ownership", "no-math-random", "agent-cost", "blueprint-db-consistency", "tech-debt-cadence", "cross-repo-correlation", "ai-contracts", "atomic-state-writes", "hook-surface", "hook-vendor-drift", "session-memory-hardcut", "open-source-licenses", "secrets-policy", "no-dev-vars", "secret-provider-quarantine", "github-actions-secrets", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "test-scan-perf", "worktree-main-ownership", "sast", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "legacy-workflow-identity", "rules", "skills"];
17
+ export declare const REPO_AUDIT_KIND_IDS: readonly ["catalog-drift", "package-surface", "command-surface", "reference-parity-matrix", "blueprint-readme-drift", "blueprint-lifecycle", "blueprint-trust", "roadmap-links", "docs-frontmatter", "agents", "vision", "tech-debt", "supported-agent-clis", "no-relative-parent-imports", "no-link-protocol", "no-relative-package-scripts", "test-isolation", "bucket-boundary", "skill-sizes", "broken-refs", "memory-rotation", "gitignore-agent-surfaces", "memory-unified", "compile-drift", "no-legacy-cli-bin", "architecture-drift", "cloudflare-deploy-contract", "toolchain-isolation", "absolute-path-policy", "no-first-party-mjs", "outside-review-runtime-ownership", "no-math-random", "agent-cost", "blueprint-db-consistency", "tech-debt-cadence", "cross-repo-correlation", "ai-contracts", "atomic-state-writes", "hook-surface", "hook-vendor-drift", "session-memory-hardcut", "open-source-licenses", "secrets-policy", "no-dev-vars", "secret-provider-quarantine", "github-actions-secrets", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "ci-guardrails-wiring", "test-scan-perf", "worktree-main-ownership", "sast", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "host-substitution-risk", "legacy-workflow-identity", "rules", "skills", "typescript-version", "changeset-required"];
18
18
  export declare const AUDIT_KIND_DESCRIPTORS: readonly AuditKindDescriptor[];
19
- export declare const AUDIT_KIND_IDS: readonly ["test-smells", "tph-e2e", ...("absolute-path-policy" | "agent-cost" | "agents" | "ai-contracts" | "architecture-drift" | "atomic-state-writes" | "blueprint-db-consistency" | "blueprint-lifecycle" | "blueprint-pr-coverage" | "blueprint-readme-drift" | "blueprint-trust" | "broken-refs" | "bucket-boundary" | "bundle-budget" | "catalog-drift" | "ci-test-perf" | "cloudflare-deploy-contract" | "command-surface" | "commit-message" | "compile-drift" | "consumer-agent-kit-dependency" | "cross-repo-correlation" | "docs-frontmatter" | "github-actions-secrets" | "gitignore-agent-surfaces" | "guardrails" | "harness-overlay-evidence" | "harness-surfaces" | "hook-surface" | "hook-vendor-drift" | "legacy-workflow-identity" | "memory-rotation" | "memory-unified" | "mutation" | "no-dev-vars" | "no-first-party-mjs" | "no-legacy-cli-bin" | "no-link-protocol" | "no-math-random" | "no-relative-package-scripts" | "no-relative-parent-imports" | "open-source-licenses" | "outside-review-runtime-ownership" | "package-surface" | "quality" | "reference-parity-matrix" | "roadmap-links" | "rules" | "sast" | "secret-provider-quarantine" | "secrets-config" | "secrets-policy" | "security-quality-regressions" | "session-memory-hardcut" | "skill-sizes" | "skills" | "supported-agent-clis" | "tech-debt" | "tech-debt-cadence" | "test-isolation" | "test-scan-perf" | "toolchain-isolation" | "vision" | "weakness-mining" | "worktree-main-ownership")[]];
20
- export declare const MCP_AUDIT_KIND_IDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "test-scan-perf", "session-memory-hardcut", "worktree-main-ownership", "sast"];
21
- export declare const AFFECTED_SAFE_AUDIT_KIND_IDS: readonly ["blueprint-readme-drift", "blueprint-lifecycle", "bucket-boundary", "skill-sizes", "broken-refs", "no-dev-vars", "absolute-path-policy", "secret-provider-quarantine", "secrets-config", "docs-frontmatter", "no-relative-parent-imports", "no-link-protocol", "no-first-party-mjs", "no-math-random", "test-scan-perf"];
19
+ export declare const AUDIT_KIND_IDS: readonly ["test-smells", "tph-e2e", ...("absolute-path-policy" | "agent-cost" | "agents" | "ai-contracts" | "architecture-drift" | "atomic-state-writes" | "blueprint-db-consistency" | "blueprint-lifecycle" | "blueprint-pr-coverage" | "blueprint-readme-drift" | "blueprint-trust" | "broken-refs" | "bucket-boundary" | "bundle-budget" | "catalog-drift" | "changeset-required" | "ci-guardrails-wiring" | "ci-test-perf" | "cloudflare-deploy-contract" | "command-surface" | "commit-message" | "compile-drift" | "consumer-agent-kit-dependency" | "cross-repo-correlation" | "docs-frontmatter" | "github-actions-secrets" | "gitignore-agent-surfaces" | "guardrails" | "harness-overlay-evidence" | "harness-surfaces" | "hook-surface" | "hook-vendor-drift" | "host-substitution-risk" | "legacy-workflow-identity" | "memory-rotation" | "memory-unified" | "mutation" | "no-dev-vars" | "no-first-party-mjs" | "no-legacy-cli-bin" | "no-link-protocol" | "no-math-random" | "no-relative-package-scripts" | "no-relative-parent-imports" | "open-source-licenses" | "outside-review-runtime-ownership" | "package-surface" | "quality" | "reference-parity-matrix" | "roadmap-links" | "rules" | "sast" | "secret-provider-quarantine" | "secrets-config" | "secrets-policy" | "security-quality-regressions" | "session-memory-hardcut" | "skill-sizes" | "skills" | "supported-agent-clis" | "tech-debt" | "tech-debt-cadence" | "test-isolation" | "test-scan-perf" | "toolchain-isolation" | "typescript-version" | "vision" | "weakness-mining" | "worktree-main-ownership")[]];
20
+ export declare const MCP_AUDIT_KIND_IDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "host-substitution-risk", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "test-scan-perf", "session-memory-hardcut", "worktree-main-ownership", "typescript-version", "ci-guardrails-wiring", "sast", "changeset-required"];
21
+ export declare const AFFECTED_SAFE_AUDIT_KIND_IDS: readonly ["blueprint-readme-drift", "blueprint-lifecycle", "bucket-boundary", "skill-sizes", "broken-refs", "no-dev-vars", "absolute-path-policy", "secret-provider-quarantine", "secrets-config", "docs-frontmatter", "no-relative-parent-imports", "no-link-protocol", "no-first-party-mjs", "no-math-random", "test-scan-perf", "ci-guardrails-wiring", "changeset-required"];
22
22
  export type AuditKindId = (typeof AUDIT_KIND_IDS)[number];
23
23
  export type RepoAuditKindId = (typeof REPO_AUDIT_KIND_IDS)[number];
24
24
  export type MCPAuditKindId = (typeof MCP_AUDIT_KIND_IDS)[number];
@@ -17,6 +17,8 @@ const affectedSafeKinds = [
17
17
  "no-first-party-mjs",
18
18
  "no-math-random",
19
19
  "test-scan-perf",
20
+ "ci-guardrails-wiring",
21
+ "changeset-required",
20
22
  ];
21
23
  const mcpKindIds = [
22
24
  "test-smells",
@@ -44,6 +46,7 @@ const mcpKindIds = [
44
46
  "harness-surfaces",
45
47
  "weakness-mining",
46
48
  "harness-overlay-evidence",
49
+ "host-substitution-risk",
47
50
  "ai-contracts",
48
51
  "atomic-state-writes",
49
52
  "no-relative-package-scripts",
@@ -60,7 +63,10 @@ const mcpKindIds = [
60
63
  "test-scan-perf",
61
64
  "session-memory-hardcut",
62
65
  "worktree-main-ownership",
66
+ "typescript-version",
67
+ "ci-guardrails-wiring",
63
68
  "sast",
69
+ "changeset-required",
64
70
  ];
65
71
  const affectedSafe = new Set(affectedSafeKinds);
66
72
  const mcpKinds = new Set(mcpKindIds);
@@ -132,15 +138,19 @@ const repoKinds = [
132
138
  "secrets-config",
133
139
  "consumer-agent-kit-dependency",
134
140
  "ci-test-perf",
141
+ "ci-guardrails-wiring",
135
142
  "test-scan-perf",
136
143
  "worktree-main-ownership",
137
144
  "sast",
138
145
  "harness-surfaces",
139
146
  "weakness-mining",
140
147
  "harness-overlay-evidence",
148
+ "host-substitution-risk",
141
149
  "legacy-workflow-identity",
142
150
  "rules",
143
151
  "skills",
152
+ "typescript-version",
153
+ "changeset-required",
144
154
  ];
145
155
  function descriptor(id, kindClass) {
146
156
  return {
@@ -151,7 +161,9 @@ function descriptor(id, kindClass) {
151
161
  gateClass: signalKinds.has(id) ? "signal" : "blocking",
152
162
  scopeSafety: affectedSafe.has(id) ? "affected-safe" : "full-scan-only",
153
163
  summaryLabel: id === "sast" ? "SAST" : id,
154
- ...(id === "sast" ? { includedInGuardrails: false } : {}),
164
+ ...(["sast", "host-substitution-risk", "changeset-required"].includes(id)
165
+ ? { includedInGuardrails: false }
166
+ : {}),
155
167
  ...(["blueprint-readme-drift", "docs-frontmatter"].includes(id) ? { fixable: true } : {}),
156
168
  };
157
169
  }
@@ -133,10 +133,9 @@ function shouldSkipDirectory(name) {
133
133
  ".stryker-tmp",
134
134
  ".agent",
135
135
  ".agents",
136
- ".gemini",
137
- ".windsurf",
138
136
  ".runtime",
139
137
  ".codex",
138
+ ".grok",
140
139
  "_worktrees",
141
140
  // Gitignored Claude Code agent surface — agent worktree scratch under
142
141
  // .claude/worktrees/* carries vendored package manifests that are not the
@@ -0,0 +1,2 @@
1
+ import type { RepoAuditResult } from "./repo-guardrails.js";
2
+ export declare function auditTypescriptVersion(rootDirectory: string): RepoAuditResult;
@@ -0,0 +1,123 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { parse as parseYaml } from "yaml";
4
+ /**
5
+ * `wp audit typescript-version` — enforce the native TypeScript compiler major
6
+ * at consumers.
7
+ *
8
+ * agent-kit migrated itself to TypeScript 7 (the native Go compiler) and pins
9
+ * `typescript: ^7.0.2` in every shipped template, but nothing checked that a
10
+ * downstream consumer stayed on 7 — so a consumer could silently regress to a
11
+ * legacy TypeScript major (observed: edge-matte pinned `typescript: ^6.0.3`).
12
+ * This audit ships through `wp audit guardrails`, so consumers inherit it
13
+ * automatically on their next `wp` bump with no CI edits.
14
+ *
15
+ * It gates only the `typescript` compiler dependency. `@typescript/typescript6`
16
+ * — the Microsoft-published TS6 compiler-API compatibility package that
17
+ * agent-kit itself depends on until TS 7.1 stabilizes the programmatic API — is
18
+ * a distinct dependency and is never flagged.
19
+ */
20
+ const REQUIRED_MAJOR = 7;
21
+ const COMPILER_DEP = "typescript";
22
+ // Inline escape hatch for a documented, temporary exception (e.g. a consumer
23
+ // mid-migration). Placed as a comment in pnpm-workspace.yaml or in the manifest.
24
+ const OPT_OUT_MARKER = "typescript-version-allow";
25
+ /** Lowest major a version range can resolve to (the enforceable floor). */
26
+ function majorFloor(range) {
27
+ const match = /(\d+)/u.exec(range);
28
+ return match ? Number(match[1]) : undefined;
29
+ }
30
+ /** `catalog:` / `workspace:` pins resolve elsewhere; `*`/`latest` carry no floor. */
31
+ function isDelegatedPin(value) {
32
+ return (value.startsWith("catalog:") ||
33
+ value.startsWith("workspace:") ||
34
+ value === "*" ||
35
+ value === "latest");
36
+ }
37
+ function checkTypescriptPin(source, value, violations) {
38
+ if (isDelegatedPin(value))
39
+ return;
40
+ const major = majorFloor(value);
41
+ if (major === undefined || major >= REQUIRED_MAJOR)
42
+ return;
43
+ violations.push({
44
+ file: source,
45
+ severity: "error",
46
+ ruleId: "typescript-version",
47
+ message: `typescript is pinned to "${value}" (major ${major}); the native compiler ` +
48
+ `requires TypeScript ${REQUIRED_MAJOR}. Bump to ^${REQUIRED_MAJOR} (or a catalog: ref). ` +
49
+ `The @typescript/typescript6 compiler-API bridge is separate and allowed.`,
50
+ });
51
+ }
52
+ function collectManifestPaths(rootDirectory) {
53
+ const paths = [];
54
+ if (existsSync(join(rootDirectory, "package.json")))
55
+ paths.push("package.json");
56
+ for (const group of ["packages", "apps"]) {
57
+ const dir = join(rootDirectory, group);
58
+ if (!existsSync(dir))
59
+ continue;
60
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
61
+ if (!entry.isDirectory())
62
+ continue;
63
+ const manifest = join(group, entry.name, "package.json");
64
+ if (existsSync(join(rootDirectory, manifest)))
65
+ paths.push(manifest);
66
+ }
67
+ }
68
+ return paths;
69
+ }
70
+ export function auditTypescriptVersion(rootDirectory) {
71
+ const violations = [];
72
+ const sources = [];
73
+ let checked = 0;
74
+ // 1. Workspace catalog pin — the primary contract; catches catalog-level drift.
75
+ const workspacePath = join(rootDirectory, "pnpm-workspace.yaml");
76
+ if (existsSync(workspacePath)) {
77
+ const raw = readFileSync(workspacePath, "utf8");
78
+ sources.push({ path: "pnpm-workspace.yaml", kind: "pnpm-workspace" });
79
+ if (!raw.includes(OPT_OUT_MARKER)) {
80
+ const workspace = (parseYaml(raw) ?? {});
81
+ const catalogPin = workspace.catalog?.[COMPILER_DEP];
82
+ if (typeof catalogPin === "string") {
83
+ checked += 1;
84
+ checkTypescriptPin("pnpm-workspace.yaml (catalog.typescript)", catalogPin, violations);
85
+ }
86
+ // pnpm also supports NAMED catalogs (`catalogs.<name>.typescript`), a
87
+ // separate map from the default `catalog` above. A pin placed there
88
+ // resolves the same way for consumers using `catalog:<name>` refs, so it
89
+ // must be enforced identically or a consumer can dodge the check by
90
+ // moving the pin into a named catalog.
91
+ for (const [catalogName, entries] of Object.entries(workspace.catalogs ?? {})) {
92
+ const namedPin = entries[COMPILER_DEP];
93
+ if (typeof namedPin !== "string")
94
+ continue;
95
+ checked += 1;
96
+ checkTypescriptPin(`pnpm-workspace.yaml (catalogs.${catalogName}.typescript)`, namedPin, violations);
97
+ }
98
+ }
99
+ }
100
+ // 2. Direct typescript pins in package manifests (catalog:/workspace: skipped —
101
+ // they resolve through the catalog checked above).
102
+ for (const manifest of collectManifestPaths(rootDirectory)) {
103
+ const raw = readFileSync(join(rootDirectory, manifest), "utf8");
104
+ sources.push({ path: manifest, kind: "package-manifest" });
105
+ if (raw.includes(OPT_OUT_MARKER))
106
+ continue;
107
+ const parsed = JSON.parse(raw);
108
+ for (const field of ["dependencies", "devDependencies"]) {
109
+ const pin = parsed[field]?.[COMPILER_DEP];
110
+ if (typeof pin === "string") {
111
+ checked += 1;
112
+ checkTypescriptPin(`${manifest} (${field}.typescript)`, pin, violations);
113
+ }
114
+ }
115
+ }
116
+ return {
117
+ ok: violations.length === 0,
118
+ title: `TypeScript ${REQUIRED_MAJOR} consumer pin`,
119
+ checked,
120
+ sources,
121
+ violations,
122
+ };
123
+ }