@webpresso/agent-kit 3.3.3 → 3.3.5
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/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/dist/esm/audit/agents.js +126 -26
- 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 +38 -12
- 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 +63 -10
- 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/dossier.js +41 -1
- 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/vendor-oxlint-plugins.d.ts +11 -0
- package/dist/esm/build/vendor-oxlint-plugins.js +48 -0
- 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 +32 -52
- package/dist/esm/cli/commands/init/mcp-spec.d.ts +48 -13
- package/dist/esm/cli/commands/init/mcp-spec.js +86 -30
- package/dist/esm/cli/commands/init/merge.js +30 -10
- 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/pr-help.js +1 -1
- package/dist/esm/cli/commands/pr.js +18 -4
- package/dist/esm/cli/commands/review.d.ts +1 -0
- package/dist/esm/cli/commands/review.js +137 -7
- package/dist/esm/cli/commands/sync.d.ts +8 -6
- package/dist/esm/cli/commands/sync.js +11 -9
- 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 +8 -0
- package/dist/esm/compose/compose.js +115 -8
- package/dist/esm/compose/ops-report-schema.js +17 -3
- package/dist/esm/compose/registry-resolve.d.ts +17 -0
- package/dist/esm/compose/registry-resolve.js +78 -11
- 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 +19 -31
- package/dist/esm/config/oxlint/path-roles.js +53 -0
- package/dist/esm/config/oxlint/query-patterns.js +2 -5
- package/dist/esm/content/dispatch.js +4 -0
- package/dist/esm/content/loader.d.ts +1 -1
- package/dist/esm/content/loader.js +10 -6
- package/dist/esm/content/skill-policy.d.ts +11 -0
- package/dist/esm/content/skill-policy.js +33 -0
- 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/hooks/doctor.d.ts +11 -0
- package/dist/esm/hooks/doctor.js +114 -45
- package/dist/esm/hooks/pretool-guard/dev-routing.js +68 -5
- 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/pretool-guard/validators/worktree-discipline.js +62 -3
- package/dist/esm/hooks/status/index.js +1 -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/registration.js +1 -1
- 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/worktree.js +65 -3
- package/dist/esm/mcp/tools/wp-ui-compose.js +38 -4
- package/dist/esm/mcp/tools/wp-ui-preview.js +14 -7
- package/dist/esm/review/authority.js +56 -1
- package/dist/esm/review/delivery-verifier.d.ts +4 -0
- package/dist/esm/review/delivery-verifier.js +9 -1
- 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/symlinker/consumers.d.ts +3 -1
- package/dist/esm/symlinker/consumers.js +7 -1
- package/dist/esm/symlinker/unified-sync.d.ts +3 -3
- package/dist/esm/symlinker/unified-sync.js +88 -11
- package/dist/esm/test/shard-durations.json +0 -2
- 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 +16 -21
- package/dist/esm/cli/commands/init/scaffolders/subagents/index.d.ts +0 -6
- package/dist/esm/cli/commands/init/scaffolders/subagents/index.js +0 -77
- 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
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { SessionMemoryStore } from "#session-memory/store.js";
|
|
3
3
|
import { defaultIndexDbPath } from "#mcp/tools/session-restore.js";
|
|
4
|
+
import { resolveManagedRtkExecutable } from "#tool-runtime/managed-rtk.js";
|
|
4
5
|
export function runGain(options = {}) {
|
|
5
6
|
const dbPath = options.indexDbPath ?? defaultIndexDbPath(options.cwd);
|
|
6
7
|
const webpressoStats = readWebpressoGainStats(dbPath);
|
|
7
8
|
printWebpressoGain(webpressoStats, dbPath, { sessions: options.sessions === true });
|
|
8
9
|
console.log("\n── RTK Gain Totals ──────────────────────────────────────────");
|
|
9
|
-
const
|
|
10
|
+
const rtkBin = resolveManagedRtkExecutable();
|
|
11
|
+
if (!rtkBin) {
|
|
12
|
+
console.log(" Managed RTK not available.");
|
|
13
|
+
console.log(" Enable: wp setup --with rtk (or set WP_RTK_BIN to a capable rtk binary)");
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
const rtk = spawnSync(rtkBin, ["gain", "--format", "json"], { encoding: "utf8" });
|
|
10
17
|
if (rtk.error) {
|
|
11
18
|
const err = rtk.error;
|
|
12
19
|
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
13
|
-
console.log(" RTK
|
|
14
|
-
console.log(" Enable: wp setup --with rtk
|
|
20
|
+
console.log(" Managed RTK binary missing at resolved path.");
|
|
21
|
+
console.log(" Enable: wp setup --with rtk (or set WP_RTK_BIN)");
|
|
15
22
|
return 0;
|
|
16
23
|
}
|
|
17
24
|
throw err;
|
|
@@ -76,6 +76,12 @@ export const GENERATED_PATHS_BLOCK = {
|
|
|
76
76
|
".agent/.blueprints.lock",
|
|
77
77
|
".agent/.blueprints.snapshot.sql",
|
|
78
78
|
".agent/.tail-hint-history.jsonl",
|
|
79
|
+
"# `wp compile` derives .rulesync/ from the canonical agent sources and replaces",
|
|
80
|
+
"# it wholesale on every run, so it is a build artifact rather than the",
|
|
81
|
+
"# hand-authored source tree rulesync's own docs describe. The .tmp- dirs are",
|
|
82
|
+
"# its staging area, normally renamed away but left behind by an interrupt.",
|
|
83
|
+
".rulesync/",
|
|
84
|
+
".rulesync.tmp-*",
|
|
79
85
|
"# Stryker writes these mutation-test artifacts beside consumer source trees.",
|
|
80
86
|
".stryker-tmp/",
|
|
81
87
|
"reports/mutation/",
|
|
@@ -54,14 +54,13 @@ import { ensureClaudeCodeUserPlugin } from "./scaffolders/claude-plugin/index.js
|
|
|
54
54
|
import { ensureCodexUserPlugin } from "./scaffolders/codex-plugin/index.js";
|
|
55
55
|
import { scaffoldClaudeRules } from "./scaffolders/claude-rules/index.js";
|
|
56
56
|
import { ensureCodexCli } from "./scaffolders/codex-cli/index.js";
|
|
57
|
-
import { ensureClaudePlaywrightMcp, ensureCodexProjectMcpServers,
|
|
57
|
+
import { ensureClaudePlaywrightMcp, ensureCodexProjectMcpServers, pruneLegacyCodexWebpressoMcp, } from "./scaffolders/codex-mcp/index.js";
|
|
58
58
|
import { scaffoldExampleSkill } from "./scaffolders/example-skill/index.js";
|
|
59
59
|
import { scaffoldLoreCommits } from "./scaffolders/lore-commits/index.js";
|
|
60
60
|
import { ensureOpenCodeWebpressoPlugin } from "./scaffolders/opencode-plugin/index.js";
|
|
61
61
|
import { ensureGrokWebpressoMcp } from "./scaffolders/grok-mcp/index.js";
|
|
62
62
|
import { ensureRtk } from "./scaffolders/rtk/index.js";
|
|
63
63
|
import { checkRuntimes } from "./scaffolders/runtime-check/index.js";
|
|
64
|
-
import { scaffoldSubagents } from "./scaffolders/subagents/index.js";
|
|
65
64
|
import { maybeRunVisionInterview } from "./scaffolders/vision/interview.js";
|
|
66
65
|
import { scaffoldVision } from "./scaffolders/vision/index.js";
|
|
67
66
|
import { scaffoldWorkspaceConfig } from "./scaffolders/workspace-config/index.js";
|
|
@@ -381,24 +380,7 @@ async function runUserOnlySetup(input) {
|
|
|
381
380
|
console.log(" playwright mcp: - skipped (the preset requires a project-owned .mcp.json; run setup inside the project repo)");
|
|
382
381
|
}
|
|
383
382
|
if (selectedHosts.includes("codex")) {
|
|
384
|
-
|
|
385
|
-
switch (webpressoMcpResult.kind) {
|
|
386
|
-
case "codex-webpresso-mcp-written":
|
|
387
|
-
console.log(` codex webpresso mcp: ✓ ${webpressoMcpResult.path} → ${webpressoMcpResult.entryPath}`);
|
|
388
|
-
break;
|
|
389
|
-
case "codex-webpresso-mcp-unchanged":
|
|
390
|
-
console.log(` codex webpresso mcp: already configured at ${webpressoMcpResult.path}`);
|
|
391
|
-
break;
|
|
392
|
-
case "codex-webpresso-mcp-ambiguous":
|
|
393
|
-
console.warn(` codex webpresso mcp: ⚠ ambiguous MCP config at ${webpressoMcpResult.path}; left unchanged`);
|
|
394
|
-
break;
|
|
395
|
-
case "codex-webpresso-mcp-skipped-dry-run":
|
|
396
|
-
console.log(" codex webpresso mcp: skipped (--dry-run)");
|
|
397
|
-
break;
|
|
398
|
-
case "codex-webpresso-mcp-not-installed":
|
|
399
|
-
console.log(` codex webpresso mcp: ⚠ no install root found (checked ${webpressoMcpResult.checked.length} paths). Install @webpresso/agent-kit globally (\`bun add -g @webpresso/agent-kit\`) or via the Claude plugin to wire up codex MCP.`);
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
383
|
+
reportLegacyCodexWebpressoMcpPrune(pruneLegacyCodexWebpressoMcp({ options: userScopeOptions }));
|
|
402
384
|
console.log(" codex project mcp: - skipped (no project .mcp.json outside a repo)");
|
|
403
385
|
}
|
|
404
386
|
if (isCiEnvironment) {
|
|
@@ -696,6 +678,25 @@ function printCodexPluginResult(result) {
|
|
|
696
678
|
break;
|
|
697
679
|
}
|
|
698
680
|
}
|
|
681
|
+
function reportLegacyCodexWebpressoMcpPrune(result) {
|
|
682
|
+
switch (result.kind) {
|
|
683
|
+
case "codex-legacy-webpresso-mcp-removed":
|
|
684
|
+
console.log(` codex webpresso mcp: ✓ retired legacy [mcp_servers.webpresso] in ${result.path} (the bundled Codex plugin is the single delivery channel)`);
|
|
685
|
+
break;
|
|
686
|
+
case "codex-legacy-webpresso-mcp-absent":
|
|
687
|
+
console.log(" codex webpresso mcp: ✓ delivered by the bundled Codex plugin");
|
|
688
|
+
break;
|
|
689
|
+
case "codex-legacy-webpresso-mcp-preserved":
|
|
690
|
+
console.warn(` codex webpresso mcp: ⚠ ${result.path} declares a user-authored webpresso server (${result.command ?? "no command"}); left unchanged — remove it manually to use the bundled Codex plugin`);
|
|
691
|
+
break;
|
|
692
|
+
case "codex-legacy-webpresso-mcp-ambiguous":
|
|
693
|
+
console.warn(` codex webpresso mcp: ⚠ ambiguous MCP config at ${result.path}; left unchanged`);
|
|
694
|
+
break;
|
|
695
|
+
case "codex-legacy-webpresso-mcp-skipped-dry-run":
|
|
696
|
+
console.log(" codex webpresso mcp: skipped (--dry-run)");
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
699
700
|
function reconcileCodexProjectMcpServers(repoRoot, options) {
|
|
700
701
|
const result = ensureCodexProjectMcpServers({ repoRoot, options });
|
|
701
702
|
switch (result.kind) {
|
|
@@ -1331,7 +1332,7 @@ export async function runInit(flags, deps = {}) {
|
|
|
1331
1332
|
runUnifiedSync({
|
|
1332
1333
|
catalogDir: join(catalogDir, "agent"),
|
|
1333
1334
|
consumerRoot: consumer.repoRoot,
|
|
1334
|
-
kinds: ["rule", "skill"],
|
|
1335
|
+
kinds: ["rule", "skill", "agent"],
|
|
1335
1336
|
check: false,
|
|
1336
1337
|
allowedSkillSlugs,
|
|
1337
1338
|
hosts: selectedHosts,
|
|
@@ -1482,7 +1483,8 @@ export async function runInit(flags, deps = {}) {
|
|
|
1482
1483
|
}
|
|
1483
1484
|
throw error;
|
|
1484
1485
|
}
|
|
1485
|
-
|
|
1486
|
+
// Canonical subagents (.claude/agents/) are projected by the unified sync
|
|
1487
|
+
// call above (kinds includes "agent") — no standalone scaffolder here.
|
|
1486
1488
|
// Generated host instructions (AGENTS.md) are a tier-1 (guarded/full)
|
|
1487
1489
|
// surface; the tier-0 quality profile omits them.
|
|
1488
1490
|
// Base-kit may have created or updated package metadata earlier in this
|
|
@@ -1625,29 +1627,14 @@ export async function runInit(flags, deps = {}) {
|
|
|
1625
1627
|
? { kind: "apply" }
|
|
1626
1628
|
: { kind: "skip", reason: skipGlobal ? "--skip-global" : "user-scope writes declined" });
|
|
1627
1629
|
}
|
|
1628
|
-
//
|
|
1629
|
-
//
|
|
1630
|
-
//
|
|
1631
|
-
// plugin / bun global / pnpm global / npm global) and write that.
|
|
1630
|
+
// Codex receives webpresso over ONE channel: the bundled Codex plugin's
|
|
1631
|
+
// `.mcp.json`. Setup retires the user-global `[mcp_servers.webpresso]`
|
|
1632
|
+
// block earlier versions wrote here rather than refreshing it.
|
|
1632
1633
|
if (selectedHosts.includes("codex")) {
|
|
1633
|
-
const
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
recordUserScope("codex-config");
|
|
1638
|
-
break;
|
|
1639
|
-
case "codex-webpresso-mcp-unchanged":
|
|
1640
|
-
console.log(` codex webpresso mcp: already configured at ${webpressoMcpResult.path}`);
|
|
1641
|
-
break;
|
|
1642
|
-
case "codex-webpresso-mcp-ambiguous":
|
|
1643
|
-
console.warn(` codex webpresso mcp: ⚠ ambiguous MCP config at ${webpressoMcpResult.path}; left unchanged`);
|
|
1644
|
-
break;
|
|
1645
|
-
case "codex-webpresso-mcp-skipped-dry-run":
|
|
1646
|
-
console.log(" codex webpresso mcp: skipped (--dry-run)");
|
|
1647
|
-
break;
|
|
1648
|
-
case "codex-webpresso-mcp-not-installed":
|
|
1649
|
-
console.log(` codex webpresso mcp: ⚠ no install root found (checked ${webpressoMcpResult.checked.length} paths). Install @webpresso/agent-kit globally (\`bun add -g @webpresso/agent-kit\`) or via the Claude plugin to wire up codex MCP.`);
|
|
1650
|
-
break;
|
|
1634
|
+
const pruneResult = pruneLegacyCodexWebpressoMcp({ options: userScopeOptions });
|
|
1635
|
+
reportLegacyCodexWebpressoMcpPrune(pruneResult);
|
|
1636
|
+
if (pruneResult.kind === "codex-legacy-webpresso-mcp-removed") {
|
|
1637
|
+
recordUserScope("codex-config");
|
|
1651
1638
|
}
|
|
1652
1639
|
reconcileCodexProjectMcpServers(consumer.repoRoot, userScopeOptions);
|
|
1653
1640
|
}
|
|
@@ -1829,7 +1816,7 @@ export async function runInit(flags, deps = {}) {
|
|
|
1829
1816
|
const rtkResult = ensureRtk({ repoRoot: consumer.repoRoot, options: userScopeOptions });
|
|
1830
1817
|
switch (rtkResult.kind) {
|
|
1831
1818
|
case "rtk-ok":
|
|
1832
|
-
console.log(rtkResult.installed ? " rtk: ✓
|
|
1819
|
+
console.log(rtkResult.installed ? " rtk: ✓ managed binary installed" : " rtk: ✓");
|
|
1833
1820
|
recordUserScope("rtk-install");
|
|
1834
1821
|
break;
|
|
1835
1822
|
case "rtk-skipped-dry-run":
|
|
@@ -1839,10 +1826,6 @@ export async function runInit(flags, deps = {}) {
|
|
|
1839
1826
|
console.error(` rtk: ✗ ${rtkResult.hint}`);
|
|
1840
1827
|
rtkFailure = "not-found";
|
|
1841
1828
|
break;
|
|
1842
|
-
case "rtk-init-failed":
|
|
1843
|
-
console.error(` rtk: ✗ rtk init exited with ${rtkResult.exitCode}`);
|
|
1844
|
-
rtkFailure = "init-failed";
|
|
1845
|
-
break;
|
|
1846
1829
|
}
|
|
1847
1830
|
}
|
|
1848
1831
|
const all = [
|
|
@@ -1870,7 +1853,6 @@ export async function runInit(flags, deps = {}) {
|
|
|
1870
1853
|
]
|
|
1871
1854
|
: []),
|
|
1872
1855
|
...claudeRulesResults,
|
|
1873
|
-
...subagentResults,
|
|
1874
1856
|
...presetResults,
|
|
1875
1857
|
];
|
|
1876
1858
|
const summary = summarizeResults(all);
|
|
@@ -2039,8 +2021,6 @@ export async function runInit(flags, deps = {}) {
|
|
|
2039
2021
|
console.log("\nwp init: setup phases finished.");
|
|
2040
2022
|
if (rtkFailure === "not-found")
|
|
2041
2023
|
return EXIT_SETUP_FAIL;
|
|
2042
|
-
if (rtkFailure === "init-failed")
|
|
2043
|
-
return EXIT_WRITE_FAIL;
|
|
2044
2024
|
if (isTelemetryEnabled(process.env)) {
|
|
2045
2025
|
const payload = {
|
|
2046
2026
|
event: "setup-complete",
|
|
@@ -30,7 +30,24 @@ export interface WebpressoInstallProbe {
|
|
|
30
30
|
stableEntry?: () => string | null;
|
|
31
31
|
/** Test seam — verify that a PATH entry is the Webpresso command. */
|
|
32
32
|
stableEntryIsWebpresso?: (entryPath: string) => boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Test seam — resolve the currently executing agent-kit package root.
|
|
35
|
+
*
|
|
36
|
+
* Without this seam the self-trust branch below is only reachable when the
|
|
37
|
+
* test file itself happens to live in a managed worktree, so the regression
|
|
38
|
+
* guard would silently skip in CI — where the checkout is a plain clone.
|
|
39
|
+
*/
|
|
40
|
+
sourceRoot?: () => string | null;
|
|
33
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* True when `entryPath` is the stable vite-plus multi-package `wp` shim that
|
|
44
|
+
* dispatches to `@webpresso/agent-kit` via `~/.vite-plus/bins/wp.json`.
|
|
45
|
+
*
|
|
46
|
+
* Unlike {@link isWebpressoWpEntry}, this does **not** require realpath to land
|
|
47
|
+
* inside a package `bin/wp` tree — vite-plus's `wp` is a symlink to the `vp`
|
|
48
|
+
* binary, so package-root ownership is proven via the bins metadata instead.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isVitePlusManagedWebpressoWp(entryPath: string): boolean;
|
|
34
51
|
/**
|
|
35
52
|
* Resolve the absolute path to webpresso's global `bin/wp` launcher on this
|
|
36
53
|
* machine. Probes the locations consumers use to install webpresso, in order
|
|
@@ -43,25 +60,43 @@ export interface WebpressoInstallProbe {
|
|
|
43
60
|
* 3. Claude plugin install — `~/.claude/plugins/cache/.../agent-kit/`
|
|
44
61
|
* 4. bun global — `~/.bun/install/global/node_modules/@webpresso/agent-kit/`
|
|
45
62
|
*
|
|
46
|
-
* Host MCP configs (Grok/
|
|
63
|
+
* Host MCP configs (Grok/OpenCode) must never be pinned to a versioned
|
|
47
64
|
* installId tree: the next `wp update` / `vp update -g` deletes that path and
|
|
48
|
-
* leaves every host with a dead launcher.
|
|
49
|
-
* candidates yield a durable `wp`.
|
|
65
|
+
* leaves every host with a dead launcher. `entryPath` is `null` when none of
|
|
66
|
+
* the candidates yield a durable `wp`.
|
|
50
67
|
*/
|
|
51
|
-
export declare function findWebpressoMcpEntry(probe?: WebpressoInstallProbe): string | null;
|
|
52
|
-
/**
|
|
53
|
-
* True when `entryPath` is the stable vite-plus multi-package `wp` shim that
|
|
54
|
-
* dispatches to `@webpresso/agent-kit` via `~/.vite-plus/bins/wp.json`.
|
|
55
|
-
*
|
|
56
|
-
* Unlike {@link isWebpressoWpEntry}, this does **not** require realpath to land
|
|
57
|
-
* inside a package `bin/wp` tree — vite-plus's `wp` is a symlink to the `vp`
|
|
58
|
-
* binary, so package-root ownership is proven via the bins metadata instead.
|
|
59
|
-
*/
|
|
60
|
-
export declare function isVitePlusManagedWebpressoWp(entryPath: string): boolean;
|
|
61
68
|
export declare function resolveWebpressoMcpEntry(probe?: WebpressoInstallProbe): {
|
|
62
69
|
entryPath: string | null;
|
|
63
70
|
checked: readonly string[];
|
|
64
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Seams for classifying a persisted host-config `command` path as agent-kit's
|
|
74
|
+
* own. Kept separate from {@link WebpressoInstallProbe}: that probe answers
|
|
75
|
+
* "where should we WRITE a launcher", this one answers "did WE write this".
|
|
76
|
+
*/
|
|
77
|
+
export interface AgentKitOwnedCommandProbe {
|
|
78
|
+
/** Test seam — override `$HOME`. */
|
|
79
|
+
readonly home?: string;
|
|
80
|
+
/** Test seam — resolve the currently executing agent-kit package root. */
|
|
81
|
+
readonly sourceRoot?: () => string | null;
|
|
82
|
+
/** Test seam — filesystem existence check for the persisted command path. */
|
|
83
|
+
readonly exists?: (path: string) => boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Every root under which agent-kit itself installs (or checks out) a `bin/wp`.
|
|
87
|
+
* A `wp` launcher recorded inside one of these was written by agent-kit; a
|
|
88
|
+
* launcher anywhere else is an operator's deliberate choice.
|
|
89
|
+
*/
|
|
90
|
+
export declare function agentKitOwnedCommandRoots(probe?: AgentKitOwnedCommandProbe): readonly string[];
|
|
91
|
+
/**
|
|
92
|
+
* Ownership — not shape — decides whether setup may retire a persisted MCP
|
|
93
|
+
* entry. An entry is agent-kit's own when it launches `wp mcp` from an
|
|
94
|
+
* ABSOLUTE path AND either that path is gone (one we would have deleted) or
|
|
95
|
+
* it lives inside one
|
|
96
|
+
* of {@link agentKitOwnedCommandRoots}. A *live* `wp mcp` launcher outside
|
|
97
|
+
* those roots is an operator pointing Codex at a custom build: preserve it.
|
|
98
|
+
*/
|
|
99
|
+
export declare function isAgentKitOwnedWpCommand(command: string, args: readonly string[], probe?: AgentKitOwnedCommandProbe): boolean;
|
|
65
100
|
export declare function isWebpressoWpEntry(entryPath: string): boolean;
|
|
66
101
|
/** Run a discovery probe with bounded output and SIGKILL escalation. */
|
|
67
102
|
export declare function runQuiet(cmd: string, args: readonly string[], timeoutMs?: number): string | null;
|
|
@@ -4,10 +4,15 @@
|
|
|
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";
|
|
@@ -17,6 +22,8 @@ import { execFileSync } from "node:child_process";
|
|
|
17
22
|
import { fileURLToPath } from "node:url";
|
|
18
23
|
import { isVersionedAgentKitPayloadPath } from "#cli/auto-update/self-invocation.js";
|
|
19
24
|
import { pathCandidates } from "#runtime/command-exists.js";
|
|
25
|
+
import { isManagedWorktreePath, isPathContained } from "#worktrees/identity.js";
|
|
26
|
+
import { resolveKnownManagedWorktreeRoots } from "#worktrees/location.js";
|
|
20
27
|
export const WEBPRESSO_MCP_SERVER_NAME = "webpresso";
|
|
21
28
|
/**
|
|
22
29
|
* The single canonical launch spec for the webpresso MCP server. `name` is the
|
|
@@ -63,26 +70,6 @@ const WP_LAUNCHER_REQUIRED_RELATIVES = [
|
|
|
63
70
|
join("bin", "runtime-lanes.js"),
|
|
64
71
|
join("bin", "runtime-manifest.json"),
|
|
65
72
|
];
|
|
66
|
-
/**
|
|
67
|
-
* Resolve the absolute path to webpresso's global `bin/wp` launcher on this
|
|
68
|
-
* machine. Probes the locations consumers use to install webpresso, in order
|
|
69
|
-
* of stability:
|
|
70
|
-
*
|
|
71
|
-
* 1. the stable global `wp` shim available on PATH (including the
|
|
72
|
-
* vite-plus multi-package shim at `~/.vite-plus/bin/wp`)
|
|
73
|
-
* 2. the currently executing `@webpresso/agent-kit` package **only when it
|
|
74
|
-
* is not a vite-plus `agent-kit#<installId>` payload path**
|
|
75
|
-
* 3. Claude plugin install — `~/.claude/plugins/cache/.../agent-kit/`
|
|
76
|
-
* 4. bun global — `~/.bun/install/global/node_modules/@webpresso/agent-kit/`
|
|
77
|
-
*
|
|
78
|
-
* Host MCP configs (Grok/Codex/etc.) must never be pinned to a versioned
|
|
79
|
-
* installId tree: the next `wp update` / `vp update -g` deletes that path and
|
|
80
|
-
* leaves every host with a dead launcher. Returns `null` when none of the
|
|
81
|
-
* candidates yield a durable `wp`.
|
|
82
|
-
*/
|
|
83
|
-
export function findWebpressoMcpEntry(probe = {}) {
|
|
84
|
-
return resolveWebpressoMcpEntry(probe).entryPath;
|
|
85
|
-
}
|
|
86
73
|
function isUsableWebpressoMcpRoot(root) {
|
|
87
74
|
return WP_LAUNCHER_REQUIRED_RELATIVES.every((relativePath) => existsSync(join(root, relativePath)));
|
|
88
75
|
}
|
|
@@ -117,6 +104,23 @@ export function isVitePlusManagedWebpressoWp(entryPath) {
|
|
|
117
104
|
function isDurableMcpEntryPath(entryPath) {
|
|
118
105
|
return !isVersionedAgentKitPayloadPath(entryPath);
|
|
119
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Resolve the absolute path to webpresso's global `bin/wp` launcher on this
|
|
109
|
+
* machine. Probes the locations consumers use to install webpresso, in order
|
|
110
|
+
* of stability:
|
|
111
|
+
*
|
|
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**
|
|
116
|
+
* 3. Claude plugin install — `~/.claude/plugins/cache/.../agent-kit/`
|
|
117
|
+
* 4. bun global — `~/.bun/install/global/node_modules/@webpresso/agent-kit/`
|
|
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`.
|
|
123
|
+
*/
|
|
120
124
|
export function resolveWebpressoMcpEntry(probe = {}) {
|
|
121
125
|
const checked = [];
|
|
122
126
|
if (probe.candidates === undefined) {
|
|
@@ -151,8 +155,9 @@ export function resolveWebpressoMcpEntry(probe = {}) {
|
|
|
151
155
|
// Never materialize host MCP configs against a vite-plus installId tree.
|
|
152
156
|
if (!isDurableMcpEntryPath(wpBin))
|
|
153
157
|
continue;
|
|
154
|
-
if (isTrustedWebpressoPackageRoot(root))
|
|
158
|
+
if (isTrustedWebpressoPackageRoot(root, probe.sourceRoot)) {
|
|
155
159
|
return { entryPath: wpBin, checked };
|
|
160
|
+
}
|
|
156
161
|
}
|
|
157
162
|
}
|
|
158
163
|
return { entryPath: null, checked };
|
|
@@ -200,27 +205,78 @@ function isPathInsideOrEqual(parent, candidate) {
|
|
|
200
205
|
const rel = relative(resolve(parentRoot), resolve(candidateRoot));
|
|
201
206
|
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
202
207
|
}
|
|
203
|
-
function isTrustedWebpressoPackageRoot(packageRoot) {
|
|
208
|
+
function isTrustedWebpressoPackageRoot(packageRoot, resolveSourceRoot = currentAgentKitPackageRoot) {
|
|
204
209
|
try {
|
|
205
210
|
if (!isUsableWebpressoPackageRoot(packageRoot))
|
|
206
211
|
return false;
|
|
207
212
|
const realPackageRoot = realpathSync(packageRoot);
|
|
208
|
-
const sourceRoot =
|
|
209
|
-
|
|
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)) {
|
|
210
221
|
return true;
|
|
222
|
+
}
|
|
211
223
|
return trustedHomeInstallPackageRoots().some((anchor) => existsSync(anchor) && isPathInsideOrEqual(anchor, realPackageRoot));
|
|
212
224
|
}
|
|
213
225
|
catch {
|
|
214
226
|
return false;
|
|
215
227
|
}
|
|
216
228
|
}
|
|
217
|
-
function trustedHomeInstallPackageRoots() {
|
|
218
|
-
const home = process.env.HOME || homedir();
|
|
229
|
+
function trustedHomeInstallPackageRoots(home = process.env.HOME || homedir()) {
|
|
219
230
|
return [
|
|
220
231
|
join(home, ".claude", "plugins", "cache", "webpresso", "agent-kit"),
|
|
221
232
|
join(home, ".bun", "install", "global", "node_modules", "@webpresso", "agent-kit"),
|
|
222
233
|
];
|
|
223
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
|
+
}
|
|
224
280
|
function packageWpFromAdjacentNpmCmdShim(entryPath) {
|
|
225
281
|
if (!entryPath.toLowerCase().endsWith(".cmd"))
|
|
226
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 = {
|