@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.

Potentially problematic release.


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

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
@@ -0,0 +1,230 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ import { isProcessAlive } from "#cli/auto-update/install-lock.js";
5
+ import { writeFileAtomicStrict } from "#shared-utils/write-json-file.js";
6
+ const LOCK_TIMEOUT_MS = 5_000;
7
+ const LOCK_POLL_MS = 25;
8
+ const LOCK_STALE_MS = 30_000;
9
+ /**
10
+ * Hard-age backstop: past this, a stale lock is reclaimed regardless of owner
11
+ * liveness. Prevents a recycled pid — or a `kill(pid,0)` that fails safe to
12
+ * "alive" (EPERM / unknown / cross-PID-namespace) — from wedging the lock
13
+ * forever. Mirrors `isInstallLockActive`, which likewise pairs liveness with an
14
+ * age bound rather than trusting liveness alone. Also serves as the reaper's own
15
+ * stale bound. Not a new deadline: it replaces the prior reaper-stale constant.
16
+ */
17
+ const LOCK_STALE_HARD_MS = 5 * 60_000;
18
+ const LOCK_OWNER_FILE = "owner";
19
+ function sleepSync(ms) {
20
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
21
+ }
22
+ function isErrno(error, code) {
23
+ return error instanceof Error && "code" in error && error.code === code;
24
+ }
25
+ /** Directory age in ms, or `undefined` when the directory is gone (ENOENT). */
26
+ function directoryAgeMs(path, now) {
27
+ try {
28
+ return now - statSync(path).mtime.getTime();
29
+ }
30
+ catch (error) {
31
+ if (isErrno(error, "ENOENT"))
32
+ return undefined;
33
+ throw error;
34
+ }
35
+ }
36
+ /**
37
+ * Read the lock's owner record. The token is the full `"<pid>:<uuid>"` string
38
+ * (identical to what release compares); `pid` is its leading integer. Returns
39
+ * `undefined` for a missing/empty/unparseable owner — i.e. a crash that never
40
+ * finished writing the record, which is reclaimable once age-stale.
41
+ */
42
+ function readLockOwner(lockPath) {
43
+ let token;
44
+ try {
45
+ token = readFileSync(`${lockPath}/${LOCK_OWNER_FILE}`, "utf8");
46
+ }
47
+ catch {
48
+ return undefined;
49
+ }
50
+ const separator = token.indexOf(":");
51
+ if (separator <= 0)
52
+ return undefined;
53
+ const pid = Number(token.slice(0, separator));
54
+ if (!Number.isInteger(pid) || pid <= 0)
55
+ return undefined;
56
+ return { pid, token };
57
+ }
58
+ function tryAcquireDirectory(path) {
59
+ try {
60
+ mkdirSync(path);
61
+ return true;
62
+ }
63
+ catch (error) {
64
+ if (isErrno(error, "EEXIST"))
65
+ return false;
66
+ throw error;
67
+ }
68
+ }
69
+ /**
70
+ * Serialize stale reclaim across contenders. A crashed reaper holder is itself
71
+ * reclaimed race-free: exactly one waiter's atomic rename of the reaper carcass
72
+ * succeeds; losers observe ENOENT and retry the mkdir. This is what stops two
73
+ * reclaimers from deleting each other's fresh lock — the original lost-update.
74
+ *
75
+ * Release is owner-token-checked, exactly like the main lock: if a paused reaper
76
+ * holder is stolen and replaced while it sleeps, its release sees a mismatched
77
+ * owner token and no-ops, so it cannot delete the successor's reaper and let a
78
+ * third reclaimer in behind it.
79
+ */
80
+ function acquireReaperLock(path, deadline, now) {
81
+ const ownerPath = `${path}/${LOCK_OWNER_FILE}`;
82
+ const ownerToken = `${process.pid}:${randomUUID()}`;
83
+ while (!tryAcquireDirectory(path)) {
84
+ const ageMs = directoryAgeMs(path, now());
85
+ if (ageMs !== undefined && ageMs >= LOCK_STALE_HARD_MS) {
86
+ const carcass = `${path}.stale-${process.pid}-${randomUUID()}`;
87
+ try {
88
+ renameSync(path, carcass);
89
+ rmSync(carcass, { recursive: true, force: true });
90
+ }
91
+ catch (error) {
92
+ // A concurrent waiter won the atomic rename; retry the mkdir.
93
+ if (!isErrno(error, "ENOENT"))
94
+ throw error;
95
+ }
96
+ continue;
97
+ }
98
+ if (now() >= deadline) {
99
+ throw new Error(`Timed out waiting for stale host config lock reaper: ${path}`);
100
+ }
101
+ sleepSync(LOCK_POLL_MS);
102
+ }
103
+ try {
104
+ writeFileSync(ownerPath, ownerToken, "utf8");
105
+ }
106
+ catch (error) {
107
+ // Mirror the main lock: don't leave a reaper others must wait out on an
108
+ // owner-write failure.
109
+ rmSync(path, { recursive: true, force: true });
110
+ throw error;
111
+ }
112
+ return () => {
113
+ try {
114
+ if (readFileSync(ownerPath, "utf8") !== ownerToken)
115
+ return;
116
+ rmSync(path, { recursive: true, force: true });
117
+ }
118
+ catch (error) {
119
+ if (!isErrno(error, "ENOENT"))
120
+ throw error;
121
+ }
122
+ };
123
+ }
124
+ /**
125
+ * Remove a stale lock while holding the reaper, so at most one reclaimer acts at
126
+ * a time. Re-checks under the reaper: only a lock that is still age-stale AND
127
+ * whose owner is absent, dead, or past the hard-age bound is deleted. A live
128
+ * owner within the hard-age window is never reclaimed, so a slow-but-alive
129
+ * writer cannot lose its lock to a successor.
130
+ */
131
+ function reclaimStaleLock(lockPath, reaperPath, deadline, now, isAlive, seams) {
132
+ const releaseReaper = acquireReaperLock(reaperPath, deadline, now);
133
+ try {
134
+ const ageMs = directoryAgeMs(lockPath, now());
135
+ if (ageMs === undefined || ageMs < LOCK_STALE_MS)
136
+ return;
137
+ const owner = readLockOwner(lockPath);
138
+ // Reclaim an absent owner record (a crash before the owner write) or a dead
139
+ // owner at the soft-stale age — recovering an abandoned lock fast is the whole
140
+ // point. A live owner waits for the hard-age backstop. The residual: a writer
141
+ // OS-suspended for 30s inside the two-statement mkdir/owner-write gap could be
142
+ // reclaimed and overlap; that window is a strict subset of the documented
143
+ // advisory-lock boundary (a suspended holder past the reclaim age), so it is
144
+ // accepted, not silently ignored.
145
+ const reclaimable = owner === undefined || !isAlive(owner.pid) || ageMs >= LOCK_STALE_HARD_MS;
146
+ if (!reclaimable)
147
+ return;
148
+ seams?.onBeforeReclaimDelete?.();
149
+ rmSync(lockPath, { recursive: true, force: true });
150
+ }
151
+ finally {
152
+ releaseReaper();
153
+ }
154
+ }
155
+ function acquireScaffolderFileLock(path, seams) {
156
+ const lockPath = `${path}.lock`;
157
+ const reaperPath = `${lockPath}.reaper`;
158
+ const ownerToken = `${process.pid}:${randomUUID()}`;
159
+ const ownerPath = `${lockPath}/${LOCK_OWNER_FILE}`;
160
+ const now = seams?.now ?? Date.now;
161
+ const isAlive = seams?.isAlive ?? isProcessAlive;
162
+ const deadline = now() + LOCK_TIMEOUT_MS;
163
+ for (;;) {
164
+ if (tryAcquireDirectory(lockPath)) {
165
+ try {
166
+ writeFileSync(ownerPath, ownerToken, "utf8");
167
+ }
168
+ catch (error) {
169
+ rmSync(lockPath, { recursive: true, force: true });
170
+ throw error;
171
+ }
172
+ return () => {
173
+ try {
174
+ if (readFileSync(ownerPath, "utf8") !== ownerToken)
175
+ return;
176
+ rmSync(lockPath, { recursive: true, force: true });
177
+ }
178
+ catch (error) {
179
+ if (!isErrno(error, "ENOENT"))
180
+ throw error;
181
+ }
182
+ };
183
+ }
184
+ const ageMs = directoryAgeMs(lockPath, now());
185
+ if (ageMs !== undefined && ageMs >= LOCK_STALE_MS) {
186
+ // Reclaim may be a no-op (owner still alive within the hard-age window);
187
+ // fall through to the deadline check so a live holder can't spin us
188
+ // forever, then retry the mkdir on the next iteration.
189
+ reclaimStaleLock(lockPath, reaperPath, deadline, now, isAlive, seams);
190
+ }
191
+ if (now() >= deadline) {
192
+ throw new Error(`Timed out waiting for host config lock: ${path}`);
193
+ }
194
+ sleepSync(LOCK_POLL_MS);
195
+ }
196
+ }
197
+ /**
198
+ * Serialize a complete read/modify/write transaction for a host config file.
199
+ * These are local single-machine home-config locks (`~/.codex`, `~/.grok`), so
200
+ * owner-pid liveness is meaningful; this is not a shared-filesystem lock.
201
+ */
202
+ export function withScaffolderFileLock(path, action, seams) {
203
+ mkdirSync(dirname(path), { recursive: true });
204
+ const release = acquireScaffolderFileLock(path, seams);
205
+ try {
206
+ return action();
207
+ }
208
+ finally {
209
+ release();
210
+ }
211
+ }
212
+ /**
213
+ * Serialize a text-file read/modify/write and install the replacement strictly
214
+ * atomically without weakening an existing file's permissions.
215
+ */
216
+ export function updateScaffolderTextFile(path, transform) {
217
+ return withScaffolderFileLock(path, () => {
218
+ const existed = existsSync(path);
219
+ if (existed && lstatSync(path).isSymbolicLink()) {
220
+ throw new Error(`Refusing to replace symlinked host config: ${path}`);
221
+ }
222
+ const existing = existed ? readFileSync(path, "utf8") : "";
223
+ const next = transform(existing);
224
+ if (next === existing)
225
+ return "unchanged";
226
+ const mode = existed ? lstatSync(path).mode & 0o777 : 0o600;
227
+ writeFileAtomicStrict(path, next, { encoding: "utf8", mode });
228
+ return "written";
229
+ });
230
+ }
@@ -0,0 +1,38 @@
1
+ import type { MergeOptions } from "#cli/commands/init/merge";
2
+ import { type WebpressoInstallProbe } from "#cli/commands/init/mcp-spec.js";
3
+ export declare const GROK_CONFIG_RELATIVE_PATH = ".grok/config.toml";
4
+ /** Render the managed `[mcp_servers.webpresso]` TOML block for Grok. */
5
+ export declare function grokWebpressoMcpBlock(entryPath: string): string;
6
+ export declare function upsertGrokWebpressoMcp(raw: string, entryPath: string): string;
7
+ export interface EnsureGrokWebpressoMcpInput {
8
+ readonly options: MergeOptions;
9
+ /** Project root whose `.grok/config.toml` is managed. */
10
+ readonly repoRoot: string;
11
+ /** Test seam — override the resolved MCP entry path. */
12
+ readonly entryPath?: string;
13
+ /** Test seam — override `<repoRoot>/.grok/config.toml`. */
14
+ readonly configPath?: string;
15
+ /** Test seam — override the install-discovery probe. */
16
+ readonly probe?: WebpressoInstallProbe;
17
+ }
18
+ export type EnsureGrokWebpressoMcpResult = {
19
+ kind: "grok-webpresso-mcp-written";
20
+ path: string;
21
+ entryPath: string;
22
+ } | {
23
+ kind: "grok-webpresso-mcp-unchanged";
24
+ path: string;
25
+ entryPath: string;
26
+ } | {
27
+ kind: "grok-webpresso-mcp-ambiguous";
28
+ path: string;
29
+ entryPath: string;
30
+ } | {
31
+ kind: "grok-webpresso-mcp-skipped-dry-run";
32
+ path: string;
33
+ } | {
34
+ kind: "grok-webpresso-mcp-not-installed";
35
+ path: string;
36
+ checked: readonly string[];
37
+ };
38
+ export declare function ensureGrokWebpressoMcp(input: EnsureGrokWebpressoMcpInput): EnsureGrokWebpressoMcpResult;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * `grok-mcp` scaffolder preset.
3
+ *
4
+ * Grok Build reads persistent MCP servers from a project-local
5
+ * `.grok/config.toml`. A project entry *replaces the user entry whole, by
6
+ * name*, so the managed `[mcp_servers.webpresso]` block owns the webpresso
7
+ * alias for the repo. The schema mirrors Codex TOML (command/args/env inline
8
+ * table); we reuse Codex's deterministic per-server upsert so unrelated
9
+ * `[mcp_servers.*]`, `[plugins]`, and `[permission]` sections are preserved.
10
+ *
11
+ * The launcher is resolved to an absolute `bin/wp` path at scaffold time via
12
+ * the shared {@link resolveWebpressoMcpEntry}; startup only spawns a verified
13
+ * binary. Env is projected from the one canonical {@link WEBPRESSO_MCP_SPEC}.
14
+ */
15
+ import { join } from "node:path";
16
+ import { updateScaffolderTextFile } from "#cli/commands/init/scaffolders/file-transaction.js";
17
+ import { agentKitMcpLaunchCommand, resolveWebpressoMcpEntry, WEBPRESSO_MCP_SPEC, } from "#cli/commands/init/mcp-spec.js";
18
+ import { commandMcpHeader, hasAmbiguousMcpServerDottedDefinition, hasManagedMcpServerTableConflict, upsertMcpServerBlock, } from "#cli/commands/init/scaffolders/codex-mcp/index.js";
19
+ export const GROK_CONFIG_RELATIVE_PATH = ".grok/config.toml";
20
+ function tomlString(value) {
21
+ return JSON.stringify(value);
22
+ }
23
+ function tomlStringArray(values) {
24
+ return `[${values.map(tomlString).join(", ")}]`;
25
+ }
26
+ function tomlEnvInlineTable(env) {
27
+ const pairs = Object.entries(env).map(([key, value]) => `${key} = ${tomlString(value)}`);
28
+ return pairs.length > 0 ? `{ ${pairs.join(", ")} }` : "{}";
29
+ }
30
+ /** Render the managed `[mcp_servers.webpresso]` TOML block for Grok. */
31
+ export function grokWebpressoMcpBlock(entryPath) {
32
+ const launch = agentKitMcpLaunchCommand(entryPath);
33
+ const lines = [
34
+ commandMcpHeader(WEBPRESSO_MCP_SPEC.name),
35
+ `command = ${tomlString(launch.command)}`,
36
+ `args = ${tomlStringArray(launch.args)}`,
37
+ `env = ${tomlEnvInlineTable(WEBPRESSO_MCP_SPEC.env)}`,
38
+ "enabled = true",
39
+ ];
40
+ return `${lines.join("\n")}\n`;
41
+ }
42
+ export function upsertGrokWebpressoMcp(raw, entryPath) {
43
+ return upsertMcpServerBlock(raw, WEBPRESSO_MCP_SPEC.name, grokWebpressoMcpBlock(entryPath));
44
+ }
45
+ export function ensureGrokWebpressoMcp(input) {
46
+ const configPath = input.configPath ?? join(input.repoRoot, ".grok", "config.toml");
47
+ if (input.options.dryRun) {
48
+ return { kind: "grok-webpresso-mcp-skipped-dry-run", path: configPath };
49
+ }
50
+ const resolution = input.entryPath === undefined ? resolveWebpressoMcpEntry(input.probe) : undefined;
51
+ const entryPath = input.entryPath ?? resolution?.entryPath;
52
+ if (!entryPath) {
53
+ const checked = resolution?.checked ?? [];
54
+ return { kind: "grok-webpresso-mcp-not-installed", path: configPath, checked };
55
+ }
56
+ let ambiguous = false;
57
+ const outcome = updateScaffolderTextFile(configPath, (existing) => {
58
+ if (hasAmbiguousMcpServerDottedDefinition(existing, WEBPRESSO_MCP_SPEC.name) ||
59
+ hasManagedMcpServerTableConflict(existing, WEBPRESSO_MCP_SPEC.name)) {
60
+ ambiguous = true;
61
+ return existing;
62
+ }
63
+ return upsertGrokWebpressoMcp(existing, entryPath);
64
+ });
65
+ if (ambiguous) {
66
+ return { kind: "grok-webpresso-mcp-ambiguous", path: configPath, entryPath };
67
+ }
68
+ return {
69
+ kind: outcome === "written" ? "grok-webpresso-mcp-written" : "grok-webpresso-mcp-unchanged",
70
+ path: configPath,
71
+ entryPath,
72
+ };
73
+ }
@@ -1,11 +1,29 @@
1
1
  import type { MergeOptions } from "#cli/commands/init/merge";
