@webpresso/agent-kit 3.1.28 → 3.2.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.

Potentially problematic release.


This version of @webpresso/agent-kit might be problematic. Click here for more details.

Files changed (329) hide show
  1. package/README.md +53 -15
  2. package/bin/_run.js +179 -19
  3. package/catalog/AGENTS.md.tpl +6 -6
  4. package/catalog/agent/agents/implementer.md +45 -0
  5. package/catalog/agent/rules/ci-cost-local-first.md +139 -0
  6. package/catalog/agent/rules/ci-test-perf.md +5 -1
  7. package/catalog/agent/rules/cmd-execution.md +9 -0
  8. package/catalog/agent/rules/parallel-orchestration-quota.md +100 -0
  9. package/catalog/agent/rules/pre-implementation.md +30 -2
  10. package/catalog/agent/rules/supported-agent-clis.md +9 -0
  11. package/catalog/agent/rules/test-scan-perf.md +80 -0
  12. package/catalog/agent/rules/typescript-version.md +50 -0
  13. package/catalog/agent/rules/workflow-skills-routing.md +4 -2
  14. package/catalog/agent/skills/codex/SKILL.md +25 -1
  15. package/catalog/agent/skills/fix/SKILL.md +3 -0
  16. package/catalog/agent/skills/verify/SKILL.md +22 -2
  17. package/catalog/docs/templates/blueprint.md +1 -1
  18. package/dist/esm/audit/agents.js +1 -0
  19. package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +12 -0
  20. package/dist/esm/audit/blueprint-lifecycle-sql.js +23 -2
  21. package/dist/esm/audit/blueprint-pr-coverage.js +72 -7
  22. package/dist/esm/audit/ci-guardrails-wiring.d.ts +22 -0
  23. package/dist/esm/audit/ci-guardrails-wiring.js +90 -0
  24. package/dist/esm/audit/ci-test-perf.js +30 -0
  25. package/dist/esm/audit/command-surface.js +4 -2
  26. package/dist/esm/audit/host-substitution-risk.d.ts +2 -0
  27. package/dist/esm/audit/host-substitution-risk.js +256 -0
  28. package/dist/esm/audit/registry.d.ts +4 -4
  29. package/dist/esm/audit/registry.js +11 -1
  30. package/dist/esm/audit/roadmap-links.js +6 -0
  31. package/dist/esm/audit/test-scan-perf.d.ts +18 -0
  32. package/dist/esm/audit/test-scan-perf.js +275 -0
  33. package/dist/esm/audit/toolchain-isolation.js +1 -2
  34. package/dist/esm/audit/typescript-version.d.ts +2 -0
  35. package/dist/esm/audit/typescript-version.js +123 -0
  36. package/dist/esm/blueprint/core/parser.js +43 -1
  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 +65 -10
  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/engine.d.ts +9 -0
  46. package/dist/esm/blueprint/lifecycle/engine.js +53 -6
  47. package/dist/esm/blueprint/lifecycle/local.d.ts +2 -1
  48. package/dist/esm/blueprint/lifecycle/local.js +19 -3
  49. package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
  50. package/dist/esm/blueprint/lifecycle/review-provenance.js +118 -12
  51. package/dist/esm/blueprint/markdown/helpers.js +6 -2
  52. package/dist/esm/blueprint/service/BlueprintCreationService.d.ts +1 -0
  53. package/dist/esm/blueprint/service/BlueprintCreationService.js +53 -4
  54. package/dist/esm/blueprint/service/blueprint-tech-debt-links.js +16 -2
  55. package/dist/esm/blueprint/trust/command-runner.d.ts +26 -0
  56. package/dist/esm/blueprint/trust/command-runner.js +148 -0
  57. package/dist/esm/blueprint/trust/gates.d.ts +7 -0
  58. package/dist/esm/blueprint/trust/gates.js +20 -0
  59. package/dist/esm/blueprint/trust/promotion.d.ts +8 -0
  60. package/dist/esm/blueprint/trust/promotion.js +62 -112
  61. package/dist/esm/blueprint/trust/scaffold.d.ts +19 -0
  62. package/dist/esm/blueprint/trust/scaffold.js +64 -0
  63. package/dist/esm/blueprint/trust/validator.d.ts +1 -0
  64. package/dist/esm/blueprint/trust/validator.js +7 -1
  65. package/dist/esm/blueprint/utils/archive.js +27 -5
  66. package/dist/esm/blueprint/utils/document-paths.d.ts +18 -0
  67. package/dist/esm/blueprint/utils/document-paths.js +45 -12
  68. package/dist/esm/build/cli-mcp-parity.js +10 -0
  69. package/dist/esm/ci/act-replay.js +110 -7
  70. package/dist/esm/ci/act-runner.d.ts +4 -0
  71. package/dist/esm/ci/act-runner.js +85 -26
  72. package/dist/esm/ci/act-worktree-git.d.ts +11 -0
  73. package/dist/esm/ci/act-worktree-git.js +219 -0
  74. package/dist/esm/cli/bundle/commands/blueprint.d.ts +1 -0
  75. package/dist/esm/cli/bundle/commands/blueprint.js +1 -0
  76. package/dist/esm/cli/cli.d.ts +1 -1
  77. package/dist/esm/cli/cli.js +34 -0
  78. package/dist/esm/cli/commands/agent-launch.d.ts +6 -1
  79. package/dist/esm/cli/commands/agent-launch.js +82 -4
  80. package/dist/esm/cli/commands/audit-core.d.ts +1 -1
  81. package/dist/esm/cli/commands/audit-core.js +8 -1
  82. package/dist/esm/cli/commands/audit.js +20 -5
  83. package/dist/esm/cli/commands/blueprint/execution.js +18 -6
  84. package/dist/esm/cli/commands/blueprint/mutations.js +83 -47
  85. package/dist/esm/cli/commands/blueprint/router-dispatch.d.ts +2 -1
  86. package/dist/esm/cli/commands/blueprint/router-dispatch.js +20 -1
  87. package/dist/esm/cli/commands/blueprint/router-output.d.ts +21 -0
  88. package/dist/esm/cli/commands/blueprint/router-output.js +48 -17
  89. package/dist/esm/cli/commands/blueprint/router.d.ts +7 -0
  90. package/dist/esm/cli/commands/blueprint/router.js +40 -3
  91. package/dist/esm/cli/commands/ci-preflight.d.ts +13 -2
  92. package/dist/esm/cli/commands/ci-preflight.js +6 -3
  93. package/dist/esm/cli/commands/ci.d.ts +8 -1
  94. package/dist/esm/cli/commands/ci.js +80 -21
  95. package/dist/esm/cli/commands/compile.js +5 -31
  96. package/dist/esm/cli/commands/dash/delivery-status.d.ts +1 -0
  97. package/dist/esm/cli/commands/dash/delivery-status.js +11 -4
  98. package/dist/esm/cli/commands/dash/keymap.js +1 -0
  99. package/dist/esm/cli/commands/dash/launch-preferences.d.ts +15 -0
  100. package/dist/esm/cli/commands/dash/launch-preferences.js +46 -17
  101. package/dist/esm/cli/commands/dash/plan-once-label.d.ts +7 -0
  102. package/dist/esm/cli/commands/dash/plan-once-label.js +5 -0
  103. package/dist/esm/cli/commands/dash/plan-once-membership.d.ts +67 -0
  104. package/dist/esm/cli/commands/dash/plan-once-membership.js +151 -0
  105. package/dist/esm/cli/commands/dash/plan-once-merge.d.ts +143 -0
  106. package/dist/esm/cli/commands/dash/plan-once-merge.js +238 -0
  107. package/dist/esm/cli/commands/dash/plan-once.d.ts +22 -2
  108. package/dist/esm/cli/commands/dash/plan-once.js +41 -4
  109. package/dist/esm/cli/commands/dash/tui/attention-hud.d.ts +9 -0
  110. package/dist/esm/cli/commands/dash/tui/attention-hud.js +9 -0
  111. package/dist/esm/cli/commands/dash/tui/chat-bridge.d.ts +41 -0
  112. package/dist/esm/cli/commands/dash/tui/chat-bridge.js +73 -0
  113. package/dist/esm/cli/commands/dash/tui/chat-mode.d.ts +47 -0
  114. package/dist/esm/cli/commands/dash/tui/chat-mode.js +98 -0
  115. package/dist/esm/cli/commands/dash/tui/frame.d.ts +3 -0
  116. package/dist/esm/cli/commands/dash/tui/frame.js +3 -1
  117. package/dist/esm/cli/commands/dash/tui/palette.d.ts +1 -1
  118. package/dist/esm/cli/commands/dash/tui/palette.js +14 -0
  119. package/dist/esm/cli/commands/dash/tui/runtime.js +239 -4
  120. package/dist/esm/cli/commands/dash/tui/state.d.ts +41 -0
  121. package/dist/esm/cli/commands/dash/tui/state.js +257 -4
  122. package/dist/esm/cli/commands/doctor.js +5 -0
  123. package/dist/esm/cli/commands/format.js +0 -2
  124. package/dist/esm/cli/commands/hook.d.ts +5 -0
  125. package/dist/esm/cli/commands/hook.js +15 -9
  126. package/dist/esm/cli/commands/init/config.d.ts +0 -1
  127. package/dist/esm/cli/commands/init/config.js +3 -6
  128. package/dist/esm/cli/commands/init/convergence-apply.d.ts +24 -0
  129. package/dist/esm/cli/commands/init/convergence-apply.js +221 -0
  130. package/dist/esm/cli/commands/init/convergence-state.d.ts +13 -0
  131. package/dist/esm/cli/commands/init/convergence-state.js +54 -0
  132. package/dist/esm/cli/commands/init/convergence.d.ts +23 -0
  133. package/dist/esm/cli/commands/init/convergence.js +57 -0
  134. package/dist/esm/cli/commands/init/git-convergence.d.ts +19 -0
  135. package/dist/esm/cli/commands/init/git-convergence.js +168 -0
  136. package/dist/esm/cli/commands/init/gitignore-patcher.js +2 -0
  137. package/dist/esm/cli/commands/init/grok-privacy-smoke.d.ts +52 -0
  138. package/dist/esm/cli/commands/init/grok-privacy-smoke.js +91 -0
  139. package/dist/esm/cli/commands/init/host-native-proof.d.ts +18 -0
  140. package/dist/esm/cli/commands/init/host-native-proof.js +88 -0
  141. package/dist/esm/cli/commands/init/host-visibility.d.ts +2 -2
  142. package/dist/esm/cli/commands/init/host-visibility.js +94 -5
  143. package/dist/esm/cli/commands/init/index.d.ts +1 -0
  144. package/dist/esm/cli/commands/init/index.js +326 -173
  145. package/dist/esm/cli/commands/init/mcp-spec.d.ts +57 -0
  146. package/dist/esm/cli/commands/init/mcp-spec.js +273 -0
  147. package/dist/esm/cli/commands/init/merge.d.ts +1 -0
  148. package/dist/esm/cli/commands/init/merge.js +30 -1
  149. package/dist/esm/cli/commands/init/plugin-cache-prune.d.ts +0 -3
  150. package/dist/esm/cli/commands/init/plugin-cache-prune.js +0 -1
  151. package/dist/esm/cli/commands/init/scaffold-agents-md.d.ts +1 -1
  152. package/dist/esm/cli/commands/init/scaffold-agents-md.js +9 -3
  153. package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +11 -0
  154. package/dist/esm/cli/commands/init/scaffold-base-kit.js +56 -1
  155. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.d.ts +5 -6
  156. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.js +6 -13
  157. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.d.ts +14 -8
  158. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.js +41 -24
  159. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +2 -0
  160. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +51 -23
  161. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.d.ts +0 -2
  162. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.js +0 -2
  163. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.d.ts +6 -3
  164. package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.js +39 -26
  165. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +12 -31
  166. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +297 -173
  167. package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +7 -0
  168. package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +58 -0
  169. package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.d.ts +38 -0
  170. package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.js +73 -0
  171. package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.d.ts +21 -0
  172. package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.js +559 -63
  173. package/dist/esm/cli/commands/init/untracked-collision.d.ts +14 -0
  174. package/dist/esm/cli/commands/init/untracked-collision.js +42 -0
  175. package/dist/esm/cli/commands/logs.d.ts +15 -0
  176. package/dist/esm/cli/commands/logs.js +192 -3
  177. package/dist/esm/cli/commands/opencode-rotate.d.ts +49 -0
  178. package/dist/esm/cli/commands/opencode-rotate.js +135 -0
  179. package/dist/esm/cli/commands/public-readiness.d.ts +11 -0
  180. package/dist/esm/cli/commands/public-readiness.js +42 -0
  181. package/dist/esm/cli/commands/quality-log-store.d.ts +1 -0
  182. package/dist/esm/cli/commands/quality-log-store.js +187 -51
  183. package/dist/esm/cli/commands/review.d.ts +35 -3
  184. package/dist/esm/cli/commands/review.js +734 -217
  185. package/dist/esm/cli/commands/run-wait.d.ts +2 -0
  186. package/dist/esm/cli/commands/run-wait.js +122 -0
  187. package/dist/esm/cli/commands/secrets.d.ts +1 -0
  188. package/dist/esm/cli/commands/secrets.js +4 -1
  189. package/dist/esm/cli/commands/setup-help.js +1 -1
  190. package/dist/esm/cli/commands/setup-repair-help.js +1 -1
  191. package/dist/esm/cli/commands/worktree/router-dispatch.js +13 -8
  192. package/dist/esm/cli/direct-provider-launch.js +31 -3
  193. package/dist/esm/cli/optional-tool-freshness.d.ts +27 -0
  194. package/dist/esm/cli/optional-tool-freshness.js +219 -9
  195. package/dist/esm/daemon/domains/git-actions.d.ts +29 -0
  196. package/dist/esm/daemon/domains/git-actions.js +136 -0
  197. package/dist/esm/daemon/domains/run-registry-interop.d.ts +16 -0
  198. package/dist/esm/daemon/domains/run-registry-interop.js +45 -0
  199. package/dist/esm/daemon/domains/sessions-search.d.ts +11 -0
  200. package/dist/esm/daemon/domains/sessions-search.js +29 -0
  201. package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +5 -0
  202. package/dist/esm/daemon/handlers/blueprints-handlers.js +36 -0
  203. package/dist/esm/daemon/handlers/deploy-handlers.d.ts +6 -0
  204. package/dist/esm/daemon/handlers/deploy-handlers.js +5 -0
  205. package/dist/esm/daemon/handlers/fleet-handlers.d.ts +15 -0
  206. package/dist/esm/daemon/handlers/fleet-handlers.js +91 -0
  207. package/dist/esm/daemon/handlers/git-handlers.d.ts +6 -0
  208. package/dist/esm/daemon/handlers/git-handlers.js +27 -0
  209. package/dist/esm/daemon/handlers/product.d.ts +7 -0
  210. package/dist/esm/daemon/handlers/product.js +10 -119
  211. package/dist/esm/daemon/handlers/sessions-handlers.d.ts +12 -0
  212. package/dist/esm/daemon/handlers/sessions-handlers.js +31 -0
  213. package/dist/esm/daemon/handlers/shared.d.ts +5 -0
  214. package/dist/esm/daemon/handlers/shared.js +16 -0
  215. package/dist/esm/daemon/methods.d.ts +8 -1
  216. package/dist/esm/daemon/methods.js +7 -0
  217. package/dist/esm/daemon/protocol/contract.d.ts +43 -0
  218. package/dist/esm/daemon/protocol/contract.js +26 -0
  219. package/dist/esm/daemon/protocol/index.d.ts +1 -1
  220. package/dist/esm/daemon/protocol/index.js +1 -1
  221. package/dist/esm/hooks/__conformance__/matrix.d.ts +6 -0
  222. package/dist/esm/hooks/__conformance__/matrix.js +137 -0
  223. package/dist/esm/hooks/conformance/batch.js +5 -0
  224. package/dist/esm/hooks/doctor.d.ts +15 -0
  225. package/dist/esm/hooks/doctor.js +79 -8
  226. package/dist/esm/hooks/permission-request/index.js +9 -3
  227. package/dist/esm/hooks/permission-request/policy.d.ts +9 -0
  228. package/dist/esm/hooks/permission-request/policy.js +13 -0
  229. package/dist/esm/hooks/pretool-guard/dev-routing.d.ts +9 -0
  230. package/dist/esm/hooks/pretool-guard/dev-routing.js +41 -5
  231. package/dist/esm/hooks/pretool-guard/logger.d.ts +7 -0
  232. package/dist/esm/hooks/pretool-guard/logger.js +16 -0
  233. package/dist/esm/hooks/pretool-guard/runner.d.ts +6 -0
  234. package/dist/esm/hooks/pretool-guard/runner.js +68 -16
  235. package/dist/esm/hooks/pretool-guard/validators/blueprint.js +30 -10
  236. package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +58 -4
  237. package/dist/esm/hooks/pretool-guard/validators/index.d.ts +7 -0
  238. package/dist/esm/hooks/pretool-guard/validators/index.js +21 -0
  239. package/dist/esm/hooks/pretool-guard/validators/mcp-redirect.d.ts +0 -1
  240. package/dist/esm/hooks/pretool-guard/validators/mcp-redirect.js +8 -5
  241. package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +19 -0
  242. package/dist/esm/hooks/pretool-guard/validators/path-contract.js +25 -8
  243. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.d.ts +9 -7
  244. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +126 -14
  245. package/dist/esm/hooks/shared/guard-outcome.d.ts +149 -0
  246. package/dist/esm/hooks/shared/guard-outcome.js +215 -0
  247. package/dist/esm/hooks/shared/hook-bootstrap.d.ts +26 -0
  248. package/dist/esm/hooks/shared/hook-bootstrap.js +64 -1
  249. package/dist/esm/hooks/shared/types.d.ts +8 -5
  250. package/dist/esm/hooks/shared/types.js +23 -8
  251. package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
  252. package/dist/esm/hooks/stop/qa-changed-files.js +74 -0
  253. package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
  254. package/dist/esm/mcp/blueprint/handlers/document-mutations.js +78 -4
  255. package/dist/esm/mcp/blueprint/handlers/review-log.js +8 -1
  256. package/dist/esm/mcp/blueprint/handlers/task-advance.js +113 -54
  257. package/dist/esm/mcp/blueprint/handlers/task-verify.js +39 -19
  258. package/dist/esm/mcp/blueprint/registration.js +3 -2
  259. package/dist/esm/mcp/server.d.ts +1 -0
  260. package/dist/esm/mcp/server.js +27 -0
  261. package/dist/esm/mcp/tools/_names.d.ts +1 -1
  262. package/dist/esm/mcp/tools/_names.js +5 -0
  263. package/dist/esm/mcp/tools/_registry.js +10 -0
  264. package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
  265. package/dist/esm/mcp/tools/_shared/result.js +44 -1
  266. package/dist/esm/mcp/tools/audit.d.ts +5 -1
  267. package/dist/esm/mcp/tools/audit.js +40 -0
  268. package/dist/esm/mcp/tools/audits.d.ts +4 -0
  269. package/dist/esm/mcp/tools/ci-act.js +9 -11
  270. package/dist/esm/mcp/tools/ci-preflight.d.ts +50 -0
  271. package/dist/esm/mcp/tools/ci-preflight.js +183 -0
  272. package/dist/esm/mcp/tools/pr-upsert.js +39 -1
  273. package/dist/esm/mcp/tools/review-run.d.ts +28 -0
  274. package/dist/esm/mcp/tools/review-run.js +216 -0
  275. package/dist/esm/mcp/tools/run-wait.d.ts +86 -0
  276. package/dist/esm/mcp/tools/run-wait.js +593 -0
  277. package/dist/esm/mcp/tools/session-id.d.ts +3 -0
  278. package/dist/esm/mcp/tools/session-id.js +45 -0
  279. package/dist/esm/mcp/tools/session-info.d.ts +3 -0
  280. package/dist/esm/mcp/tools/session-info.js +49 -0
  281. package/dist/esm/output-transforms/audit.d.ts +23 -0
  282. package/dist/esm/output-transforms/audit.js +47 -0
  283. package/dist/esm/output-transforms/index.js +2 -0
  284. package/dist/esm/paths/state-root.js +3 -0
  285. package/dist/esm/review/authority.js +30 -7
  286. package/dist/esm/review/availability.d.ts +22 -0
  287. package/dist/esm/review/availability.js +123 -23
  288. package/dist/esm/review/delivery-verifier.d.ts +27 -0
  289. package/dist/esm/review/delivery-verifier.js +115 -0
  290. package/dist/esm/review/events.d.ts +3 -2
  291. package/dist/esm/review/events.js +23 -3
  292. package/dist/esm/review/execution/adapters.js +26 -9
  293. package/dist/esm/review/execution/output-policy.d.ts +2 -0
  294. package/dist/esm/review/execution/output-policy.js +12 -0
  295. package/dist/esm/review/execution/review-checkout.js +87 -6
  296. package/dist/esm/review/execution/supervisor.js +5 -0
  297. package/dist/esm/review/execution/types.d.ts +8 -0
  298. package/dist/esm/review/opencode-account.d.ts +24 -0
  299. package/dist/esm/review/opencode-account.js +68 -0
  300. package/dist/esm/review/opencode-auth.d.ts +33 -0
  301. package/dist/esm/review/opencode-auth.js +79 -0
  302. package/dist/esm/review/subject.d.ts +27 -0
  303. package/dist/esm/review/subject.js +179 -32
  304. package/dist/esm/runtime/executor.d.ts +1 -0
  305. package/dist/esm/runtime/executor.js +2 -1
  306. package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
  307. package/dist/esm/runtime/opencode-account-materializer.js +33 -0
  308. package/dist/esm/secret-gate/runner.d.ts +1 -0
  309. package/dist/esm/secret-gate/runner.js +4 -1
  310. package/dist/esm/status/snapshot.js +30 -19
  311. package/dist/esm/test/duration-sequencer.d.ts +20 -0
  312. package/dist/esm/test/duration-sequencer.js +54 -0
  313. package/dist/esm/test/shard-durations.json +922 -0
  314. package/dist/esm/test/worker-budget.d.ts +6 -0
  315. package/dist/esm/test/worker-budget.js +59 -1
  316. package/dist/esm/test-helpers/global-setup.d.ts +1 -1
  317. package/dist/esm/test-helpers/global-setup.js +68 -24
  318. package/dist/esm/ultragoal/service.d.ts +7 -0
  319. package/dist/esm/ultragoal/service.js +42 -2
  320. package/dist/esm/utils/write-json-file.js +3 -1
  321. package/dist/esm/worktrees/codex-project-trust.d.ts +15 -0
  322. package/dist/esm/worktrees/codex-project-trust.js +56 -0
  323. package/dist/esm/worktrees/current.d.ts +1 -0
  324. package/dist/esm/worktrees/current.js +1 -0
  325. package/dist/esm/worktrees/project-agent-surfaces.d.ts +1 -0
  326. package/dist/esm/worktrees/project-agent-surfaces.js +2 -1
  327. package/dist/esm/worktrees/registry.d.ts +8 -0
  328. package/dist/esm/worktrees/registry.js +18 -3
  329. package/package.json +18 -15
