@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
|
@@ -4,18 +4,26 @@
|
|
|
4
4
|
* One source of truth for how every supported host launches the webpresso MCP
|
|
5
5
|
* server. Each host channel (Claude plugin manifest, Codex plugin manifest,
|
|
6
6
|
* Codex `config.toml`, OpenCode `opencode.json`, Grok `.grok/config.toml`)
|
|
7
|
-
* projects this spec into its own config dialect. The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* projects this spec into its own config dialect. The Claude plugin manifest
|
|
8
|
+
* references the launcher via `${CLAUDE_PLUGIN_ROOT}`, which Claude Code
|
|
9
|
+
* expands. Codex does **not** expand `${PLUGIN_ROOT}` in a bundled `.mcp.json`
|
|
10
|
+
* — that variable reaches hook commands only — and it spawns bundled MCP
|
|
11
|
+
* servers with the *project* cwd and no plugin env, so the Codex manifest can
|
|
12
|
+
* anchor on neither the plugin root nor a relative path and uses the bare
|
|
13
|
+
* `wp` name resolved from PATH at spawn time. The remaining setup-time writers
|
|
14
|
+
* (Grok, OpenCode) resolve an absolute `bin/wp` path via
|
|
15
|
+
* {@link resolveWebpressoMcpEntry}. Compare channels *semantically* (command
|
|
11
16
|
* basename, args, env) — never byte-for-byte.
|
|
12
17
|
*/
|
|
13
18
|
import { accessSync, constants, existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
14
19
|
import { homedir } from "node:os";
|
|
15
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
20
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
16
21
|
import { execFileSync } from "node:child_process";
|
|
17
22
|
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { isVersionedAgentKitPayloadPath } from "#cli/auto-update/self-invocation.js";
|
|
18
24
|
import { pathCandidates } from "#runtime/command-exists.js";
|
|
25
|
+
import { isManagedWorktreePath, isPathContained } from "#worktrees/identity.js";
|
|
26
|
+
import { resolveKnownManagedWorktreeRoots } from "#worktrees/location.js";
|
|
19
27
|
export const WEBPRESSO_MCP_SERVER_NAME = "webpresso";
|
|
20
28
|
/**
|
|
21
29
|
* The single canonical launch spec for the webpresso MCP server. `name` is the
|
|
@@ -62,23 +70,57 @@ const WP_LAUNCHER_REQUIRED_RELATIVES = [
|
|
|
62
70
|
join("bin", "runtime-lanes.js"),
|
|
63
71
|
join("bin", "runtime-manifest.json"),
|
|
64
72
|
];
|
|
73
|
+
function isUsableWebpressoMcpRoot(root) {
|
|
74
|
+
return WP_LAUNCHER_REQUIRED_RELATIVES.every((relativePath) => existsSync(join(root, relativePath)));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* True when `entryPath` is the stable vite-plus multi-package `wp` shim that
|
|
78
|
+
* dispatches to `@webpresso/agent-kit` via `~/.vite-plus/bins/wp.json`.
|
|
79
|
+
*
|
|
80
|
+
* Unlike {@link isWebpressoWpEntry}, this does **not** require realpath to land
|
|
81
|
+
* inside a package `bin/wp` tree — vite-plus's `wp` is a symlink to the `vp`
|
|
82
|
+
* binary, so package-root ownership is proven via the bins metadata instead.
|
|
83
|
+
*/
|
|
84
|
+
export function isVitePlusManagedWebpressoWp(entryPath) {
|
|
85
|
+
try {
|
|
86
|
+
if (isVersionedAgentKitPayloadPath(entryPath))
|
|
87
|
+
return false;
|
|
88
|
+
const binDir = dirname(entryPath);
|
|
89
|
+
if (basename(binDir) !== "bin")
|
|
90
|
+
return false;
|
|
91
|
+
if (basename(dirname(binDir)) !== ".vite-plus")
|
|
92
|
+
return false;
|
|
93
|
+
const base = basename(entryPath).toLowerCase();
|
|
94
|
+
if (base !== "wp" && base !== "wp.exe")
|
|
95
|
+
return false;
|
|
96
|
+
const metaPath = join(dirname(binDir), "bins", "wp.json");
|
|
97
|
+
const meta = JSON.parse(readFileSync(metaPath, "utf8"));
|
|
98
|
+
return meta.package === "@webpresso/agent-kit";
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function isDurableMcpEntryPath(entryPath) {
|
|
105
|
+
return !isVersionedAgentKitPayloadPath(entryPath);
|
|
106
|
+
}
|
|
65
107
|
/**
|
|
66
108
|
* Resolve the absolute path to webpresso's global `bin/wp` launcher on this
|
|
67
109
|
* machine. Probes the locations consumers use to install webpresso, in order
|
|
68
110
|
* of stability:
|
|
69
111
|
*
|
|
70
|
-
* 1. the stable global `wp` shim available on PATH
|
|
71
|
-
*
|
|
112
|
+
* 1. the stable global `wp` shim available on PATH (including the
|
|
113
|
+
* vite-plus multi-package shim at `~/.vite-plus/bin/wp`)
|
|
114
|
+
* 2. the currently executing `@webpresso/agent-kit` package **only when it
|
|
115
|
+
* is not a vite-plus `agent-kit#<installId>` payload path**
|
|
72
116
|
* 3. Claude plugin install — `~/.claude/plugins/cache/.../agent-kit/`
|
|
73
117
|
* 4. bun global — `~/.bun/install/global/node_modules/@webpresso/agent-kit/`
|
|
74
|
-
*
|
|
118
|
+
*
|
|
119
|
+
* Host MCP configs (Grok/OpenCode) must never be pinned to a versioned
|
|
120
|
+
* installId tree: the next `wp update` / `vp update -g` deletes that path and
|
|
121
|
+
* leaves every host with a dead launcher. `entryPath` is `null` when none of
|
|
122
|
+
* the candidates yield a durable `wp`.
|
|
75
123
|
*/
|
|
76
|
-
export function findWebpressoMcpEntry(probe = {}) {
|
|
77
|
-
return resolveWebpressoMcpEntry(probe).entryPath;
|
|
78
|
-
}
|
|
79
|
-
function isUsableWebpressoMcpRoot(root) {
|
|
80
|
-
return WP_LAUNCHER_REQUIRED_RELATIVES.every((relativePath) => existsSync(join(root, relativePath)));
|
|
81
|
-
}
|
|
82
124
|
export function resolveWebpressoMcpEntry(probe = {}) {
|
|
83
125
|
const checked = [];
|
|
84
126
|
if (probe.candidates === undefined) {
|
|
@@ -88,10 +130,17 @@ export function resolveWebpressoMcpEntry(probe = {}) {
|
|
|
88
130
|
if (stableEntry) {
|
|
89
131
|
checked.push(stableEntry);
|
|
90
132
|
const packageWp = packageWpFromAdjacentNpmCmdShim(stableEntry);
|
|
91
|
-
if (packageWp !== null)
|
|
133
|
+
if (packageWp !== null && isDurableMcpEntryPath(packageWp)) {
|
|
92
134
|
return { entryPath: packageWp, checked };
|
|
135
|
+
}
|
|
93
136
|
const verifierAllows = probe.stableEntryIsWebpresso?.(stableEntry) ?? true;
|
|
94
|
-
|
|
137
|
+
// Prefer the vite-plus multi-package shim before package-root ownership:
|
|
138
|
+
// it survives installId swaps and is what PATH `wp` resolves to in the
|
|
139
|
+
// default consumer install.
|
|
140
|
+
if (verifierAllows && isVitePlusManagedWebpressoWp(stableEntry)) {
|
|
141
|
+
return { entryPath: stableEntry, checked };
|
|
142
|
+
}
|
|
143
|
+
if (verifierAllows && isWebpressoWpEntry(stableEntry) && isDurableMcpEntryPath(stableEntry)) {
|
|
95
144
|
return { entryPath: stableEntry, checked };
|
|
96
145
|
}
|
|
97
146
|
}
|
|
@@ -103,8 +152,12 @@ export function resolveWebpressoMcpEntry(probe = {}) {
|
|
|
103
152
|
continue;
|
|
104
153
|
checked.push(root);
|
|
105
154
|
const wpBin = join(root, WP_BIN_RELATIVE);
|
|
106
|
-
|
|
155
|
+
// Never materialize host MCP configs against a vite-plus installId tree.
|
|
156
|
+
if (!isDurableMcpEntryPath(wpBin))
|
|
157
|
+
continue;
|
|
158
|
+
if (isTrustedWebpressoPackageRoot(root, probe.sourceRoot)) {
|
|
107
159
|
return { entryPath: wpBin, checked };
|
|
160
|
+
}
|
|
108
161
|
}
|
|
109
162
|
}
|
|
110
163
|
return { entryPath: null, checked };
|
|
@@ -152,27 +205,78 @@ function isPathInsideOrEqual(parent, candidate) {
|
|
|
152
205
|
const rel = relative(resolve(parentRoot), resolve(candidateRoot));
|
|
153
206
|
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
154
207
|
}
|
|
155
|
-
function isTrustedWebpressoPackageRoot(packageRoot) {
|
|
208
|
+
function isTrustedWebpressoPackageRoot(packageRoot, resolveSourceRoot = currentAgentKitPackageRoot) {
|
|
156
209
|
try {
|
|
157
210
|
if (!isUsableWebpressoPackageRoot(packageRoot))
|
|
158
211
|
return false;
|
|
159
212
|
const realPackageRoot = realpathSync(packageRoot);
|
|
160
|
-
const sourceRoot =
|
|
161
|
-
|
|
213
|
+
const sourceRoot = resolveSourceRoot();
|
|
214
|
+
// A managed blueprint worktree is deleted by `wp worktree merge-cleanup`
|
|
215
|
+
// once its PR lands, so self-trusting it here would persist a path into
|
|
216
|
+
// host MCP configs that later resolves to nothing. Only the durable
|
|
217
|
+
// primary checkout may earn self-trust.
|
|
218
|
+
const home = process.env.HOME || homedir();
|
|
219
|
+
const isManagedWorktree = isManagedWorktreePath(realPackageRoot, resolveKnownManagedWorktreeRoots(home));
|
|
220
|
+
if (sourceRoot !== null && !isManagedWorktree && realPackageRoot === realpathSync(sourceRoot)) {
|
|
162
221
|
return true;
|
|
222
|
+
}
|
|
163
223
|
return trustedHomeInstallPackageRoots().some((anchor) => existsSync(anchor) && isPathInsideOrEqual(anchor, realPackageRoot));
|
|
164
224
|
}
|
|
165
225
|
catch {
|
|
166
226
|
return false;
|
|
167
227
|
}
|
|
168
228
|
}
|
|
169
|
-
function trustedHomeInstallPackageRoots() {
|
|
170
|
-
const home = process.env.HOME || homedir();
|
|
229
|
+
function trustedHomeInstallPackageRoots(home = process.env.HOME || homedir()) {
|
|
171
230
|
return [
|
|
172
231
|
join(home, ".claude", "plugins", "cache", "webpresso", "agent-kit"),
|
|
173
232
|
join(home, ".bun", "install", "global", "node_modules", "@webpresso", "agent-kit"),
|
|
174
233
|
];
|
|
175
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Every root under which agent-kit itself installs (or checks out) a `bin/wp`.
|
|
237
|
+
* A `wp` launcher recorded inside one of these was written by agent-kit; a
|
|
238
|
+
* launcher anywhere else is an operator's deliberate choice.
|
|
239
|
+
*/
|
|
240
|
+
export function agentKitOwnedCommandRoots(probe = {}) {
|
|
241
|
+
const home = probe.home ?? process.env.HOME ?? homedir();
|
|
242
|
+
const sourceRoot = (probe.sourceRoot ?? currentAgentKitPackageRoot)();
|
|
243
|
+
return [
|
|
244
|
+
...trustedHomeInstallPackageRoots(home),
|
|
245
|
+
join(home, ".vite-plus"),
|
|
246
|
+
...resolveKnownManagedWorktreeRoots(home),
|
|
247
|
+
...(sourceRoot === null ? [] : [sourceRoot]),
|
|
248
|
+
];
|
|
249
|
+
}
|
|
250
|
+
const WP_LAUNCHER_BASENAMES = new Set(["wp", "wp.exe"]);
|
|
251
|
+
/**
|
|
252
|
+
* Ownership — not shape — decides whether setup may retire a persisted MCP
|
|
253
|
+
* entry. An entry is agent-kit's own when it launches `wp mcp` from an
|
|
254
|
+
* ABSOLUTE path AND either that path is gone (one we would have deleted) or
|
|
255
|
+
* it lives inside one
|
|
256
|
+
* of {@link agentKitOwnedCommandRoots}. A *live* `wp mcp` launcher outside
|
|
257
|
+
* those roots is an operator pointing Codex at a custom build: preserve it.
|
|
258
|
+
*/
|
|
259
|
+
export function isAgentKitOwnedWpCommand(command, args, probe = {}) {
|
|
260
|
+
if (!WP_LAUNCHER_BASENAMES.has(basename(command).toLowerCase()))
|
|
261
|
+
return false;
|
|
262
|
+
if (args.length !== WEBPRESSO_MCP_SPEC.args.length)
|
|
263
|
+
return false;
|
|
264
|
+
if (args.some((value, index) => value !== WEBPRESSO_MCP_SPEC.args[index]))
|
|
265
|
+
return false;
|
|
266
|
+
// Every agent-kit writer of this block persisted an ABSOLUTE launcher path
|
|
267
|
+
// (see resolveWebpressoMcpEntry). A bare PATH name — the spelling the Codex
|
|
268
|
+
// plugin manifest itself ships — or a cwd-relative path is therefore
|
|
269
|
+
// operator-authored, and its meaning depends on a PATH/cwd this predicate
|
|
270
|
+
// cannot reconstruct. Preserve it and let the doctor report it.
|
|
271
|
+
if (!isAbsolute(command))
|
|
272
|
+
return false;
|
|
273
|
+
const exists = probe.exists ?? existsSync;
|
|
274
|
+
// Only an absolute path can have been written by us and then rotted away
|
|
275
|
+
// (deleted managed worktree, replaced versioned install tree).
|
|
276
|
+
if (!exists(command))
|
|
277
|
+
return true;
|
|
278
|
+
return agentKitOwnedCommandRoots(probe).some((root) => isPathContained(root, command));
|
|
279
|
+
}
|
|
176
280
|
function packageWpFromAdjacentNpmCmdShim(entryPath) {
|
|
177
281
|
if (!entryPath.toLowerCase().endsWith(".cmd"))
|
|
178
282
|
return null;
|
|
@@ -9,10 +9,21 @@
|
|
|
9
9
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, readdirSync } from "node:fs";
|
|
10
10
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
11
11
|
import { convergeUnitFromTarget } from "./convergence.js";
|
|
12
|
-
import { resolveUntrackedCollision } from "./untracked-collision.js";
|
|
13
|
-
|
|
12
|
+
import { isGitIgnored, resolveUntrackedCollision } from "./untracked-collision.js";
|
|
13
|
+
/**
|
|
14
|
+
* Guard an overwrite against clobbering unknown untracked content. Returns a
|
|
15
|
+
* warning to surface to the caller when the write proceeded past divergent
|
|
16
|
+
* content it could safely recover from (git-ignored lane only); throws when
|
|
17
|
+
* the write must be blocked.
|
|
18
|
+
*
|
|
19
|
+
* `patchDerived` must be true only when `incoming` was computed FROM the
|
|
20
|
+
* target's existing content (e.g. `patchJsonFile`'s key-preserving patch) —
|
|
21
|
+
* that is what makes it safe to write through divergent unmarked content
|
|
22
|
+
* without a backup.
|
|
23
|
+
*/
|
|
24
|
+
function guardUntrackedOverwrite(targetPath, incoming, opts, patchDerived) {
|
|
14
25
|
if (!opts.repoRoot)
|
|
15
|
-
return;
|
|
26
|
+
return undefined;
|
|
16
27
|
const repoRoot = resolve(opts.repoRoot);
|
|
17
28
|
const target = resolve(targetPath);
|
|
18
29
|
const repoRelative = relative(repoRoot, target);
|
|
@@ -20,19 +31,25 @@ function guardUntrackedOverwrite(targetPath, incoming, opts) {
|
|
|
20
31
|
repoRelative === ".." ||
|
|
21
32
|
repoRelative.startsWith(`..${sep}`) ||
|
|
22
33
|
isAbsolute(repoRelative)) {
|
|
23
|
-
return;
|
|
34
|
+
return undefined;
|
|
24
35
|
}
|
|
36
|
+
const gitPath = repoRelative.split(sep).join("/");
|
|
37
|
+
const scope = isGitIgnored(repoRoot, gitPath) ? "repo-ignored" : "repo-committed";
|
|
25
38
|
const result = resolveUntrackedCollision({
|
|
26
39
|
unit: convergeUnitFromTarget(repoRoot, target, {
|
|
27
40
|
action: "modify",
|
|
28
|
-
scope
|
|
41
|
+
scope,
|
|
29
42
|
reason: "setup merge target",
|
|
30
43
|
}),
|
|
31
44
|
desiredContent: incoming,
|
|
32
45
|
force: opts.overwrite,
|
|
46
|
+
patchDerived,
|
|
33
47
|
});
|
|
34
48
|
if (!result.ok)
|
|
35
49
|
throw new Error(result.error);
|
|
50
|
+
if (result.warning)
|
|
51
|
+
console.error(`wp setup: ${result.warning}`);
|
|
52
|
+
return result.warning;
|
|
36
53
|
}
|
|
37
54
|
export function writeFileMerged(targetPath, incoming, opts = {}) {
|
|
38
55
|
const exists = existsSync(targetPath);
|
|
@@ -51,9 +68,9 @@ export function writeFileMerged(targetPath, incoming, opts = {}) {
|
|
|
51
68
|
if (shouldOverwrite) {
|
|
52
69
|
if (opts.dryRun)
|
|
53
70
|
return { targetPath, action: "skipped-dry" };
|
|
54
|
-
guardUntrackedOverwrite(targetPath, incoming, opts);
|
|
71
|
+
const warning = guardUntrackedOverwrite(targetPath, incoming, opts, false);
|
|
55
72
|
writeFileSync(targetPath, incoming);
|
|
56
|
-
return { targetPath, action: "overwritten" };
|
|
73
|
+
return { targetPath, action: "overwritten", ...(warning ? { note: warning } : {}) };
|
|
57
74
|
}
|
|
58
75
|
if (opts.dryRun)
|
|
59
76
|
return { targetPath, action: "skipped-dry" };
|
|
@@ -119,11 +136,14 @@ export function patchJsonFile(targetPath, patcher, opts = {}) {
|
|
|
119
136
|
if (opts.dryRun) {
|
|
120
137
|
return { targetPath, action: "skipped-dry" };
|
|
121
138
|
}
|
|
122
|
-
|
|
123
|
-
guardUntrackedOverwrite(targetPath, incoming, opts);
|
|
139
|
+
const warning = exists ? guardUntrackedOverwrite(targetPath, incoming, opts, true) : undefined;
|
|
124
140
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
125
141
|
writeFileSync(targetPath, incoming);
|
|
126
|
-
return {
|
|
142
|
+
return {
|
|
143
|
+
targetPath,
|
|
144
|
+
action: exists ? "overwritten" : "created",
|
|
145
|
+
...(warning ? { note: warning } : {}),
|
|
146
|
+
};
|
|
127
147
|
}
|
|
128
148
|
export function summarizeResults(results) {
|
|
129
149
|
const summary = {
|
|
@@ -30,13 +30,11 @@ const TECH_STACK_RULES = [
|
|
|
30
30
|
{ dep: /^typescript$/, label: "TypeScript" },
|
|
31
31
|
];
|
|
32
32
|
// Kept under the 8 KB (8192-byte) hard ceiling. The rendered template embeds
|
|
33
|
-
// the full `native_tool_names` registry list
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// breach it should trigger a conscious restructure (or exclude the
|
|
39
|
-
// auto-generated tool list from the prose budget), not another reflexive bump.
|
|
33
|
+
// the full `native_tool_names` registry list (~15-17 bytes per tool). Soft
|
|
34
|
+
// target 8_180 leaves headroom under 8_192. The ui-compose batch
|
|
35
|
+
// (wp_ui_blocks_list, wp_ui_compose, wp_ui_preview) required trimming catalog
|
|
36
|
+
// AGENTS.md.tpl prose rather than raising the ceiling. Next breach of 8_192
|
|
37
|
+
// should restructure (or stop embedding the full tool list), not bump again.
|
|
40
38
|
export const AGENTS_MD_MAX_BYTES = 8_180;
|
|
41
39
|
export function renderRepositoryMap(consumer) {
|
|
42
40
|
const packages = consumer.workspacePackages;
|
|
@@ -6,21 +6,18 @@
|
|
|
6
6
|
* the patch tiny and deterministic: per-server upserts, no TOML parser
|
|
7
7
|
* dependency, no edits to unrelated user config.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* 2. Consumer-owned `.mcp.json` package declarations — compiled to setup-
|
|
18
|
-
* provisioned, exact-version binaries with environment-variable names
|
|
19
|
-
* forwarded through Codex's stdio `env_vars` allow-list.
|
|
9
|
+
* Webpresso's own MCP server is NOT one of them. Codex receives `webpresso`
|
|
10
|
+
* over exactly one delivery channel — the bundled Codex plugin's `.mcp.json`.
|
|
11
|
+
* Setup therefore *retires* the `[mcp_servers.webpresso]` block earlier
|
|
12
|
+
* versions wrote here (see {@link pruneLegacyCodexWebpressoMcp}) instead of
|
|
13
|
+
* refreshing it. What remains managed here is consumer-owned `.mcp.json`
|
|
14
|
+
* package declarations — compiled to setup-provisioned, exact-version binaries
|
|
15
|
+
* with environment-variable names forwarded through Codex's stdio `env_vars`
|
|
16
|
+
* allow-list.
|
|
20
17
|
*/
|
|
21
18
|
import { spawnSync } from "node:child_process";
|
|
22
|
-
import {
|
|
23
|
-
export {
|
|
19
|
+
import { WEBPRESSO_MCP_SERVER_NAME, type AgentKitOwnedCommandProbe, type WebpressoInstallProbe } from "#cli/commands/init/mcp-spec.js";
|
|
20
|
+
export { WEBPRESSO_MCP_SERVER_NAME };
|
|
24
21
|
export type { WebpressoInstallProbe };
|
|
25
22
|
import type { MergeOptions } from "#cli/commands/init/merge";
|
|
26
23
|
import { type PinnedCodexMcpDefinition } from "#hooks/host-config-contract.js";
|
|
@@ -143,35 +140,63 @@ export type EnsureClaudePlaywrightMcpResult = {
|
|
|
143
140
|
path: string;
|
|
144
141
|
};
|
|
145
142
|
export declare function ensureClaudePlaywrightMcp(input: EnsureClaudePlaywrightMcpInput): EnsureClaudePlaywrightMcpResult;
|
|
146
|
-
export
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
export type PruneOwnedMcpServerOutcome = {
|
|
144
|
+
readonly kind: "removed";
|
|
145
|
+
readonly raw: string;
|
|
146
|
+
readonly command: string;
|
|
147
|
+
} | {
|
|
148
|
+
readonly kind: "absent";
|
|
149
|
+
readonly raw: string;
|
|
150
|
+
} | {
|
|
151
|
+
readonly kind: "preserved";
|
|
152
|
+
readonly raw: string;
|
|
153
|
+
readonly command: string | null;
|
|
154
|
+
} | {
|
|
155
|
+
readonly kind: "ambiguous";
|
|
156
|
+
readonly raw: string;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Remove `[mcp_servers.<serverId>]` (plus its contiguous sub-tables) when
|
|
160
|
+
* `isOwned` claims the block's launch tuple. Fails closed — leaving `raw`
|
|
161
|
+
* byte-identical — for every TOML spelling this line scanner cannot rewrite
|
|
162
|
+
* safely (dotted keys, inline parent table, array tables, non-contiguous
|
|
163
|
+
* sub-tables).
|
|
164
|
+
*/
|
|
165
|
+
export declare function pruneOwnedMcpServerBlock(raw: string, serverId: string, isOwned: (command: string, args: readonly string[]) => boolean): PruneOwnedMcpServerOutcome;
|
|
166
|
+
export interface PruneLegacyCodexWebpressoMcpInput {
|
|
149
167
|
options: MergeOptions;
|
|
150
|
-
/** Test seam — override the resolved MCP entry path. */
|
|
151
|
-
entryPath?: string;
|
|
152
168
|
/** Test seam — override `$CODEX_HOME/config.toml`. */
|
|
153
169
|
configPath?: string;
|
|
154
|
-
/** Test seam — override
|
|
155
|
-
|
|
170
|
+
/** Test seam — override agent-kit install-root ownership resolution. */
|
|
171
|
+
ownership?: AgentKitOwnedCommandProbe;
|
|
156
172
|
}
|
|
157
|
-
export type
|
|
158
|
-
kind: "codex-webpresso-mcp-
|
|
173
|
+
export type PruneLegacyCodexWebpressoMcpResult = {
|
|
174
|
+
kind: "codex-legacy-webpresso-mcp-removed";
|
|
159
175
|
path: string;
|
|
160
|
-
|
|
176
|
+
command: string;
|
|
161
177
|
} | {
|
|
162
|
-
kind: "codex-webpresso-mcp-
|
|
178
|
+
kind: "codex-legacy-webpresso-mcp-absent";
|
|
163
179
|
path: string;
|
|
164
|
-
entryPath: string;
|
|
165
180
|
} | {
|
|
166
|
-
kind: "codex-webpresso-mcp-
|
|
181
|
+
kind: "codex-legacy-webpresso-mcp-preserved";
|
|
167
182
|
path: string;
|
|
168
|
-
|
|
183
|
+
command: string | null;
|
|
169
184
|
} | {
|
|
170
|
-
kind: "codex-webpresso-mcp-
|
|
185
|
+
kind: "codex-legacy-webpresso-mcp-ambiguous";
|
|
171
186
|
path: string;
|
|
172
187
|
} | {
|
|
173
|
-
kind: "codex-webpresso-mcp-
|
|
188
|
+
kind: "codex-legacy-webpresso-mcp-skipped-dry-run";
|
|
174
189
|
path: string;
|
|
175
|
-
checked: readonly string[];
|
|
176
190
|
};
|
|
177
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Retire the legacy user-global `[mcp_servers.webpresso]` block. Runs from
|
|
193
|
+
* setup AND from the `wp codex` launch preflight — the launch path is the only
|
|
194
|
+
* one that reaches machines that never re-run setup.
|
|
195
|
+
*/
|
|
196
|
+
export declare function pruneLegacyCodexWebpressoMcp(input: PruneLegacyCodexWebpressoMcpInput): PruneLegacyCodexWebpressoMcpResult;
|
|
197
|
+
/**
|
|
198
|
+
* Installed Codex plugin MCP manifests — the surviving delivery channel.
|
|
199
|
+
* Cheap directory probe only: no `codex plugin` subprocess belongs on the
|
|
200
|
+
* launch hot path.
|
|
201
|
+
*/
|
|
202
|
+
export declare function findInstalledCodexPluginMcpManifests(homeDir?: string): readonly string[];
|