2
+ import { type WebpressoInstallProbe } from "#cli/commands/init/mcp-spec.js";
2
3
  export declare const STALE_OPENCODE_HOOK_PLUGIN_RELATIVE_PATH: string;
3
4
  export declare const OPENCODE_PLUGIN_SUPPORT_LEVEL = "native-npm";
4
5
  export declare const OPENCODE_PLUGIN_PACKAGE = "@webpresso/opencode-plugin";
6
+ export declare const OPENCODE_MCP_SERVER_NAME: "webpresso";
7
+ /**
8
+ * The canonical OpenCode `mcp.webpresso` entry. OpenCode's local-server shape
9
+ * folds the launcher and its args into a single `command` array, keys env
10
+ * under `environment`, and gates activation with `enabled`. Projected from the
11
+ * one canonical {@link WEBPRESSO_MCP_SPEC}.
12
+ */
13
+ export declare function opencodeWebpressoMcpEntry(entryPath: string): {
14
+ type: "local";
15
+ command: string[];
16
+ enabled: true;
17
+ environment: Record<string, string>;
18
+ };
5
19
  export interface EnsureOpenCodeWebpressoPluginInput {
6
20
  readonly repoRoot: string;
7
21
  readonly options: MergeOptions;
8
22
  readonly configPath?: string;
23
+ /** Test seam — override the resolved MCP entry path. */
24
+ readonly entryPath?: string;
25
+ /** Test seam — override the install-discovery probe. */
26
+ readonly probe?: WebpressoInstallProbe;
9
27
  }
10
28
  export type EnsureOpenCodeWebpressoPluginResult = {
11
29
  kind: "opencode-webpresso-plugin-written";
@@ -13,12 +31,15 @@ export type EnsureOpenCodeWebpressoPluginResult = {
13
31
  pluginPackage: string;
14
32
  removed: readonly string[];
15
33
  preserved: readonly string[];
34
+ /** Absolute wp launcher wired into `mcp.webpresso`, or null when unresolved. */
35
+ mcpEntryPath: string | null;
16
36
  } | {
17
37
  kind: "opencode-webpresso-plugin-unchanged";
18
38
  path: string;
19
39
  pluginPackage: string;
20
40
  removed: readonly string[];
21
41
  preserved: readonly string[];
42
+ mcpEntryPath: string | null;
22
43
  } | {
23
44
  kind: "opencode-webpresso-plugin-skipped-dry-run";
24
45
  path: string;