@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.
- package/THIRD-PARTY-NOTICES.md +12 -10
- package/catalog/AGENTS.md.tpl +3 -5
- package/catalog/agent/rules/changeset-release.md +15 -4
- package/catalog/agent/rules/managed-tool-binaries.md +67 -0
- package/catalog/agent/rules/parallel-orchestration-quota.md +99 -1
- package/catalog/agent/rules/pre-implementation.md +36 -20
- package/catalog/agent/rules/rtk-routing.md +22 -16
- package/catalog/agent/skills/autopilot/SKILL.md +4 -3
- package/catalog/agent/skills/ultragoal/SKILL.md +11 -9
- package/catalog/agent/skills/verify/SKILL.md +12 -12
- package/catalog/compose/admin-blocks-registry.json +659 -0
- package/dist/esm/audit/hook-surface.js +5 -1
- package/dist/esm/audit/repo-guardrails.d.ts +8 -0
- package/dist/esm/audit/repo-guardrails.js +71 -1
- package/dist/esm/blueprint/core/parser.js +40 -16
- package/dist/esm/blueprint/core/validation/criteria.js +2 -1
- package/dist/esm/blueprint/execution/artifacts.js +8 -4
- package/dist/esm/blueprint/execution/frontmatter-keys.d.ts +17 -0
- package/dist/esm/blueprint/execution/frontmatter-keys.js +24 -0
- package/dist/esm/blueprint/execution/metadata.js +8 -4
- package/dist/esm/blueprint/lifecycle/audit.d.ts +21 -0
- package/dist/esm/blueprint/lifecycle/audit.js +89 -3
- package/dist/esm/blueprint/lifecycle/review-provenance.js +148 -40
- package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +22 -0
- package/dist/esm/blueprint/lifecycle/rollout-anchor.js +37 -1
- package/dist/esm/blueprint/markdown/blocked-line.d.ts +49 -0
- package/dist/esm/blueprint/markdown/blocked-line.js +63 -0
- package/dist/esm/blueprint/markdown/helpers.js +10 -4
- package/dist/esm/blueprint/tracked-document/parser.js +3 -3
- package/dist/esm/blueprint/trust/gate-row.d.ts +55 -0
- package/dist/esm/blueprint/trust/gate-row.js +113 -0
- package/dist/esm/blueprint/trust/promotion.d.ts +17 -0
- package/dist/esm/blueprint/trust/promotion.js +19 -19
- package/dist/esm/build/atomic-file-copy.d.ts +32 -0
- package/dist/esm/build/atomic-file-copy.js +44 -0
- package/dist/esm/build/cli-mcp-parity.js +3 -2
- package/dist/esm/build/package-manifest.js +7 -2
- package/dist/esm/build/vendor-oxlint-plugins.d.ts +8 -0
- package/dist/esm/build/vendor-oxlint-plugins.js +53 -0
- package/dist/esm/cli/commands/audit-core.js +9 -1
- package/dist/esm/cli/commands/audit.js +16 -5
- package/dist/esm/cli/commands/blueprint/mutations.d.ts +15 -0
- package/dist/esm/cli/commands/blueprint/mutations.js +8 -2
- package/dist/esm/cli/commands/compile.d.ts +13 -0
- package/dist/esm/cli/commands/compile.js +182 -62
- package/dist/esm/cli/commands/format.js +17 -1
- package/dist/esm/cli/commands/gain/index.js +10 -3
- package/dist/esm/cli/commands/init/gitignore-patcher.js +6 -0
- package/dist/esm/cli/commands/init/index.js +29 -48
- package/dist/esm/cli/commands/init/mcp-spec.d.ts +54 -4
- package/dist/esm/cli/commands/init/mcp-spec.js +126 -22
- package/dist/esm/cli/commands/init/merge.js +30 -10
- package/dist/esm/cli/commands/init/scaffold-agents-md.js +5 -7
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +56 -31
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +180 -43
- package/dist/esm/cli/commands/init/scaffolders/rtk/index.d.ts +17 -5
- package/dist/esm/cli/commands/init/scaffolders/rtk/index.js +56 -75
- package/dist/esm/cli/commands/init/scaffolders/runtime-check/index.js +7 -6
- package/dist/esm/cli/commands/init/untracked-collision.d.ts +12 -1
- package/dist/esm/cli/commands/init/untracked-collision.js +23 -1
- package/dist/esm/cli/commands/package-manager.js +8 -2
- package/dist/esm/cli/commands/pr-help.js +1 -1
- package/dist/esm/cli/commands/pr.js +18 -4
- package/dist/esm/cli/commands/review.js +60 -6
- package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +24 -2
- package/dist/esm/cli/commands/worktree/router-dispatch.js +80 -48
- package/dist/esm/cli/commands/worktree/router.js +5 -2
- package/dist/esm/cli/direct-provider-launch.js +12 -4
- package/dist/esm/compiler/flatten.d.ts +10 -1
- package/dist/esm/compiler/flatten.js +14 -4
- package/dist/esm/compose/admin-blocks-registry-integrity.d.ts +22 -0
- package/dist/esm/compose/admin-blocks-registry-integrity.js +90 -0
- package/dist/esm/compose/compose.d.ts +46 -0
- package/dist/esm/compose/compose.js +250 -0
- package/dist/esm/compose/escape-html.d.ts +5 -0
- package/dist/esm/compose/escape-html.js +12 -0
- package/dist/esm/compose/ops-report-render.d.ts +5 -0
- package/dist/esm/compose/ops-report-render.js +71 -0
- package/dist/esm/compose/ops-report-schema.d.ts +42 -0
- package/dist/esm/compose/ops-report-schema.js +60 -0
- package/dist/esm/compose/preview.d.ts +14 -0
- package/dist/esm/compose/preview.js +32 -0
- package/dist/esm/compose/registry-resolve.d.ts +64 -0
- package/dist/esm/compose/registry-resolve.js +200 -0
- package/dist/esm/config/oxlint/graphql-conventions.js +2 -5
- package/dist/esm/config/oxlint/import-hygiene.js +21 -8
- package/dist/esm/config/oxlint/oxlint-config.fixture.d.ts +1 -1
- package/dist/esm/config/oxlint/oxlint-config.fixture.js +2 -3
- package/dist/esm/config/oxlint/oxlintrc.d.ts +5 -7
- package/dist/esm/config/oxlint/oxlintrc.js +18 -21
- package/dist/esm/config/oxlint/path-roles.js +53 -0
- package/dist/esm/config/oxlint/query-patterns.js +2 -5
- package/dist/esm/daemon/domains/blueprint-exec-ledger.d.ts +86 -0
- package/dist/esm/daemon/domains/blueprint-exec-ledger.js +134 -0
- package/dist/esm/daemon/domains/blueprint-exec.d.ts +106 -0
- package/dist/esm/daemon/domains/blueprint-exec.js +161 -0
- package/dist/esm/daemon/domains/launch.d.ts +25 -0
- package/dist/esm/daemon/domains/launch.js +30 -1
- package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +12 -1
- package/dist/esm/daemon/handlers/blueprints-handlers.js +148 -6
- package/dist/esm/daemon/handlers/product.d.ts +10 -0
- package/dist/esm/daemon/handlers/product.js +10 -1
- package/dist/esm/daemon/methods.d.ts +21 -1
- package/dist/esm/daemon/methods.js +20 -0
- package/dist/esm/daemon/protocol/contract.d.ts +58 -0
- package/dist/esm/daemon/protocol/contract.js +38 -0
- package/dist/esm/daemon/server.js +7 -0
- package/dist/esm/docs-linter/blueprint-plan.js +2 -2
- package/dist/esm/errors/wp-error.js +21 -2
- package/dist/esm/git/changed-files.d.ts +20 -0
- package/dist/esm/git/changed-files.js +63 -0
- package/dist/esm/hooks/doctor.d.ts +11 -0
- package/dist/esm/hooks/doctor.js +114 -45
- package/dist/esm/hooks/pretool-guard/dev-routing.js +1 -1
- package/dist/esm/hooks/pretool-guard/rtk-rewrite.d.ts +24 -0
- package/dist/esm/hooks/pretool-guard/rtk-rewrite.js +50 -0
- package/dist/esm/hooks/pretool-guard/runner.js +6 -0
- package/dist/esm/hooks/status/index.js +1 -1
- package/dist/esm/hooks/stop/qa-changed-files.js +6 -1
- package/dist/esm/mcp/blueprint/_shared/lifecycle.js +39 -11
- package/dist/esm/mcp/blueprint/handlers/review-log.d.ts +14 -7
- package/dist/esm/mcp/blueprint/handlers/review-log.js +46 -22
- package/dist/esm/mcp/blueprint/handlers/validate.js +6 -0
- package/dist/esm/mcp/blueprint/registration.js +1 -1
- package/dist/esm/mcp/tools/_names.d.ts +1 -1
- package/dist/esm/mcp/tools/_names.js +4 -0
- package/dist/esm/mcp/tools/_registry.js +8 -0
- package/dist/esm/mcp/tools/_shared/redact.d.ts +22 -0
- package/dist/esm/mcp/tools/_shared/redact.js +114 -8
- package/dist/esm/mcp/tools/gain.js +28 -2
- package/dist/esm/mcp/tools/release-progress.d.ts +9 -0
- package/dist/esm/mcp/tools/release-progress.js +110 -0
- package/dist/esm/mcp/tools/worktree.js +65 -3
- package/dist/esm/mcp/tools/wp-ui-blocks-list.d.ts +3 -0
- package/dist/esm/mcp/tools/wp-ui-blocks-list.js +43 -0
- package/dist/esm/mcp/tools/wp-ui-compose.d.ts +3 -0
- package/dist/esm/mcp/tools/wp-ui-compose.js +96 -0
- package/dist/esm/mcp/tools/wp-ui-preview.d.ts +3 -0
- package/dist/esm/mcp/tools/wp-ui-preview.js +59 -0
- package/dist/esm/package.json +2 -0
- package/dist/esm/review/lifecycle-writers.d.ts +76 -0
- package/dist/esm/review/lifecycle-writers.js +330 -0
- package/dist/esm/review/subject.js +66 -32
- package/dist/esm/status/snapshot.d.ts +5 -0
- package/dist/esm/status/snapshot.js +27 -2
- package/dist/esm/test/shard-durations.json +0 -1
- package/dist/esm/test-helpers/global-setup.js +38 -3
- package/dist/esm/tool-runtime/managed-rtk.d.ts +44 -0
- package/dist/esm/tool-runtime/managed-rtk.js +275 -0
- package/dist/esm/tool-runtime/resolve-runner.js +18 -194
- package/dist/esm/utils/package-root.d.ts +32 -0
- package/dist/esm/utils/package-root.js +82 -0
- package/dist/esm/worktrees/git-metadata.d.ts +41 -0
- package/dist/esm/worktrees/git-metadata.js +48 -0
- package/package.json +27 -21
- package/dist/esm/config/oxlint/code-safety.d.ts +0 -18
- package/dist/esm/config/oxlint/foundation-purity.d.ts +0 -21
- package/dist/esm/config/oxlint/graphql-conventions.d.ts +0 -23
- package/dist/esm/config/oxlint/import-hygiene.d.ts +0 -34
- package/dist/esm/config/oxlint/index.d.ts +0 -22
- package/dist/esm/config/oxlint/index.js +0 -28
- package/dist/esm/config/oxlint/monorepo-paths.d.ts +0 -22
- package/dist/esm/config/oxlint/query-patterns.d.ts +0 -24
- package/dist/esm/config/oxlint/testing-quality.d.ts +0 -35
- package/dist/esm/config/oxlint/tier-boundaries.d.ts +0 -35
- package/dist/esm/output-transforms/rulesync.d.ts +0 -2
- package/dist/esm/output-transforms/rulesync.js +0 -79
package/dist/esm/hooks/doctor.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
|
|
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
|
|
1188
|
-
if (
|
|
1189
|
-
|
|
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 !== "
|
|
1434
|
-
|
|
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`
|
|
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 === "
|
|
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
|
-
|
|
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(
|
|
103
|
+
renameSync(sourcePath, destinationPath);
|
|
104
|
+
renamed = true;
|
|
101
105
|
finalOverviewPath = found.shape === "flat" ? destination.flat : destination.folder;
|
|
102
106
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* trust model a hand-authored `approvals:` block already had (the
|
|
9
|
-
* is caller-asserted, verified only by the referenced
|
|
10
|
-
* entry being tracked in git) — this tool just makes that
|
|
11
|
-
* reachable from MCP instead of a raw Write/Edit or a Bash/python
|
|
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
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* trust model a hand-authored `approvals:` block already had (the
|
|
37
|
-
* is caller-asserted, verified only by the referenced
|
|
38
|
-
* entry being tracked in git) — this tool just makes that
|
|
39
|
-
* reachable from MCP instead of a raw Write/Edit or a Bash/python
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
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:
|
|
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
|
|
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,
|