@@ -0,0 +1,14 @@
1
+ import type { ConvergeUnit } from "./convergence.js";
2
+ export type CollisionResolution = "none" | "tracked" | "removed-identical" | "removed-owned";
3
+ export interface CollisionResult {
4
+ readonly ok: boolean;
5
+ readonly resolution?: CollisionResolution;
6
+ readonly error?: string;
7
+ }
8
+ export interface ResolveCollisionOptions {
9
+ readonly unit: ConvergeUnit;
10
+ readonly desiredContent: string | Uint8Array;
11
+ /** Compatibility input only. Unknown content remains protected when true. */
12
+ readonly force?: boolean;
13
+ }
14
+ export declare function resolveUntrackedCollision(options: ResolveCollisionOptions): CollisionResult;
@@ -0,0 +1,42 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, lstatSync, readFileSync, rmSync } from "node:fs";
3
+ const GIT_TIMEOUT_MS = 15_000;
4
+ const OWNERSHIP_MARKER = /^\uFEFF?[ \t]*(?:#|\/\/|\/\*|<!--|")?[ \t]*(?:>>> managed by webpresso\b|Generated by webpresso\b)/iu;
5
+ function git(repoRoot, args) {
6
+ const result = spawnSync("git", [...args], {
7
+ cwd: repoRoot,
8
+ encoding: "utf8",
9
+ timeout: GIT_TIMEOUT_MS,
10
+ maxBuffer: 1024 * 1024,
11
+ stdio: ["ignore", "pipe", "pipe"],
12
+ });
13
+ return { ok: result.status === 0 && !result.error };
14
+ }
15
+ export function resolveUntrackedCollision(options) {
16
+ const { unit } = options;
17
+ if (!existsSync(unit.targetPath))
18
+ return { ok: true, resolution: "none" };
19
+ if (git(unit.repoRoot, ["ls-files", "--error-unmatch", "--", unit.gitPath]).ok) {
20
+ return { ok: true, resolution: "tracked" };
21
+ }
22
+ if (!lstatSync(unit.targetPath).isFile()) {
23
+ return {
24
+ ok: false,
25
+ error: `Untracked collision at ${unit.gitPath} is not a regular file; move it aside and rerun wp setup.`,
26
+ };
27
+ }
28
+ const current = readFileSync(unit.targetPath);
29
+ if (current.equals(Buffer.from(options.desiredContent))) {
30
+ rmSync(unit.targetPath);
31
+ return { ok: true, resolution: "removed-identical" };
32
+ }
33
+ if (OWNERSHIP_MARKER.test(current.toString("utf8"))) {
34
+ rmSync(unit.targetPath);
35
+ return { ok: true, resolution: "removed-owned" };
36
+ }
37
+ return {
38
+ ok: false,
39
+ error: `Unknown untracked content blocks ${unit.gitPath}; it was preserved${options.force ? " despite --force" : ""}. ` +
40
+ "Move or commit the file, then rerun wp setup.",
41
+ };
42
+ }
@@ -1,3 +1,18 @@
1
1
  import type { CAC } from "cac";
2
+ interface LogsCommandOptions {
3
+ readonly follow?: boolean;
4
+ readonly tail?: string | number | boolean;
5
+ readonly watch?: boolean;
6
+ }
7
+ interface FollowLogFileOptions {
8
+ readonly pollIntervalMs?: number;
9
+ readonly signal?: AbortSignal;
10
+ readonly write?: (chunk: string) => void;
11
+ }
2
12
  export declare const LOGS_COMMAND_HELP: string;
3
13
  export declare function registerLogsCommand(cli: CAC): void;
14
+ export declare function parseTailLineCount(value: LogsCommandOptions["tail"]): number | null;
15
+ export declare function tailLogContent(content: string, lineCount: number): string;
16
+ export declare function readTailLogContent(logPath: string, lineCount: number, chunkSize?: number): string;
17
+ export declare function followLogFile(logPath: string, startOffset: number, options?: FollowLogFileOptions): Promise<number>;
18
+ export {};
@@ -1,16 +1,22 @@
1
- import { readFileSync } from "node:fs";
1
+ import { closeSync, createReadStream, openSync, readFileSync, readSync, statSync, unwatchFile, watchFile, } from "node:fs";
2
2
  import { CLI_LOG_COMMANDS, isCliLogCommandName, readCliLogEntry } from "./quality-log-store.js";
3
+ const TAIL_READ_CHUNK_BYTES = 64 * 1024;
3
4
  export const LOGS_COMMAND_HELP = [
4
5
  "Print persisted raw output for a recent summary-first quality command run.",
5
6
  "",
6
7
  "Examples:",
7
8
  " wp logs test",
8
9
  " wp logs qa 2",
10
+ " wp logs test --tail 200",
11
+ " wp logs test --tail 200 --follow",
9
12
  ].join("\n");
10
13
  export function registerLogsCommand(cli) {
11
14
  cli
12
15
  .command("logs <command> [n]", LOGS_COMMAND_HELP)
13
- .action((command, n) => {
16
+ .option("--follow", "Keep printing appended output from the selected log")
17
+ .option("--tail <lines>", "Print only the last N lines from the selected log")
18
+ .option("--watch", "Alias for --follow")
19
+ .action((command, n, options = {}) => {
14
20
  if (!isCliLogCommandName(command)) {
15
21
  console.error(`Unknown logs command: ${command}. Expected one of: ${CLI_LOG_COMMANDS.join(", ")}`);
16
22
  return 1;
@@ -20,12 +26,195 @@ export function registerLogsCommand(cli) {
20
26
  console.error("Usage: wp logs <command> [n]\n`n` must be an integer in the range 1..10.");
21
27
  return 1;
22
28
  }
29
+ const tailLines = parseTailLineCount(options.tail);
30
+ if (tailLines !== null && tailLines < 1) {
31
+ console.error("Usage: wp logs <command> [n] [--tail <lines>]\n`tail` must be a positive integer.");
32
+ return 1;
33
+ }
23
34
  const entry = readCliLogEntry(command, ordinal);
24
35
  if (!entry) {
25
36
  console.log(`No logs yet for ${command}.`);
26
37
  return 0;
27
38
  }
28
- process.stdout.write(readFileSync(entry.logPath, "utf8"));
39
+ const snapshot = tailLines === null
40
+ ? readFullLogSnapshot(entry.logPath)
41
+ : readTailLogSnapshot(entry.logPath, tailLines);
42
+ const printed = snapshot.content;
43
+ process.stdout.write(printed);
44
+ if (options.follow === true || options.watch === true) {
45
+ return followLogFile(entry.logPath, snapshot.offset);
46
+ }
29
47
  return 0;
30
48
  });
31
49
  }
50
+ export function parseTailLineCount(value) {
51
+ if (value === undefined || value === null || value === false)
52
+ return null;
53
+ if (value === true)
54
+ return 0;
55
+ const count = typeof value === "number" ? value : Number(value);
56
+ if (!Number.isInteger(count) || count < 1)
57
+ return 0;
58
+ return count;
59
+ }
60
+ export function tailLogContent(content, lineCount) {
61
+ const lines = content.split("\n");
62
+ const trailingNewline = lines.at(-1) === "";
63
+ const body = trailingNewline ? lines.slice(0, -1) : lines;
64
+ const selected = body.slice(-lineCount);
65
+ if (selected.length === 0)
66
+ return "";
67
+ return `${selected.join("\n")}${trailingNewline ? "\n" : ""}`;
68
+ }
69
+ export function readTailLogContent(logPath, lineCount, chunkSize = TAIL_READ_CHUNK_BYTES) {
70
+ return readTailLogSnapshot(logPath, lineCount, chunkSize).content;
71
+ }
72
+ function readFullLogSnapshot(logPath) {
73
+ const content = readFileSync(logPath, "utf8");
74
+ return { content, offset: Buffer.byteLength(content) };
75
+ }
76
+ function readTailLogSnapshot(logPath, lineCount, chunkSize = TAIL_READ_CHUNK_BYTES) {
77
+ const fileSize = statSync(logPath).size;
78
+ if (fileSize === 0 || lineCount < 1)
79
+ return { content: "", offset: fileSize };
80
+ const fd = openSync(logPath, "r");
81
+ try {
82
+ const hasTrailingNewline = readLastByte(fd, fileSize) === 0x0a;
83
+ const targetNewlines = hasTrailingNewline ? lineCount + 1 : lineCount;
84
+ const chunks = [];
85
+ let position = fileSize;
86
+ let newlineCount = 0;
87
+ while (position > 0 && newlineCount < targetNewlines) {
88
+ const bytesToRead = Math.min(chunkSize, position);
89
+ position -= bytesToRead;
90
+ const buffer = Buffer.allocUnsafe(bytesToRead);
91
+ const bytesRead = readSync(fd, buffer, 0, bytesToRead, position);
92
+ const chunk = buffer.subarray(0, bytesRead);
93
+ chunks.unshift(chunk);
94
+ newlineCount += countNewlines(chunk);
95
+ }
96
+ return {
97
+ content: tailLogContent(Buffer.concat(chunks).toString("utf8"), lineCount),
98
+ offset: fileSize,
99
+ };
100
+ }
101
+ finally {
102
+ closeSync(fd);
103
+ }
104
+ }
105
+ function readLastByte(fd, fileSize) {
106
+ if (fileSize < 1)
107
+ return null;
108
+ const buffer = Buffer.allocUnsafe(1);
109
+ const bytesRead = readSync(fd, buffer, 0, 1, fileSize - 1);
110
+ return bytesRead === 1 ? buffer.readUInt8(0) : null;
111
+ }
112
+ function countNewlines(buffer) {
113
+ let count = 0;
114
+ for (const byte of buffer) {
115
+ if (byte === 0x0a)
116
+ count++;
117
+ }
118
+ return count;
119
+ }
120
+ export function followLogFile(logPath, startOffset, options = {}) {
121
+ if (options.signal?.aborted === true)
122
+ return Promise.resolve(0);
123
+ let offset = startOffset;
124
+ let pendingSize = startOffset;
125
+ let reading = false;
126
+ let readGeneration = 0;
127
+ let settled = false;
128
+ let activeStream = null;
129
+ let poll = null;
130
+ const write = options.write ??
131
+ ((chunk) => {
132
+ process.stdout.write(chunk);
133
+ });
134
+ let resolvePromise = null;
135
+ const settle = () => {
136
+ if (settled)
137
+ return;
138
+ settled = true;
139
+ unwatchFile(logPath, onChange);
140
+ activeStream?.destroy();
141
+ if (poll !== null)
142
+ clearInterval(poll);
143
+ process.off("SIGINT", onSignal);
144
+ process.off("SIGTERM", onSignal);
145
+ options.signal?.removeEventListener("abort", onSignal);
146
+ resolvePromise?.(0);
147
+ };
148
+ const onSignal = () => {
149
+ settle();
150
+ };
151
+ const drain = () => {
152
+ if (settled || reading || pendingSize <= offset)
153
+ return;
154
+ reading = true;
155
+ const nextOffset = pendingSize;
156
+ const generation = readGeneration;
157
+ const stream = createReadStream(logPath, {
158
+ encoding: "utf8",
159
+ start: offset,
160
+ end: nextOffset - 1,
161
+ });
162
+ activeStream = stream;
163
+ stream.on("data", (chunk) => {
164
+ if (settled)
165
+ return;
166
+ write(String(chunk));
167
+ });
168
+ stream.on("close", () => {
169
+ if (activeStream === stream)
170
+ activeStream = null;
171
+ if (settled || generation !== readGeneration)
172
+ return;
173
+ offset = nextOffset;
174
+ reading = false;
175
+ drain();
176
+ });
177
+ stream.on("error", onSignal);
178
+ };
179
+ const onChange = (current) => {
180
+ if (settled)
181
+ return;
182
+ if (current.size < pendingSize) {
183
+ readGeneration++;
184
+ activeStream?.destroy();
185
+ activeStream = null;
186
+ reading = false;
187
+ offset = 0;
188
+ pendingSize = current.size;
189
+ drain();
190
+ return;
191
+ }
192
+ if (current.size <= pendingSize)
193
+ return;
194
+ pendingSize = current.size;
195
+ drain();
196
+ };
197
+ return new Promise((resolve) => {
198
+ resolvePromise = resolve;
199
+ process.once("SIGINT", onSignal);
200
+ process.once("SIGTERM", onSignal);
201
+ options.signal?.addEventListener("abort", onSignal, { once: true });
202
+ watchFile(logPath, { interval: options.pollIntervalMs ?? 250 }, onChange);
203
+ poll = setInterval(() => {
204
+ if (settled)
205
+ return;
206
+ try {
207
+ onChange(statSync(logPath));
208
+ }
209
+ catch (error) {
210
+ if (isMissingFileError(error))
211
+ return;
212
+ onSignal();
213
+ }
214
+ }, options.pollIntervalMs ?? 250);
215
+ onChange(statSync(logPath));
216
+ });
217
+ }
218
+ function isMissingFileError(error) {
219
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
220
+ }
@@ -0,0 +1,49 @@
1
+ import { resolveOpencodeAccountsGracefully } from "#runtime/opencode-account-materializer.js";
2
+ /** The `--wp-rotate` flag, namespaced like `--wp-permission-mode`. */
3
+ export declare const OPENCODE_ROTATE_FLAG = "--wp-rotate";
4
+ export type OpencodeRotateOutcome =
5
+ /** The previously-active account was cooled and a different one is now active. */
6
+ {
7
+ readonly kind: "rotated";
8
+ readonly cooledAccountId: string;
9
+ readonly activeAccountId: string;
10
+ readonly activeIsCooled: boolean;
11
+ readonly waitMs?: number;
12
+ }
13
+ /** The on-disk key matched no known account, so nothing was cooled. */
14
+ | {
15
+ readonly kind: "reselected";
16
+ readonly activeAccountId: string;
17
+ readonly activeIsCooled: boolean;
18
+ }
19
+ /** Fewer than two accounts are provisioned — there is nowhere to rotate to. */
20
+ | {
21
+ readonly kind: "single-account";
22
+ }
23
+ /** Secrets could not be resolved; the caller must still launch normally. */
24
+ | {
25
+ readonly kind: "unavailable";
26
+ readonly reason: string;
27
+ };
28
+ export interface OpencodeRotateDeps {
29
+ readonly resolveOpencodeAccounts?: typeof resolveOpencodeAccountsGracefully;
30
+ readonly readActiveKey?: () => string | undefined;
31
+ readonly materializeGlobalOpencodeAuth?: (key: string) => void;
32
+ readonly recordCooldown?: (canonicalId: string) => Promise<unknown>;
33
+ }
34
+ /**
35
+ * Cool the currently-materialized opencode-go account and materialize the next
36
+ * usable one into the global `auth.json`.
37
+ *
38
+ * The active account is identified by reading the global store back and matching
39
+ * its key against the resolved accounts — `wp` wrote that value itself, so this
40
+ * stays correct without persisting a separate "last launched" record. When the
41
+ * key matches nothing (a hand-edited store, or a fresh machine), the rotation
42
+ * degrades to a plain reselect and cools nothing, because cooling an account we
43
+ * cannot identify would suppress the wrong one.
44
+ *
45
+ * Never throws: the caller launches the TUI regardless of the outcome.
46
+ */
47
+ export declare function rotateActiveOpencodeAccount(cwd: string, secretProfile: string, deps?: OpencodeRotateDeps): Promise<OpencodeRotateOutcome>;
48
+ /** Human-readable lines describing a rotation outcome, in print order. */
49
+ export declare function describeOpencodeRotate(outcome: OpencodeRotateOutcome): readonly string[];
@@ -0,0 +1,135 @@
1
+ import { loadOpencodeAccountAvailability, pruneExpiredOpencodeAccountSuppressions, recordOpencodeAccountAvailabilityWithLock, } from "#review/availability.js";
2
+ import { opencodeAccountCanonicalId, selectActiveOpencodeAccount, } from "#review/opencode-account.js";
3
+ import { materializeOpencodeGoAuth, readOpencodeGoKey } from "#review/opencode-auth.js";
4
+ import { resolveOpencodeAccountsGracefully } from "#runtime/opencode-account-materializer.js";
5
+ /** The `--wp-rotate` flag, namespaced like `--wp-permission-mode`. */
6
+ export const OPENCODE_ROTATE_FLAG = "--wp-rotate";
7
+ /**
8
+ * A manual rotation has no provider response to parse a reset window from, so it
9
+ * assumes the shortest real OpenCode Go window (5 hours). Recording no window at
10
+ * all would leave the record permanently suppressed — `suppressionExpired` only
11
+ * clears a record that carries `retryAfterMs` — so every rotated account would
12
+ * be cooled forever and selection would degrade to "first account" again. A
13
+ * too-short guess is self-correcting: the account is retried, and if it is still
14
+ * capped the gate (or the next rotation) simply cools it again.
15
+ */
16
+ const MANUAL_ROTATION_COOLDOWN_MS = 5 * 60 * 60 * 1_000;
17
+ function coolActiveAccount(canonicalId, deps) {
18
+ if (deps.recordCooldown)
19
+ return deps.recordCooldown(canonicalId);
20
+ // Same event shape the review gate records on an in-flight usage limit, so
21
+ // both writers produce identical, mutually-readable cooldown records.
22
+ return recordOpencodeAccountAvailabilityWithLock(canonicalId, {
23
+ failureCode: "opencode-go-usage-limit",
24
+ terminalClassification: "provider-limit-reached",
25
+ limitWindow: "unknown",
26
+ retryAfterMs: MANUAL_ROTATION_COOLDOWN_MS,
27
+ });
28
+ }
29
+ /**
30
+ * Pick the account to make active next.
31
+ *
32
+ * `excludeId` is the account we just cooled. It is filtered out here rather
33
+ * than relied upon coming back suppressed from the store: that keeps rotation
34
+ * correct even if the cooldown write is slow, contended, or fails outright, and
35
+ * removes a read-after-write dependency from the hot path.
36
+ */
37
+ function selectNextAccount(accounts, excludeId) {
38
+ const pruned = pruneExpiredOpencodeAccountSuppressions(loadOpencodeAccountAvailability());
39
+ const candidates = excludeId === undefined ? accounts : accounts.filter((account) => account.id !== excludeId);
40
+ // Callers guarantee at least one candidate: rotation needs >= 2 accounts, and
41
+ // the no-exclude path is only reached with a non-empty list.
42
+ const pool = candidates.length > 0 ? candidates : accounts;
43
+ const selection = selectActiveOpencodeAccount(pool, pruned.state);
44
+ return selection ?? { account: pool[0], cooled: false };
45
+ }
46
+ /**
47
+ * Cool the currently-materialized opencode-go account and materialize the next
48
+ * usable one into the global `auth.json`.
49
+ *
50
+ * The active account is identified by reading the global store back and matching
51
+ * its key against the resolved accounts — `wp` wrote that value itself, so this
52
+ * stays correct without persisting a separate "last launched" record. When the
53
+ * key matches nothing (a hand-edited store, or a fresh machine), the rotation
54
+ * degrades to a plain reselect and cools nothing, because cooling an account we
55
+ * cannot identify would suppress the wrong one.
56
+ *
57
+ * Never throws: the caller launches the TUI regardless of the outcome.
58
+ */
59
+ export async function rotateActiveOpencodeAccount(cwd, secretProfile, deps = {}) {
60
+ try {
61
+ return await rotateOrThrow(cwd, secretProfile, deps);
62
+ }
63
+ catch (error) {
64
+ // The cooldown write takes the user-scope lock the review gate also holds
65
+ // (contention rejects after a few short retries) and the auth write can fail
66
+ // with EACCES/ENOSPC. Neither may abort the launch, so both degrade to the
67
+ // same "carry on and launch" outcome the sibling select path uses.
68
+ return { kind: "unavailable", reason: error instanceof Error ? error.message : String(error) };
69
+ }
70
+ }
71
+ async function rotateOrThrow(cwd, secretProfile, deps) {
72
+ const resolution = (deps.resolveOpencodeAccounts ?? resolveOpencodeAccountsGracefully)({
73
+ cwd,
74
+ secretProfile,
75
+ });
76
+ if (!resolution.ok)
77
+ return { kind: "unavailable", reason: resolution.reason };
78
+ const accounts = resolution.accounts;
79
+ if (accounts.length < 2)
80
+ return { kind: "single-account" };
81
+ const activeKey = (deps.readActiveKey ?? (() => readOpencodeGoKey()))();
82
+ const current = accounts.find((account) => account.key === activeKey);
83
+ const materialize = deps.materializeGlobalOpencodeAuth ?? ((key) => void materializeOpencodeGoAuth(key));
84
+ if (!current) {
85
+ const next = selectNextAccount(accounts);
86
+ materialize(next.account.key);
87
+ return { kind: "reselected", activeAccountId: next.account.id, activeIsCooled: next.cooled };
88
+ }
89
+ await coolActiveAccount(opencodeAccountCanonicalId(current.id), deps);
90
+ const next = selectNextAccount(accounts, current.id);
91
+ materialize(next.account.key);
92
+ return {
93
+ kind: "rotated",
94
+ cooledAccountId: current.id,
95
+ activeAccountId: next.account.id,
96
+ activeIsCooled: next.cooled,
97
+ ...(next.waitMs === undefined ? {} : { waitMs: next.waitMs }),
98
+ };
99
+ }
100
+ function formatWait(waitMs) {
101
+ if (waitMs === undefined)
102
+ return "";
103
+ const hours = waitMs / 3_600_000;
104
+ if (hours >= 24)
105
+ return ` (resets in ~${Math.ceil(hours / 24)}d)`;
106
+ if (hours >= 1)
107
+ return ` (resets in ~${Math.ceil(hours)}h)`;
108
+ return ` (resets in ~${Math.max(1, Math.ceil(waitMs / 60_000))}m)`;
109
+ }
110
+ /** Human-readable lines describing a rotation outcome, in print order. */
111
+ export function describeOpencodeRotate(outcome) {
112
+ switch (outcome.kind) {
113
+ case "unavailable":
114
+ return [`wp opencode: could not resolve OpenCode Go accounts (${outcome.reason}).`];
115
+ case "single-account":
116
+ return [
117
+ "wp opencode: only one OpenCode Go account is configured; nothing to rotate to. Add OPENCODE_GO_KEY_<ID> secrets to enable rotation.",
118
+ ];
119
+ case "reselected":
120
+ return [
121
+ "wp opencode: the active key matches no configured account, so nothing was cooled.",
122
+ `wp opencode: now active -> opencode-go:${outcome.activeAccountId}`,
123
+ ];
124
+ case "rotated": {
125
+ const lines = [
126
+ `wp opencode: cooled opencode-go:${outcome.cooledAccountId} (usage limit)`,
127
+ `wp opencode: now active -> opencode-go:${outcome.activeAccountId}`,
128
+ ];
129
+ if (outcome.activeIsCooled) {
130
+ lines.push(`wp opencode: every account is within its cooldown; using the soonest-expiring one${formatWait(outcome.waitMs)}. Run again after it resets, or add another OPENCODE_GO_KEY_<ID>.`);
131
+ }
132
+ return lines;
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,11 @@
1
+ import type { CAC } from "cac";
2
+ export declare const PUBLIC_READINESS_COMMAND_HELP: string;
3
+ export interface PublicReadinessDeps {
4
+ readonly cwd?: string;
5
+ readonly resolveRepoRoot?: (cwd: string) => string | null;
6
+ readonly hasReadinessScript?: (repoRoot: string) => boolean;
7
+ readonly run?: (repoRoot: string) => number;
8
+ readonly stderr?: Pick<typeof process.stderr, "write">;
9
+ }
10
+ export declare function runPublicReadiness(deps?: PublicReadinessDeps): number;
11
+ export declare function registerPublicReadinessCommand(cli: CAC): void;
@@ -0,0 +1,42 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { getGitTopLevel } from "#git/changed-files";
5
+ export const PUBLIC_READINESS_COMMAND_HELP = [
6
+ "Run the public package readiness gate.",
7
+ "",
8
+ "Delegates to scripts/public-readiness.ts, preserving that script as the",
9
+ "single owner of package and repository visibility readiness checks.",
10
+ "",
11
+ "Examples:",
12
+ " wp public:readiness",
13
+ ].join("\n");
14
+ function defaultRunPublicReadiness(repoRoot) {
15
+ const result = spawnSync("bun", ["scripts/public-readiness.ts"], {
16
+ cwd: repoRoot,
17
+ stdio: "inherit",
18
+ env: process.env,
19
+ });
20
+ if (result.error) {
21
+ process.stderr.write(`${result.error.message}\n`);
22
+ return 1;
23
+ }
24
+ return typeof result.status === "number" ? result.status : 1;
25
+ }
26
+ export function runPublicReadiness(deps = {}) {
27
+ const cwd = deps.cwd ?? process.cwd();
28
+ const stderr = deps.stderr ?? process.stderr;
29
+ const resolveRepoRoot = deps.resolveRepoRoot ?? ((current) => getGitTopLevel(current));
30
+ const hasReadinessScript = deps.hasReadinessScript ??
31
+ ((repoRoot) => existsSync(path.join(repoRoot, "scripts", "public-readiness.ts")));
32
+ const run = deps.run ?? defaultRunPublicReadiness;
33
+ const repoRoot = resolveRepoRoot(cwd) ?? cwd;
34
+ if (!hasReadinessScript(repoRoot)) {
35
+ stderr.write(`wp public:readiness: scripts/public-readiness.ts is missing at ${repoRoot}.\n`);
36
+ return 1;
37
+ }
38
+ return run(repoRoot);
39
+ }
40
+ export function registerPublicReadinessCommand(cli) {
41
+ cli.command("public:readiness", PUBLIC_READINESS_COMMAND_HELP).action(() => runPublicReadiness());
42
+ }
@@ -6,6 +6,7 @@ export interface CliLogEntry {
6
6
  readonly timestamp: string;
7
7
  readonly exitCode: number;
8
8
  readonly logPath: string;
9
+ readonly active?: boolean;
9
10
  readonly options?: Record<string, unknown>;
10
11
  readonly summary?: string;
11
12
  }