@webpresso/agent-kit 3.3.2 → 3.3.4

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 (167) hide show
  1. package/THIRD-PARTY-NOTICES.md +12 -10
  2. package/catalog/AGENTS.md.tpl +3 -5
  3. package/catalog/agent/rules/changeset-release.md +15 -4
  4. package/catalog/agent/rules/managed-tool-binaries.md +67 -0
  5. package/catalog/agent/rules/parallel-orchestration-quota.md +99 -1
  6. package/catalog/agent/rules/pre-implementation.md +36 -20
  7. package/catalog/agent/rules/rtk-routing.md +22 -16
  8. package/catalog/agent/skills/autopilot/SKILL.md +4 -3
  9. package/catalog/agent/skills/ultragoal/SKILL.md +11 -9
  10. package/catalog/agent/skills/verify/SKILL.md +12 -12
  11. package/catalog/compose/admin-blocks-registry.json +659 -0
  12. package/dist/esm/audit/hook-surface.js +5 -1
  13. package/dist/esm/audit/repo-guardrails.d.ts +8 -0
  14. package/dist/esm/audit/repo-guardrails.js +71 -1
  15. package/dist/esm/blueprint/core/parser.js +40 -16
  16. package/dist/esm/blueprint/core/validation/criteria.js +2 -1
  17. package/dist/esm/blueprint/execution/artifacts.js +8 -4
  18. package/dist/esm/blueprint/execution/frontmatter-keys.d.ts +17 -0
  19. package/dist/esm/blueprint/execution/frontmatter-keys.js +24 -0
  20. package/dist/esm/blueprint/execution/metadata.js +8 -4
  21. package/dist/esm/blueprint/lifecycle/audit.d.ts +21 -0
  22. package/dist/esm/blueprint/lifecycle/audit.js +89 -3
  23. package/dist/esm/blueprint/lifecycle/review-provenance.js +148 -40
  24. package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +22 -0
  25. package/dist/esm/blueprint/lifecycle/rollout-anchor.js +37 -1
  26. package/dist/esm/blueprint/markdown/blocked-line.d.ts +49 -0
  27. package/dist/esm/blueprint/markdown/blocked-line.js +63 -0
  28. package/dist/esm/blueprint/markdown/helpers.js +10 -4
  29. package/dist/esm/blueprint/tracked-document/parser.js +3 -3
  30. package/dist/esm/blueprint/trust/gate-row.d.ts +55 -0
  31. package/dist/esm/blueprint/trust/gate-row.js +113 -0
  32. package/dist/esm/blueprint/trust/promotion.d.ts +17 -0
  33. package/dist/esm/blueprint/trust/promotion.js +19 -19
  34. package/dist/esm/build/atomic-file-copy.d.ts +32 -0
  35. package/dist/esm/build/atomic-file-copy.js +44 -0
  36. package/dist/esm/build/cli-mcp-parity.js +3 -2
  37. package/dist/esm/build/package-manifest.js +7 -2
  38. package/dist/esm/build/vendor-oxlint-plugins.d.ts +8 -0
  39. package/dist/esm/build/vendor-oxlint-plugins.js +53 -0
  40. package/dist/esm/cli/commands/audit-core.js +9 -1
  41. package/dist/esm/cli/commands/audit.js +16 -5
  42. package/dist/esm/cli/commands/blueprint/mutations.d.ts +15 -0
  43. package/dist/esm/cli/commands/blueprint/mutations.js +8 -2
  44. package/dist/esm/cli/commands/compile.d.ts +13 -0
  45. package/dist/esm/cli/commands/compile.js +182 -62
  46. package/dist/esm/cli/commands/format.js +17 -1
  47. package/dist/esm/cli/commands/gain/index.js +10 -3
  48. package/dist/esm/cli/commands/init/gitignore-patcher.js +6 -0
  49. package/dist/esm/cli/commands/init/index.js +29 -48
  50. package/dist/esm/cli/commands/init/mcp-spec.d.ts +54 -4
  51. package/dist/esm/cli/commands/init/mcp-spec.js +126 -22
  52. package/dist/esm/cli/commands/init/merge.js +30 -10
  53. package/dist/esm/cli/commands/init/scaffold-agents-md.js +5 -7
  54. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +56 -31
  55. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +180 -43
  56. package/dist/esm/cli/commands/init/scaffolders/rtk/index.d.ts +17 -5
  57. package/dist/esm/cli/commands/init/scaffolders/rtk/index.js +56 -75
  58. package/dist/esm/cli/commands/init/scaffolders/runtime-check/index.js +7 -6
  59. package/dist/esm/cli/commands/init/untracked-collision.d.ts +12 -1
  60. package/dist/esm/cli/commands/init/untracked-collision.js +23 -1
  61. package/dist/esm/cli/commands/package-manager.js +8 -2
  62. package/dist/esm/cli/commands/pr-help.js +1 -1
  63. package/dist/esm/cli/commands/pr.js +18 -4
  64. package/dist/esm/cli/commands/review.js +60 -6
  65. package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +24 -2
  66. package/dist/esm/cli/commands/worktree/router-dispatch.js +80 -48
  67. package/dist/esm/cli/commands/worktree/router.js +5 -2
  68. package/dist/esm/cli/direct-provider-launch.js +12 -4
  69. package/dist/esm/compiler/flatten.d.ts +10 -1
  70. package/dist/esm/compiler/flatten.js +14 -4
  71. package/dist/esm/compose/admin-blocks-registry-integrity.d.ts +22 -0
  72. package/dist/esm/compose/admin-blocks-registry-integrity.js +90 -0
  73. package/dist/esm/compose/compose.d.ts +46 -0
  74. package/dist/esm/compose/compose.js +250 -0
  75. package/dist/esm/compose/escape-html.d.ts +5 -0
  76. package/dist/esm/compose/escape-html.js +12 -0
  77. package/dist/esm/compose/ops-report-render.d.ts +5 -0
  78. package/dist/esm/compose/ops-report-render.js +71 -0
  79. package/dist/esm/compose/ops-report-schema.d.ts +42 -0
  80. package/dist/esm/compose/ops-report-schema.js +60 -0
  81. package/dist/esm/compose/preview.d.ts +14 -0
  82. package/dist/esm/compose/preview.js +32 -0
  83. package/dist/esm/compose/registry-resolve.d.ts +64 -0
  84. package/dist/esm/compose/registry-resolve.js +200 -0
  85. package/dist/esm/config/oxlint/graphql-conventions.js +2 -5
  86. package/dist/esm/config/oxlint/import-hygiene.js +21 -8
  87. package/dist/esm/config/oxlint/oxlint-config.fixture.d.ts +1 -1
  88. package/dist/esm/config/oxlint/oxlint-config.fixture.js +2 -3
  89. package/dist/esm/config/oxlint/oxlintrc.d.ts +5 -7
  90. package/dist/esm/config/oxlint/oxlintrc.js +18 -21
  91. package/dist/esm/config/oxlint/path-roles.js +53 -0
  92. package/dist/esm/config/oxlint/query-patterns.js +2 -5
  93. package/dist/esm/daemon/domains/blueprint-exec-ledger.d.ts +86 -0
  94. package/dist/esm/daemon/domains/blueprint-exec-ledger.js +134 -0
  95. package/dist/esm/daemon/domains/blueprint-exec.d.ts +106 -0
  96. package/dist/esm/daemon/domains/blueprint-exec.js +161 -0
  97. package/dist/esm/daemon/domains/launch.d.ts +25 -0
  98. package/dist/esm/daemon/domains/launch.js +30 -1
  99. package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +12 -1
  100. package/dist/esm/daemon/handlers/blueprints-handlers.js +148 -6
  101. package/dist/esm/daemon/handlers/product.d.ts +10 -0
  102. package/dist/esm/daemon/handlers/product.js +10 -1
  103. package/dist/esm/daemon/methods.d.ts +21 -1
  104. package/dist/esm/daemon/methods.js +20 -0
  105. package/dist/esm/daemon/protocol/contract.d.ts +58 -0
  106. package/dist/esm/daemon/protocol/contract.js +38 -0
  107. package/dist/esm/daemon/server.js +7 -0
  108. package/dist/esm/docs-linter/blueprint-plan.js +2 -2
  109. package/dist/esm/errors/wp-error.js +21 -2
  110. package/dist/esm/git/changed-files.d.ts +20 -0
  111. package/dist/esm/git/changed-files.js +63 -0
  112. package/dist/esm/hooks/doctor.d.ts +11 -0
  113. package/dist/esm/hooks/doctor.js +114 -45
  114. package/dist/esm/hooks/pretool-guard/dev-routing.js +1 -1
  115. package/dist/esm/hooks/pretool-guard/rtk-rewrite.d.ts +24 -0
  116. package/dist/esm/hooks/pretool-guard/rtk-rewrite.js +50 -0
  117. package/dist/esm/hooks/pretool-guard/runner.js +6 -0
  118. package/dist/esm/hooks/status/index.js +1 -1
  119. package/dist/esm/hooks/stop/qa-changed-files.js +6 -1
  120. package/dist/esm/mcp/blueprint/_shared/lifecycle.js +39 -11
  121. package/dist/esm/mcp/blueprint/handlers/review-log.d.ts +14 -7
  122. package/dist/esm/mcp/blueprint/handlers/review-log.js +46 -22
  123. package/dist/esm/mcp/blueprint/handlers/validate.js +6 -0
  124. package/dist/esm/mcp/blueprint/registration.js +1 -1
  125. package/dist/esm/mcp/tools/_names.d.ts +1 -1
  126. package/dist/esm/mcp/tools/_names.js +4 -0
  127. package/dist/esm/mcp/tools/_registry.js +8 -0
  128. package/dist/esm/mcp/tools/_shared/redact.d.ts +22 -0
  129. package/dist/esm/mcp/tools/_shared/redact.js +114 -8
  130. package/dist/esm/mcp/tools/gain.js +28 -2
  131. package/dist/esm/mcp/tools/release-progress.d.ts +9 -0
  132. package/dist/esm/mcp/tools/release-progress.js +110 -0
  133. package/dist/esm/mcp/tools/worktree.js +65 -3
  134. package/dist/esm/mcp/tools/wp-ui-blocks-list.d.ts +3 -0
  135. package/dist/esm/mcp/tools/wp-ui-blocks-list.js +43 -0
  136. package/dist/esm/mcp/tools/wp-ui-compose.d.ts +3 -0
  137. package/dist/esm/mcp/tools/wp-ui-compose.js +96 -0
  138. package/dist/esm/mcp/tools/wp-ui-preview.d.ts +3 -0
  139. package/dist/esm/mcp/tools/wp-ui-preview.js +59 -0
  140. package/dist/esm/package.json +2 -0
  141. package/dist/esm/review/lifecycle-writers.d.ts +76 -0
  142. package/dist/esm/review/lifecycle-writers.js +330 -0
  143. package/dist/esm/review/subject.js +66 -32
  144. package/dist/esm/status/snapshot.d.ts +5 -0
  145. package/dist/esm/status/snapshot.js +27 -2
  146. package/dist/esm/test/shard-durations.json +0 -1
  147. package/dist/esm/test-helpers/global-setup.js +38 -3
  148. package/dist/esm/tool-runtime/managed-rtk.d.ts +44 -0
  149. package/dist/esm/tool-runtime/managed-rtk.js +275 -0
  150. package/dist/esm/tool-runtime/resolve-runner.js +18 -194
  151. package/dist/esm/utils/package-root.d.ts +32 -0
  152. package/dist/esm/utils/package-root.js +82 -0
  153. package/dist/esm/worktrees/git-metadata.d.ts +41 -0
  154. package/dist/esm/worktrees/git-metadata.js +48 -0
  155. package/package.json +27 -21
  156. package/dist/esm/config/oxlint/code-safety.d.ts +0 -18
  157. package/dist/esm/config/oxlint/foundation-purity.d.ts +0 -21
  158. package/dist/esm/config/oxlint/graphql-conventions.d.ts +0 -23
  159. package/dist/esm/config/oxlint/import-hygiene.d.ts +0 -34
  160. package/dist/esm/config/oxlint/index.d.ts +0 -22
  161. package/dist/esm/config/oxlint/index.js +0 -28
  162. package/dist/esm/config/oxlint/monorepo-paths.d.ts +0 -22
  163. package/dist/esm/config/oxlint/query-patterns.d.ts +0 -24
  164. package/dist/esm/config/oxlint/testing-quality.d.ts +0 -35
  165. package/dist/esm/config/oxlint/tier-boundaries.d.ts +0 -35
  166. package/dist/esm/output-transforms/rulesync.d.ts +0 -2
  167. package/dist/esm/output-transforms/rulesync.js +0 -79
@@ -27,6 +27,8 @@ import { isAgentKitSourceRepo } from "#cli/commands/init/source-repo-hook-policy
27
27
  import { findAgentKitPackageRoot, resolveAgentKitPackageRoot, } from "#cli/commands/init/package-root";
28
28
  import { readInstalledHooksMap } from "#hooks/shared/installed-hooks.js";
29
29
  import { auditCodexConfigPaths, checkCodexConfigPathIntegrity } from "#hooks/codex-config-paths.js";
30
+ import { findInstalledCodexPluginMcpManifests, pruneOwnedMcpServerBlock, } from "#cli/commands/init/scaffolders/codex-mcp/index.js";
31
+ import { isAgentKitOwnedWpCommand, WEBPRESSO_MCP_SERVER_NAME, } from "#cli/commands/init/mcp-spec.js";
30
32
  import { parseProjectMcpServers, scanCodexConfigDrift } from "#hooks/host-config-contract.js";
31
33
  import { defaultCodexConfigFilePath } from "#cli/commands/init/scaffolders/agent-hooks/codex-trust-sync.js";
32
34
  import { expectedRootWpBinRelativePath, formatRootLauncherContractFailure, rootContractMode, validateRootLauncherContract, } from "#launcher/root-contract.js";
@@ -55,7 +57,10 @@ function createOwnedProbeSettler(child, timeoutMs, timeoutResult, resolve) {
55
57
  return settle;
56
58
  }
57
59
  const RTK_REQUESTED_MARKER = join(".agent", ".rtk-requested");
58
- const RTK_INSTALL_HINT = "rtk requested via --with rtk but not on PATH; brew install rtk";
60
+ const RTK_INSTALL_HINT = "rtk requested but managed binary missing; re-run wp setup (or set WP_RTK_BIN)";
61
+ const RTK_FREESTANDING_HOOK_REL = join(".claude", "hooks", "rtk-rewrite.sh");
62
+ const RTK_FREESTANDING_SETTINGS_REL = join(".claude", "settings.json");
63
+ const RTK_FREESTANDING_REPAIR = "stale freestanding rtk-rewrite surface; run wp setup (ensureRtk strips rtk-rewrite.sh — pretool-guard owns rewrite)";
59
64
  const HOST_SMOKE_ENV = "WP_RUN_HOST_SMOKE";
60
65
  const HOOK_PROBE_TIMEOUT_ENV = "WP_DOCTOR_HOOK_TIMEOUT_MS";
61
66
  const WRAPPER_PROBE_ENV = "WP_DOCTOR_WRAPPER_PROBE";
@@ -347,6 +352,62 @@ function checkConsumerCodexHookPaths(cwd = process.cwd()) {
347
352
  function wasRtkRequested(cwd = process.cwd()) {
348
353
  return tryAccess(join(cwd, RTK_REQUESTED_MARKER));
349
354
  }
355
+ export function checkFreestandingRtkSurface(cwd = process.cwd()) {
356
+ const hookPath = join(cwd, RTK_FREESTANDING_HOOK_REL);
357
+ const settingsPath = join(cwd, RTK_FREESTANDING_SETTINGS_REL);
358
+ const hookPresent = tryAccess(hookPath);
359
+ const markers = [];
360
+ if (hookPresent)
361
+ markers.push(RTK_FREESTANDING_HOOK_REL);
362
+ if (tryAccess(settingsPath)) {
363
+ try {
364
+ const settings = readFileSync(settingsPath, "utf8");
365
+ if (settings.includes("rtk-rewrite.sh")) {
366
+ markers.push(`${RTK_FREESTANDING_SETTINGS_REL} rtk-rewrite.sh`);
367
+ }
368
+ if (settings.includes("rtk hook claude")) {
369
+ markers.push(`${RTK_FREESTANDING_SETTINGS_REL} rtk hook claude`);
370
+ }
371
+ }
372
+ catch {
373
+ // ignore unreadable settings
374
+ }
375
+ }
376
+ if (markers.length === 0)
377
+ return null;
378
+ return {
379
+ name: "freestanding rtk PreToolUse retired",
380
+ ok: false,
381
+ detail: `${RTK_FREESTANDING_REPAIR} (found: ${markers.join(", ")})`,
382
+ };
383
+ }
384
+ /** Non-mutating warn for user-global freestanding RTK rewriters (rtk init -g). */
385
+ export function checkGlobalFreestandingRtkSurface() {
386
+ const globalSettings = join(homedir(), ".claude", "settings.json");
387
+ const globalHook = join(homedir(), ".claude", "hooks", "rtk-rewrite.sh");
388
+ const markers = [];
389
+ if (tryAccess(globalHook))
390
+ markers.push("~/.claude/hooks/rtk-rewrite.sh");
391
+ if (tryAccess(globalSettings)) {
392
+ try {
393
+ const settings = readFileSync(globalSettings, "utf8");
394
+ if (settings.includes("rtk-rewrite.sh"))
395
+ markers.push("~/.claude/settings.json rtk-rewrite.sh");
396
+ if (settings.includes("rtk hook claude"))
397
+ markers.push("~/.claude/settings.json rtk hook claude");
398
+ }
399
+ catch {
400
+ // ignore
401
+ }
402
+ }
403
+ if (markers.length === 0)
404
+ return null;
405
+ return {
406
+ name: "global freestanding rtk (warn)",
407
+ ok: true,
408
+ detail: `WARNING: global freestanding RTK PreToolUse still present (${markers.join(", ")}); remove manually or re-run rtk uninstall — project setup does not mutate ~/.claude. Dual rewriters may race updatedInput with wp-pretool-guard.`,
409
+ };
410
+ }
350
411
  function shouldRunHostChecks(mode) {
351
412
  if (mode === "skip")
352
413
  return false;
@@ -393,36 +454,13 @@ function resolveRequestedHosts(mode, hostNames) {
393
454
  export function checkRtkOnPath(cwd) {
394
455
  if (!wasRtkRequested(cwd))
395
456
  return Promise.resolve(null);
396
- return new Promise((resolve) => {
397
- const timeoutMs = hookProbeTimeoutMs();
398
- const child = spawn("rtk", ["--version"], {
399
- stdio: ["ignore", "pipe", "pipe"],
400
- detached: process.platform !== "win32",
401
- });
402
- let stdout = "";
403
- let stderr = "";
404
- const settle = createOwnedProbeSettler(child, timeoutMs, () => ({
405
- name: "rtk on PATH",
406
- ok: false,
407
- detail: `rtk probe timed out after ${timeoutMs}ms`,
408
- }), resolve);
409
- child.stdout.on("data", (chunk) => {
410
- stdout += String(chunk);
411
- });
412
- child.stderr?.on("data", (chunk) => {
413
- stderr += String(chunk);
414
- });
415
- child.on("error", () => {
416
- settle({ name: "rtk on PATH", ok: false, detail: RTK_INSTALL_HINT });
417
- });
418
- child.on("close", (code) => {
419
- if (code === 0) {
420
- settle({ name: "rtk on PATH", ok: true, detail: stdout.trim() || "rtk present" });
421
- return;
422
- }
423
- const suffix = stderr.trim().length > 0 ? ` (${stderr.trim()})` : "";
424
- settle({ name: "rtk on PATH", ok: false, detail: `${RTK_INSTALL_HINT}${suffix}` });
425
- });
457
+ // Managed binary (Webpresso cache / WP_RTK_BIN), not bare PATH brew install.
458
+ return import("#tool-runtime/managed-rtk.js").then(({ isManagedRtkAvailable, resolveManagedRtkExecutable }) => {
459
+ const path = resolveManagedRtkExecutable();
460
+ if (path && isManagedRtkAvailable()) {
461
+ return { name: "rtk managed", ok: true, detail: path };
462
+ }
463
+ return { name: "rtk managed", ok: false, detail: RTK_INSTALL_HINT };
426
464
  });
427
465
  }
428
466
  async function probeHookBin(wpCli, hookName, checkStdin) {
@@ -1184,19 +1222,44 @@ async function checkCodexHost() {
1184
1222
  detail: `missing MCP entry (webpresso=${hasAgentKit})`,
1185
1223
  };
1186
1224
  }
1187
- const mcpPathFindings = auditCodexConfigPaths().filter((finding) => finding.location.startsWith("mcp_servers.webpresso."));
1188
- if (mcpPathFindings.length > 0) {
1189
- const detail = mcpPathFindings
1190
- .map((finding) => `${finding.location}: ${finding.path} (${finding.reason === "missing" ? "path does not exist" : "volatile versioned path"})`)
1191
- .join("; ");
1192
- return {
1193
- name: "Codex host integration",
1194
- ok: false,
1195
- detail: `webpresso MCP listed but launch paths are broken or rot-prone — ${detail}`,
1196
- };
1197
- }
1225
+ const legacy = classifyLegacyCodexWebpressoChannel();
1226
+ if (legacy)
1227
+ return { name: "Codex host integration", ok: false, detail: legacy };
1198
1228
  return { name: "Codex host integration", ok: true, detail: "webpresso MCP visible" };
1199
1229
  }
1230
+ /**
1231
+ * Codex must receive `webpresso` over exactly one channel — the bundled Codex
1232
+ * plugin. Report the retired user-global `[mcp_servers.webpresso]` block that
1233
+ * `wp setup` removes, and call out a double registration distinctly. A
1234
+ * user-authored block (a live `wp` outside every agent-kit root) is never
1235
+ * removed by setup, so it gets its own manual-action finding.
1236
+ */
1237
+ export function classifyLegacyCodexWebpressoChannel(configPath = defaultCodexConfigFilePath(), homeDir = process.env.HOME || homedir()) {
1238
+ const outcome = classifyCodexConfigWebpressoBlock(configPath);
1239
+ if (outcome === null || outcome.kind === "absent")
1240
+ return null;
1241
+ if (outcome.kind === "ambiguous") {
1242
+ return `${configPath} declares webpresso in a TOML spelling wp setup cannot rewrite; remove the [mcp_servers.webpresso] definition by hand — the bundled Codex plugin is the single delivery channel`;
1243
+ }
1244
+ if (outcome.kind === "preserved") {
1245
+ return `${configPath} declares a user-authored webpresso MCP server (${outcome.command ?? "no command"}); wp setup preserves it. Remove it by hand to use the bundled Codex plugin as the single delivery channel`;
1246
+ }
1247
+ const dualChannel = findInstalledCodexPluginMcpManifests(homeDir).length > 0;
1248
+ const prefix = dualChannel
1249
+ ? "webpresso is registered over BOTH delivery channels — the bundled Codex plugin AND a legacy"
1250
+ : "webpresso is still registered over the retired legacy";
1251
+ return `${prefix} [mcp_servers.webpresso] block in ${configPath} (command = ${outcome.command}); run \`wp setup\` — it removes the legacy block`;
1252
+ }
1253
+ function classifyCodexConfigWebpressoBlock(configPath) {
1254
+ try {
1255
+ if (!existsSync(configPath))
1256
+ return null;
1257
+ return pruneOwnedMcpServerBlock(readFileSync(configPath, "utf8"), WEBPRESSO_MCP_SERVER_NAME, isAgentKitOwnedWpCommand);
1258
+ }
1259
+ catch {
1260
+ return null;
1261
+ }
1262
+ }
1200
1263
  async function checkOpenCodeHost(cwd = process.cwd()) {
1201
1264
  const available = await runCommand("opencode", ["--version"]);
1202
1265
  if (!available.ok) {
@@ -1430,9 +1493,8 @@ export function checkPackagedHostArtifacts(cwd = process.cwd()) {
1430
1493
  const serverRecord = server && typeof server === "object" && !Array.isArray(server)
1431
1494
  ? server
1432
1495
  : null;
1433
- if (serverRecord?.command !== "${PLUGIN_ROOT}/bin/wp" ||
1434
- !stringArrayEquals(serverRecord.args, ["mcp"])) {
1435
- failures.push("Codex adapter MCP surface must expose direct webpresso server map launching ${PLUGIN_ROOT}/bin/wp mcp");
1496
+ if (serverRecord?.command !== "wp" || !stringArrayEquals(serverRecord.args, ["mcp"])) {
1497
+ failures.push("Codex adapter MCP surface must expose direct webpresso server map launching wp mcp");
1436
1498
  }
1437
1499
  }
1438
1500
  if (codexHooks) {
@@ -2138,6 +2200,13 @@ export async function runHooksDoctor(opts = {}) {
2138
2200
  const rtkCheck = await checkRtkOnPath(opts.cwd);
2139
2201
  if (rtkCheck)
2140
2202
  checks.push(rtkCheck);
2203
+ const freestandingRtk = checkFreestandingRtkSurface(opts.cwd);
2204
+ if (freestandingRtk)
2205
+ checks.push(freestandingRtk);
2206
+ // Non-mutating WARN only — never mutates ~/.claude; surfaces dual-rewriter risk.
2207
+ const globalFreestandingRtk = checkGlobalFreestandingRtkSurface();
2208
+ if (globalFreestandingRtk)
2209
+ checks.push(globalFreestandingRtk);
2141
2210
  if (shouldRunHostChecks(hostMode)) {
2142
2211
  for (const host of requestedHosts) {
2143
2212
  if (host === "codex") {
@@ -23,7 +23,7 @@ function routeRawMutatingGitWorktree(command) {
23
23
  return {
24
24
  action: "deny",
25
25
  tool: "wp_worktree",
26
- guidance: "Use the `wp_worktree` MCP tool (or `wp worktree` CLI when MCP is unavailable) instead — managed repositories may mutate linked worktrees only through agent-kit so owner bindings and the global registry stay consistent.",
26
+ guidance: "Use the `wp_worktree` MCP tool (or `wp worktree prune --all` / `wp worktree remove <target>` when MCP is unavailable) instead — managed repositories may mutate linked worktrees only through agent-kit so owner bindings and the global registry stay consistent. Both prune paths also run `git worktree prune`, so a hand-deleted worktree directory still releases the branch it claimed.",
27
27
  code: "GUARD_RAW_GIT_MUTATION",
28
28
  };
29
29
  }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * RTK rewrite inside path-stable pretool-guard (no freestanding consumer hook).
3
+ *
4
+ * v1: exact `git status` → `<managed-rtk> git status` when managed rtk is available.
5
+ */
6
+ export type RtkRewriteDecision = {
7
+ readonly action: "rewrite";
8
+ readonly command: string;
9
+ } | {
10
+ readonly action: "noop";
11
+ readonly reason: string;
12
+ };
13
+ export type RtkPresenceProbe = () => boolean;
14
+ export type RtkExecutableResolver = () => string | null;
15
+ /**
16
+ * Decide whether to rewrite a Bash command through managed rtk.
17
+ */
18
+ export declare function decideRtkRewrite(command: string, isRtkPresent?: RtkPresenceProbe, resolveExecutable?: RtkExecutableResolver): RtkRewriteDecision;
19
+ /**
20
+ * POSIX single-quote wrap for absolute managed paths.
21
+ * Refuse NUL/newline/single-quote — those cannot be safely embedded in `'…'`.
22
+ * Double-quote wrapping is intentionally avoided ($ / ` / " injection).
23
+ */
24
+ export declare function shellQuoteExecutable(path: string): string | null;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * RTK rewrite inside path-stable pretool-guard (no freestanding consumer hook).
3
+ *
4
+ * v1: exact `git status` → `<managed-rtk> git status` when managed rtk is available.
5
+ */
6
+ import { isManagedRtkAvailable, resolveManagedRtkExecutable } from "#tool-runtime/managed-rtk.js";
7
+ const UNCERTAIN_SHELL_RE = /[\n\r|;&<>`$(){}[\]\\'"#*?]/u;
8
+ /**
9
+ * Decide whether to rewrite a Bash command through managed rtk.
10
+ */
11
+ export function decideRtkRewrite(command, isRtkPresent = isManagedRtkAvailable, resolveExecutable = resolveManagedRtkExecutable) {
12
+ if (command !== command.trim() || command.length === 0) {
13
+ return { action: "noop", reason: "uncertain-parse" };
14
+ }
15
+ if (UNCERTAIN_SHELL_RE.test(command)) {
16
+ return { action: "noop", reason: "uncertain-parse" };
17
+ }
18
+ // already managed absolute path or bare rtk prefix
19
+ if (command.startsWith("rtk ") || command.includes("/rtk ")) {
20
+ return { action: "noop", reason: "already-rtk" };
21
+ }
22
+ if (command !== "git status") {
23
+ return { action: "noop", reason: "not-eligible" };
24
+ }
25
+ if (!isRtkPresent()) {
26
+ return { action: "noop", reason: "rtk-missing" };
27
+ }
28
+ const executable = resolveExecutable();
29
+ if (!executable) {
30
+ return { action: "noop", reason: "rtk-missing" };
31
+ }
32
+ const bin = shellQuoteExecutable(executable);
33
+ if (!bin) {
34
+ return { action: "noop", reason: "rtk-missing" };
35
+ }
36
+ return { action: "rewrite", command: `${bin} git status` };
37
+ }
38
+ /**
39
+ * POSIX single-quote wrap for absolute managed paths.
40
+ * Refuse NUL/newline/single-quote — those cannot be safely embedded in `'…'`.
41
+ * Double-quote wrapping is intentionally avoided ($ / ` / " injection).
42
+ */
43
+ export function shellQuoteExecutable(path) {
44
+ if (path.length === 0 || /[\0\n\r']/.test(path))
45
+ return null;
46
+ // Unquoted only for the common managed-cache shape (no shell metacharacters).
47
+ if (/^[A-Za-z0-9_./:@+=,-]+$/.test(path))
48
+ return path;
49
+ return `'${path}'`;
50
+ }
@@ -11,6 +11,7 @@ import { VALIDATORS } from "./validators/index.js";
11
11
  import { isDirectEntrypoint } from "#hooks/shared/direct-entrypoint";
12
12
  import { optimizeToolInput, } from "./optimization.js";
13
13
  import { recordOptimizationEvent } from "./optimization-events.js";
14
+ import { decideRtkRewrite } from "./rtk-rewrite.js";
14
15
  const RED = "\x1b[31m";
15
16
  const YELLOW = "\x1b[33m";
16
17
  const DIM = "\x1b[2m";
@@ -256,6 +257,11 @@ export function processValidation(inputJson, options = {}) {
256
257
  process.exit(2);
257
258
  }
258
259
  if (command) {
260
+ const rtkRewrite = decideRtkRewrite(command);
261
+ if (rtkRewrite.action === "rewrite") {
262
+ writeUpdatedInput(input, rtkRewrite.command);
263
+ process.exit(0);
264
+ }
259
265
  const originalAction = routeCommand(command)?.action ?? { action: "passthrough" };
260
266
  const optimization = optimizeToolInput(input, originalAction, options.optimizationRules);
261
267
  if (optimization.action === "rewrite") {
@@ -109,7 +109,7 @@ function hasValidCodexPluginArtifacts(repoRoot) {
109
109
  const hookMap = hooks?.hooks;
110
110
  return (plugin?.mcpServers === "./.mcp.json" &&
111
111
  plugin.hooks === "./hooks/hooks.json" &&
112
- serverRecord?.command === "${PLUGIN_ROOT}/bin/wp" &&
112
+ serverRecord?.command === "wp" &&
113
113
  stringArrayEquals(serverRecord.args, ["mcp"]) &&
114
114
  hookMap !== null &&
115
115
  typeof hookMap === "object" &&
@@ -45,12 +45,17 @@ export function filterQaFiles(files) {
45
45
  export function getTypecheckFiles(files) {
46
46
  return files.filter((f) => TYPECHECKABLE_EXTENSIONS.has(extname(f)));
47
47
  }
48
+ const TEST_SOURCE_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx"]);
48
49
  export function findTestFiles(sourceFile, projectDir) {
49
50
  const ext = extname(sourceFile);
50
51
  const base = basename(sourceFile, ext);
51
52
  const dir = dirname(sourceFile);
52
- if (base.endsWith(".test") || base.endsWith(".integration.test"))
53
+ // Only treat real test modules as tests. Names like `tsconfig.test.json` end
54
+ // with `.test` after stripping the extension and must not become vitest filters.
55
+ if (TEST_SOURCE_EXTENSIONS.has(ext) &&
56
+ (base.endsWith(".test") || base.endsWith(".integration.test"))) {
53
57
  return [sourceFile];
58
+ }
54
59
  const pattern = join(dir, `${base}.{test,integration.test}{.ts,.tsx}`);
55
60
  return globSync(pattern, { cwd: projectDir, withFileTypes: false });
56
61
  }
@@ -94,21 +94,49 @@ export async function applyLocalBlueprintTransition(input) {
94
94
  mkdirSync(path.dirname(found.shape === "flat" ? destination.flat : destination.directory), {
95
95
  recursive: true,
96
96
  });
97
+ const sourcePath = found.shape === "flat" ? overviewPath : found.dir;
98
+ const destinationPath = found.shape === "flat" ? destination.flat : destination.directory;
97
99
  let finalOverviewPath = overviewPath;
100
+ let renamed = false;
98
101
  if (found.state !== to_state) {
99
102
  const { renameSync } = await import("node:fs");
100
- renameSync(found.shape === "flat" ? overviewPath : found.dir, found.shape === "flat" ? destination.flat : destination.directory);
103
+ renameSync(sourcePath, destinationPath);
104
+ renamed = true;
101
105
  finalOverviewPath = found.shape === "flat" ? destination.flat : destination.folder;
102
106
  }
103
- writeFileAtomic(finalOverviewPath, updated, "utf8");
104
- await reIngest(projectCwd);
105
- const refreshed = getCurrentProjectBlueprint(projectCwd, slug);
106
- if (!refreshed.blueprint) {
107
- throw new Error(`Blueprint "${slug}" did not appear in the projection after transition`);
107
+ // Atomicity: everything from here on can still fail (re-ingest, projection
108
+ // lookup). The rename above may already have happened, and the frontmatter
109
+ // write below always happens before those checks run. Roll BOTH back
110
+ // before rethrowing so a failed transition never leaves the blueprint
111
+ // stranded in the destination lifecycle directory, nor leaves stale
112
+ // frontmatter (bumped last_updated/progress/status) on an otherwise
113
+ // unmoved file — either the transition fully lands (moved + content
114
+ // updated + re-ingested + visible in the projection) or the working tree
115
+ // is restored to exactly what it was before this call.
116
+ try {
117
+ writeFileAtomic(finalOverviewPath, updated, "utf8");
118
+ await reIngest(projectCwd);
119
+ const refreshed = getCurrentProjectBlueprint(projectCwd, slug);
120
+ if (!refreshed.blueprint) {
121
+ throw new Error(`Blueprint "${slug}" did not appear in the projection after transition`);
122
+ }
123
+ return {
124
+ blueprint: refreshed.blueprint,
125
+ overviewPath: finalOverviewPath,
126
+ fromState: found.state,
127
+ };
128
+ }
129
+ catch (error) {
130
+ try {
131
+ if (renamed) {
132
+ const { renameSync } = await import("node:fs");
133
+ renameSync(destinationPath, sourcePath);
134
+ }
135
+ writeFileAtomic(overviewPath, markdown, "utf8");
136
+ }
137
+ catch (rollbackError) {
138
+ throw new Error(`Transition to "${to_state}" failed and automatic rollback also failed; the blueprint may be left ${renamed ? `moved at ${destinationPath}` : "with partially-updated frontmatter"}. Original error: ${error instanceof Error ? error.message : String(error)}. Rollback error: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
139
+ }
140
+ throw error;
108
141
  }
109
- return {
110
- blueprint: refreshed.blueprint,
111
- overviewPath: finalOverviewPath,
112
- fromState: found.state,
113
- };
114
142
  }
@@ -2,13 +2,20 @@ import type { ProjectResolver } from "#project-resolver.js";
2
2
  import type { ToolHandlerResult } from "#mcp/auto-discover.js";
3
3
  /**
4
4
  * MCP wrapper around the SAME reviews.md-ledger writer `logReviewEntry` that
5
- * (an unwired) `wp review log` uses, plus the matching frontmatter `approvals:`
6
- * entry that `collectTrackedReviewProvenance` (the promotion approval gate)
7
- * actually reads. This is the tracked/frontmatter provenance path — the same
8
- * trust model a hand-authored `approvals:` block already had (the reviewer id
9
- * is caller-asserted, verified only by the referenced artifact/reviews.md
10
- * entry being tracked in git) — this tool just makes that existing path
11
- * reachable from MCP instead of a raw Write/Edit or a Bash/python workaround.
5
+ * (an unwired) `wp review log` uses. For NON-approval verdicts it also appends
6
+ * the matching frontmatter `approvals:` entry, so rejection attribution stays
7
+ * visible where readers already look. This is the tracked/frontmatter provenance
8
+ * path — the same trust model a hand-authored `approvals:` block already had (the
9
+ * reviewer id is caller-asserted, verified only by the referenced
10
+ * artifact/reviews.md entry being tracked in git) — this tool just makes that
11
+ * existing path reachable from MCP instead of a raw Write/Edit or a Bash/python
12
+ * workaround.
13
+ *
14
+ * It never writes a POLICY-APPROVAL entry (approve / approve-with-nits) into
15
+ * frontmatter. `collectTrackedReviewProvenance` — the legacy promotion leg — only
16
+ * counts policy approvals and never reads or hashes the blueprint body, so such
17
+ * an entry would be gate authority that survives an arbitrary rewrite of the
18
+ * plan. See `LEGACY_APPROVAL_LEG_ANCHOR_COMMIT` in `#lifecycle/rollout-anchor.js`.
12
19
  *
13
20
  * A policy-approval verdict (approve / approve-with-nits) REQUIRES
14
21
  * reviewer==="human" — matching `logReviewEvent`'s existing v2-ledger
@@ -30,13 +30,20 @@ const reviewLogSchema = z.object({
30
30
  });
31
31
  /**
32
32
  * MCP wrapper around the SAME reviews.md-ledger writer `logReviewEntry` that
33
- * (an unwired) `wp review log` uses, plus the matching frontmatter `approvals:`
34
- * entry that `collectTrackedReviewProvenance` (the promotion approval gate)
35
- * actually reads. This is the tracked/frontmatter provenance path — the same
36
- * trust model a hand-authored `approvals:` block already had (the reviewer id
37
- * is caller-asserted, verified only by the referenced artifact/reviews.md
38
- * entry being tracked in git) — this tool just makes that existing path
39
- * reachable from MCP instead of a raw Write/Edit or a Bash/python workaround.
33
+ * (an unwired) `wp review log` uses. For NON-approval verdicts it also appends
34
+ * the matching frontmatter `approvals:` entry, so rejection attribution stays
35
+ * visible where readers already look. This is the tracked/frontmatter provenance
36
+ * path — the same trust model a hand-authored `approvals:` block already had (the
37
+ * reviewer id is caller-asserted, verified only by the referenced
38
+ * artifact/reviews.md entry being tracked in git) — this tool just makes that
39
+ * existing path reachable from MCP instead of a raw Write/Edit or a Bash/python
40
+ * workaround.
41
+ *
42
+ * It never writes a POLICY-APPROVAL entry (approve / approve-with-nits) into
43
+ * frontmatter. `collectTrackedReviewProvenance` — the legacy promotion leg — only
44
+ * counts policy approvals and never reads or hashes the blueprint body, so such
45
+ * an entry would be gate authority that survives an arbitrary rewrite of the
46
+ * plan. See `LEGACY_APPROVAL_LEG_ANCHOR_COMMIT` in `#lifecycle/rollout-anchor.js`.
40
47
  *
41
48
  * A policy-approval verdict (approve / approve-with-nits) REQUIRES
42
49
  * reviewer==="human" — matching `logReviewEvent`'s existing v2-ledger
@@ -143,22 +150,39 @@ export async function handleReviewLog(projectResolver, cwd, raw) {
143
150
  ...(rev ? { rev } : {}),
144
151
  ...(note ? { note } : {}),
145
152
  });
146
- const overviewRaw = readFileSync(found.path, "utf8");
147
- const parsed = matter(overviewRaw);
148
- const existingApprovals = Array.isArray(parsed.data.approvals) ? parsed.data.approvals : [];
149
- const approvalEntry = {
150
- reviewer: entry.reviewer,
151
- verdict: entry.verdict,
152
- evidence: entry.evidence ?? "reviews.md",
153
- artifact: entry.artifact ?? normalizedArtifactPath,
154
- ...(entry.commit ? { commit: entry.commit } : {}),
155
- ...(entry.rev ? { rev: entry.rev } : {}),
156
- };
157
- const updatedData = { ...parsed.data, approvals: [...existingApprovals, approvalEntry] };
158
- const rendered = matter.stringify(parsed.content, updatedData);
159
- writeFileAtomic(found.path, rendered, { encoding: "utf8" });
153
+ // Policy-approval verdicts are NOT projected into frontmatter `approvals:`.
154
+ // That field feeds `collectTrackedReviewProvenance`, the legacy promotion leg
155
+ // that carries no content binding at all: it matches a reviews.md record and
156
+ // a tracked artifact but never reads or hashes the blueprint body, and the
157
+ // gate supplies no `targetHash`. Writing an approval there would mint
158
+ // authority that survives an arbitrary rewrite of the plan. Non-approval
159
+ // verdicts (reject / no-verdict) are still recorded, because the legacy leg
160
+ // only ever counts policy approvals -- they are attribution, never authority.
161
+ //
162
+ // Nothing is lost: the approval is fully recorded in reviews.md and in the
163
+ // tracked artifact. Satisfying the promotion gate still requires a converged
164
+ // `wp review gate` run (content-addressed v2 ledger) or `wp review override`.
165
+ const mintsGateAuthority = isPolicyApprovalVerdict(entry.verdict);
166
+ if (!mintsGateAuthority) {
167
+ const overviewRaw = readFileSync(found.path, "utf8");
168
+ const parsed = matter(overviewRaw);
169
+ const existingApprovals = Array.isArray(parsed.data.approvals) ? parsed.data.approvals : [];
170
+ const approvalEntry = {
171
+ reviewer: entry.reviewer,
172
+ verdict: entry.verdict,
173
+ evidence: entry.evidence ?? "reviews.md",
174
+ artifact: entry.artifact ?? normalizedArtifactPath,
175
+ ...(entry.commit ? { commit: entry.commit } : {}),
176
+ ...(entry.rev ? { rev: entry.rev } : {}),
177
+ };
178
+ const updatedData = { ...parsed.data, approvals: [...existingApprovals, approvalEntry] };
179
+ const rendered = matter.stringify(parsed.content, updatedData);
180
+ writeFileAtomic(found.path, rendered, { encoding: "utf8" });
181
+ }
160
182
  return finishPayload({
161
- summary: `Logged review ${entry.id} -> ${entry.blueprintSlug} and recorded approvals frontmatter`,
183
+ summary: mintsGateAuthority
184
+ ? `Logged review ${entry.id} -> ${entry.blueprintSlug} in reviews.md (no frontmatter approval recorded: an approval verdict must bind to reviewed content via wp review gate)`
185
+ : `Logged review ${entry.id} -> ${entry.blueprintSlug} and recorded approvals frontmatter`,
162
186
  event_id: entry.id,
163
187
  blueprint_slug: entry.blueprintSlug,
164
188
  reviewer: entry.reviewer,
@@ -41,6 +41,12 @@ export function runValidate(filePath) {
41
41
  gaps.push(`Invalid frontmatter field: last_updated (${lastUpdated})`);
42
42
  }
43
43
  const body = fm.content;
44
+ // Deliberately LOOSER than the canonical grammar in `#markdown/task-heading`
45
+ // (`Task\s+\S` accepts any id, not just `\d+(?:\.\d+)+`, and needs no title).
46
+ // This is a gap REPORTER: it has to see a malformed heading in order to tell
47
+ // the author about it. Converging it onto the strict matcher would make
48
+ // exactly the malformed tasks it exists to catch invisible again — the
49
+ // reported gaps would silently drop to zero. Do not "de-duplicate" this.
44
50
  const taskHeaderRegex = /^####\s+(?:\[[^\]]+\]\s+)?Task\s+\S/m;
45
51
  if (!taskHeaderRegex.test(body))
46
52
  gaps.push('No "#### Task" sections found');
@@ -289,7 +289,7 @@ export async function registerBlueprintTools(registrar, cwd, projectResolver = c
289
289
  next_action: nextActionOutputSchema,
290
290
  },
291
291
  }, (r) => handleTaskVerify(projectResolver, cwd, r), { title: "Blueprint Task Verify", destructiveHint: false, openWorldHint: false });
292
- registrar.registerTool("wp_blueprint_review_log", 'Record a tracked review-ledger entry for a blueprint: writes the given artifact content under blueprints/<state>/<slug>/review-artifacts/, appends the matching structured entry to reviews.md (via the same logReviewEntry writer, distinct from the CLI\'s own `wp review log` subcommand), and appends the corresponding `approvals:` frontmatter entry to _overview.md that the legacy promotion approval path (`collectTrackedReviewProvenance`) reads. A `verdict` of approve or approve-with-nits REQUIRES reviewer="human" this tool cannot mint a claude/codex/grok/OpenCode-Go frontmatter approval; satisfying the strong-reviewer promotion gate still requires either a converged `wp review gate` run or a maintainer directly authoring that frontmatter. Use this tool for reject/no-verdict entries from any reviewer, or a human-adjudicated approve/approve-with-nits — including the audit-trail note when `wp review gate` fails to converge. Returns { event_id, blueprint_slug, reviewer, verdict, status, artifact_path }.', {
292
+ registrar.registerTool("wp_blueprint_review_log", 'Record a tracked review-ledger entry for a blueprint: writes the given artifact content under blueprints/<state>/<slug>/review-artifacts/ and appends the matching structured entry to reviews.md (via the same logReviewEntry writer, distinct from the CLI\'s own `wp review log` subcommand). For reject/no-verdict it ALSO appends the corresponding `approvals:` frontmatter entry to _overview.md, so rejection attribution stays visible. It NEVER writes an approve/approve-with-nits entry into `approvals:`: that field feeds the legacy promotion path (`collectTrackedReviewProvenance`), which never reads or hashes the blueprint body, so an approval recorded there would keep satisfying the gate after the plan was rewritten. A `verdict` of approve or approve-with-nits additionally REQUIRES reviewer="human". Satisfying the promotion gate requires a converged `wp review gate` run (content-addressed v2 ledger) or `wp review override`. Use this tool for reject/no-verdict entries from any reviewer, or to record a human-adjudicated approve/approve-with-nits in reviews.md — including the audit-trail note when `wp review gate` fails to converge. Returns { event_id, blueprint_slug, reviewer, verdict, status, artifact_path }.', {
293
293
  type: "object",
294
294
  properties: {
295
295
  project_id: { type: "string" },
@@ -8,4 +8,4 @@
8
8
  * is enforced by `_names.test.ts` against `COMPILED_TOOL_REGISTRY`, so this list
9
9
  * cannot silently drift.
10
10
  */
11
- export declare const WP_TOOL_NAMES: readonly ["wp_audit", "wp_audits", "wp_bench", "wp_ci_act", "wp_ci_preflight", "wp_e2e", "wp_fleet_status", "wp_format", "wp_gain", "wp_lint", "wp_pr_status", "wp_pr_upsert", "wp_pr_wait", "wp_qa", "wp_release_readiness", "wp_review_run", "wp_run_wait", "wp_session_batch_execute", "wp_session_capture", "wp_session_context", "wp_session_doctor", "wp_session_execute", "wp_session_execute_file", "wp_session_fetch_and_index", "wp_session_id", "wp_session_index", "wp_session_info", "wp_session_purge", "wp_session_retrieve", "wp_session_restore", "wp_session_search", "wp_session_snapshot", "wp_session_stats", "wp_test", "wp_typecheck", "wp_ultragoal_cancel", "wp_ultragoal_handoff", "wp_ultragoal_new", "wp_ultragoal_run", "wp_ultragoal_status", "wp_worker_tail", "wp_worktree"];
11
+ export declare const WP_TOOL_NAMES: readonly ["wp_audit", "wp_audits", "wp_bench", "wp_ci_act", "wp_ci_preflight", "wp_e2e", "wp_fleet_status", "wp_format", "wp_gain", "wp_lint", "wp_pr_status", "wp_pr_upsert", "wp_pr_wait", "wp_qa", "wp_release_progress", "wp_release_readiness", "wp_review_run", "wp_run_wait", "wp_session_batch_execute", "wp_session_capture", "wp_session_context", "wp_session_doctor", "wp_session_execute", "wp_session_execute_file", "wp_session_fetch_and_index", "wp_session_id", "wp_session_index", "wp_session_info", "wp_session_purge", "wp_session_retrieve", "wp_session_restore", "wp_session_search", "wp_session_snapshot", "wp_session_stats", "wp_test", "wp_typecheck", "wp_ui_blocks_list", "wp_ui_compose", "wp_ui_preview", "wp_ultragoal_cancel", "wp_ultragoal_handoff", "wp_ultragoal_new", "wp_ultragoal_run", "wp_ultragoal_status", "wp_worker_tail", "wp_worktree"];
@@ -23,6 +23,7 @@ export const WP_TOOL_NAMES = [
23
23
  "wp_pr_upsert",
24
24
  "wp_pr_wait",
25
25
  "wp_qa",
26
+ "wp_release_progress",
26
27
  "wp_release_readiness",
27
28
  "wp_review_run",
28
29
  "wp_run_wait",
@@ -44,6 +45,9 @@ export const WP_TOOL_NAMES = [
44
45
  "wp_session_stats",
45
46
  "wp_test",
46
47
  "wp_typecheck",
48
+ "wp_ui_blocks_list",
49
+ "wp_ui_compose",
50
+ "wp_ui_preview",
47
51
  "wp_ultragoal_cancel",
48
52
  "wp_ultragoal_handoff",
49
53
  "wp_ultragoal_new",
@@ -12,6 +12,7 @@ import prStatus from "./pr-status.js";
12
12
  import prUpsert from "./pr-upsert.js";
13
13
  import prWait from "./pr-wait.js";
14
14
  import qa from "./qa.js";
15
+ import releaseProgress from "./release-progress.js";
15
16
  import releaseReadiness from "./release-readiness.js";
16
17
  import reviewRun from "./review-run.js";
17
18
  import runWait from "./run-wait.js";
@@ -33,6 +34,9 @@ import sessionSnapshot from "./session-snapshot.js";
33
34
  import sessionStats from "./session-stats.js";
34
35
  import test from "./test.js";
35
36
  import typecheck from "./typecheck.js";
37
+ import uiBlocksList from "./wp-ui-blocks-list.js";
38
+ import uiCompose from "./wp-ui-compose.js";
39
+ import uiPreview from "./wp-ui-preview.js";
36
40
  import ultragoalCancel from "./ultragoal-cancel.js";
37
41
  import ultragoalHandoff from "./ultragoal-handoff.js";
38
42
  import ultragoalNew from "./ultragoal-new.js";
@@ -55,6 +59,7 @@ export const COMPILED_TOOL_REGISTRY = [
55
59
  prUpsert,
56
60
  prWait,
57
61
  qa,
62
+ releaseProgress,
58
63
  releaseReadiness,
59
64
  reviewRun,
60
65
  runWait,
@@ -76,6 +81,9 @@ export const COMPILED_TOOL_REGISTRY = [
76
81
  sessionStats,
77
82
  test,
78
83
  typecheck,
84
+ uiBlocksList,
85
+ uiCompose,
86
+ uiPreview,
79
87
  ultragoalCancel,
80
88
  ultragoalHandoff,
81
89
  ultragoalNew,