@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.
Files changed (156) hide show
  1. package/THIRD-PARTY-NOTICES.md +12 -10
  2. package/catalog/agent/rules/changeset-release.md +15 -4
  3. package/catalog/agent/rules/managed-tool-binaries.md +67 -0
  4. package/catalog/agent/rules/parallel-orchestration-quota.md +99 -1
  5. package/catalog/agent/rules/pre-implementation.md +36 -20
  6. package/catalog/agent/rules/rtk-routing.md +22 -16
  7. package/catalog/agent/skills/autopilot/SKILL.md +4 -3
  8. package/catalog/agent/skills/ultragoal/SKILL.md +11 -9
  9. package/dist/esm/audit/agents.js +126 -26
  10. package/dist/esm/audit/hook-surface.js +5 -1
  11. package/dist/esm/audit/repo-guardrails.d.ts +8 -0
  12. package/dist/esm/audit/repo-guardrails.js +71 -1
  13. package/dist/esm/blueprint/core/parser.js +38 -12
  14. package/dist/esm/blueprint/execution/artifacts.js +8 -4
  15. package/dist/esm/blueprint/execution/frontmatter-keys.d.ts +17 -0
  16. package/dist/esm/blueprint/execution/frontmatter-keys.js +24 -0
  17. package/dist/esm/blueprint/execution/metadata.js +8 -4
  18. package/dist/esm/blueprint/lifecycle/audit.d.ts +21 -0
  19. package/dist/esm/blueprint/lifecycle/audit.js +89 -3
  20. package/dist/esm/blueprint/lifecycle/review-provenance.js +63 -10
  21. package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +22 -0
  22. package/dist/esm/blueprint/lifecycle/rollout-anchor.js +37 -1
  23. package/dist/esm/blueprint/markdown/blocked-line.d.ts +49 -0
  24. package/dist/esm/blueprint/markdown/blocked-line.js +63 -0
  25. package/dist/esm/blueprint/markdown/helpers.js +10 -4
  26. package/dist/esm/blueprint/tracked-document/parser.js +3 -3
  27. package/dist/esm/blueprint/trust/dossier.js +41 -1
  28. package/dist/esm/blueprint/trust/gate-row.d.ts +55 -0
  29. package/dist/esm/blueprint/trust/gate-row.js +113 -0
  30. package/dist/esm/blueprint/trust/promotion.d.ts +17 -0
  31. package/dist/esm/blueprint/trust/promotion.js +19 -19
  32. package/dist/esm/build/atomic-file-copy.d.ts +32 -0
  33. package/dist/esm/build/atomic-file-copy.js +44 -0
  34. package/dist/esm/build/vendor-oxlint-plugins.d.ts +11 -0
  35. package/dist/esm/build/vendor-oxlint-plugins.js +48 -0
  36. package/dist/esm/cli/commands/blueprint/mutations.d.ts +15 -0
  37. package/dist/esm/cli/commands/blueprint/mutations.js +8 -2
  38. package/dist/esm/cli/commands/compile.d.ts +13 -0
  39. package/dist/esm/cli/commands/compile.js +182 -62
  40. package/dist/esm/cli/commands/format.js +17 -1
  41. package/dist/esm/cli/commands/gain/index.js +10 -3
  42. package/dist/esm/cli/commands/init/gitignore-patcher.js +6 -0
  43. package/dist/esm/cli/commands/init/index.js +32 -52
  44. package/dist/esm/cli/commands/init/mcp-spec.d.ts +48 -13
  45. package/dist/esm/cli/commands/init/mcp-spec.js +86 -30
  46. package/dist/esm/cli/commands/init/merge.js +30 -10
  47. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +56 -31
  48. package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +180 -43
  49. package/dist/esm/cli/commands/init/scaffolders/rtk/index.d.ts +17 -5
  50. package/dist/esm/cli/commands/init/scaffolders/rtk/index.js +56 -75
  51. package/dist/esm/cli/commands/init/scaffolders/runtime-check/index.js +7 -6
  52. package/dist/esm/cli/commands/init/untracked-collision.d.ts +12 -1
  53. package/dist/esm/cli/commands/init/untracked-collision.js +23 -1
  54. package/dist/esm/cli/commands/pr-help.js +1 -1
  55. package/dist/esm/cli/commands/pr.js +18 -4
  56. package/dist/esm/cli/commands/review.d.ts +1 -0
  57. package/dist/esm/cli/commands/review.js +137 -7
  58. package/dist/esm/cli/commands/sync.d.ts +8 -6
  59. package/dist/esm/cli/commands/sync.js +11 -9
  60. package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +24 -2
  61. package/dist/esm/cli/commands/worktree/router-dispatch.js +80 -48
  62. package/dist/esm/cli/commands/worktree/router.js +5 -2
  63. package/dist/esm/cli/direct-provider-launch.js +12 -4
  64. package/dist/esm/compiler/flatten.d.ts +10 -1
  65. package/dist/esm/compiler/flatten.js +14 -4
  66. package/dist/esm/compose/admin-blocks-registry-integrity.d.ts +22 -0
  67. package/dist/esm/compose/admin-blocks-registry-integrity.js +90 -0
  68. package/dist/esm/compose/compose.d.ts +8 -0
  69. package/dist/esm/compose/compose.js +115 -8
  70. package/dist/esm/compose/ops-report-schema.js +17 -3
  71. package/dist/esm/compose/registry-resolve.d.ts +17 -0
  72. package/dist/esm/compose/registry-resolve.js +78 -11
  73. package/dist/esm/config/oxlint/graphql-conventions.js +2 -5
  74. package/dist/esm/config/oxlint/import-hygiene.js +21 -8
  75. package/dist/esm/config/oxlint/oxlint-config.fixture.d.ts +1 -1
  76. package/dist/esm/config/oxlint/oxlint-config.fixture.js +2 -3
  77. package/dist/esm/config/oxlint/oxlintrc.d.ts +5 -7
  78. package/dist/esm/config/oxlint/oxlintrc.js +19 -31
  79. package/dist/esm/config/oxlint/path-roles.js +53 -0
  80. package/dist/esm/config/oxlint/query-patterns.js +2 -5
  81. package/dist/esm/content/dispatch.js +4 -0
  82. package/dist/esm/content/loader.d.ts +1 -1
  83. package/dist/esm/content/loader.js +10 -6
  84. package/dist/esm/content/skill-policy.d.ts +11 -0
  85. package/dist/esm/content/skill-policy.js +33 -0
  86. package/dist/esm/daemon/domains/blueprint-exec-ledger.d.ts +86 -0
  87. package/dist/esm/daemon/domains/blueprint-exec-ledger.js +134 -0
  88. package/dist/esm/daemon/domains/blueprint-exec.d.ts +106 -0
  89. package/dist/esm/daemon/domains/blueprint-exec.js +161 -0
  90. package/dist/esm/daemon/domains/launch.d.ts +25 -0
  91. package/dist/esm/daemon/domains/launch.js +30 -1
  92. package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +12 -1
  93. package/dist/esm/daemon/handlers/blueprints-handlers.js +148 -6
  94. package/dist/esm/daemon/handlers/product.d.ts +10 -0
  95. package/dist/esm/daemon/handlers/product.js +10 -1
  96. package/dist/esm/daemon/methods.d.ts +21 -1
  97. package/dist/esm/daemon/methods.js +20 -0
  98. package/dist/esm/daemon/protocol/contract.d.ts +58 -0
  99. package/dist/esm/daemon/protocol/contract.js +38 -0
  100. package/dist/esm/daemon/server.js +7 -0
  101. package/dist/esm/docs-linter/blueprint-plan.js +2 -2
  102. package/dist/esm/errors/wp-error.js +21 -2
  103. package/dist/esm/hooks/doctor.d.ts +11 -0
  104. package/dist/esm/hooks/doctor.js +114 -45
  105. package/dist/esm/hooks/pretool-guard/dev-routing.js +68 -5
  106. package/dist/esm/hooks/pretool-guard/rtk-rewrite.d.ts +24 -0
  107. package/dist/esm/hooks/pretool-guard/rtk-rewrite.js +50 -0
  108. package/dist/esm/hooks/pretool-guard/runner.js +6 -0
  109. package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +62 -3
  110. package/dist/esm/hooks/status/index.js +1 -1
  111. package/dist/esm/mcp/blueprint/_shared/lifecycle.js +39 -11
  112. package/dist/esm/mcp/blueprint/handlers/review-log.d.ts +14 -7
  113. package/dist/esm/mcp/blueprint/handlers/review-log.js +46 -22
  114. package/dist/esm/mcp/blueprint/registration.js +1 -1
  115. package/dist/esm/mcp/tools/_shared/redact.d.ts +22 -0
  116. package/dist/esm/mcp/tools/_shared/redact.js +114 -8
  117. package/dist/esm/mcp/tools/gain.js +28 -2
  118. package/dist/esm/mcp/tools/worktree.js +65 -3
  119. package/dist/esm/mcp/tools/wp-ui-compose.js +38 -4
  120. package/dist/esm/mcp/tools/wp-ui-preview.js +14 -7
  121. package/dist/esm/review/authority.js +56 -1
  122. package/dist/esm/review/delivery-verifier.d.ts +4 -0
  123. package/dist/esm/review/delivery-verifier.js +9 -1
  124. package/dist/esm/review/lifecycle-writers.d.ts +76 -0
  125. package/dist/esm/review/lifecycle-writers.js +330 -0
  126. package/dist/esm/review/subject.js +66 -32
  127. package/dist/esm/status/snapshot.d.ts +5 -0
  128. package/dist/esm/status/snapshot.js +27 -2
  129. package/dist/esm/symlinker/consumers.d.ts +3 -1
  130. package/dist/esm/symlinker/consumers.js +7 -1
  131. package/dist/esm/symlinker/unified-sync.d.ts +3 -3
  132. package/dist/esm/symlinker/unified-sync.js +88 -11
  133. package/dist/esm/test/shard-durations.json +0 -2
  134. package/dist/esm/test-helpers/global-setup.js +38 -3
  135. package/dist/esm/tool-runtime/managed-rtk.d.ts +44 -0
  136. package/dist/esm/tool-runtime/managed-rtk.js +275 -0
  137. package/dist/esm/tool-runtime/resolve-runner.js +18 -194
  138. package/dist/esm/utils/package-root.d.ts +32 -0
  139. package/dist/esm/utils/package-root.js +82 -0
  140. package/dist/esm/worktrees/git-metadata.d.ts +41 -0
  141. package/dist/esm/worktrees/git-metadata.js +48 -0
  142. package/package.json +16 -21
  143. package/dist/esm/cli/commands/init/scaffolders/subagents/index.d.ts +0 -6
  144. package/dist/esm/cli/commands/init/scaffolders/subagents/index.js +0 -77
  145. package/dist/esm/config/oxlint/code-safety.d.ts +0 -18
  146. package/dist/esm/config/oxlint/foundation-purity.d.ts +0 -21
  147. package/dist/esm/config/oxlint/graphql-conventions.d.ts +0 -23
  148. package/dist/esm/config/oxlint/import-hygiene.d.ts +0 -34
  149. package/dist/esm/config/oxlint/index.d.ts +0 -22
  150. package/dist/esm/config/oxlint/index.js +0 -28
  151. package/dist/esm/config/oxlint/monorepo-paths.d.ts +0 -22
  152. package/dist/esm/config/oxlint/query-patterns.d.ts +0 -24
  153. package/dist/esm/config/oxlint/testing-quality.d.ts +0 -35
  154. package/dist/esm/config/oxlint/tier-boundaries.d.ts +0 -35
  155. package/dist/esm/output-transforms/rulesync.d.ts +0 -2
  156. package/dist/esm/output-transforms/rulesync.js +0 -79
@@ -1,12 +1,11 @@
1
1
  import { existsSync, realpathSync, statSync } from "node:fs";
2
2
  import { basename, dirname, join, resolve } from "node:path";
3
3
  import { createRequire } from "node:module";
4
- import { fileURLToPath, pathToFileURL } from "node:url";
5
- import { Worker } from "node:worker_threads";
6
4
  import { resolveAgentKitPackageRoot } from "#cli/commands/init/package-root.js";
7
5
  import { resolveGlobalCapableVpCommand } from "#cli/global-vp.js";
8
6
  import { pathCandidates } from "#runtime/command-exists.js";
9
7
  import { readTrustedJsonFile } from "#shared-utils/read-json-file.js";
8
+ import { isManagedRtkAvailable, resolveManagedRtkExecutable } from "./managed-rtk.js";
10
9
  const require = createRequire(import.meta.url);
11
10
  const MANAGED_TOOL_PREFIX = {
12
11
  oxfmt: {
@@ -33,196 +32,8 @@ const MANAGED_TOOL_PREFIX = {
33
32
  wrangler: { packageName: "wrangler", binName: "wrangler" },
34
33
  };
35
34
  let rtkAvailable = null;
36
- // The real token-killer `rtk` exposes a `gain` analytics subcommand (see
37
- // RTK.md); the unrelated `reachingforthejack/rtk` (Rust Type Kit) collision
38
- // binary does not. Probe that capability instead of `--version`, which *any*
39
- // `rtk` on PATH answers with exit 0 — trusting it would route wrapped commands
40
- // (typecheck/qa/test gates) through a foreign binary that may mangle args or
41
- // drop the wrapped exit code, masking a failing gate as green.
42
- const RTK_CAPABILITY_ARGS = ["gain", "--help"];
43
- // Measured cold cost of `rtk gain --help` is ~11ms; 3s is generous headroom.
44
- // A probe that exceeds it is a broken or wrong binary, so degrade to unfiltered
45
- // output rather than let a hung `rtk` stall every wrapped command (per
46
- // no-timeout-as-fix: the bound surfaces the fault, it does not silence it).
47
- const RTK_PROBE_TIMEOUT_MS = 3000;
48
- const RTK_PROBE_ESCALATION_DELAY_MS = 250;
49
- const RTK_PROBE_FORCE_KILL_OBSERVE_MS = 1000;
50
- const RTK_PROBE_ADAPTER_SLACK_MS = 1000;
51
- const RTK_PROBE_ADAPTER_TIMEOUT_MS = RTK_PROBE_TIMEOUT_MS +
52
- RTK_PROBE_ESCALATION_DELAY_MS +
53
- RTK_PROBE_FORCE_KILL_OBSERVE_MS +
54
- RTK_PROBE_ADAPTER_SLACK_MS;
55
- const RTK_PROBE_STATUS_INDEX = 0;
56
- const RTK_PROBE_PID_INDEX = 1;
57
- const RTK_PROBE_PENDING = 0;
58
- const RTK_PROBE_AVAILABLE = 1;
59
- const RTK_PROBE_UNAVAILABLE = 2;
60
- const RTK_PROBE_CLEANUP_FAILED = 3;
61
- // `resolveRunner` is intentionally synchronous because it is also used by pure
62
- // command planners. The worker keeps that contract while allowing the inner
63
- // probe to use the same async, owned-process-group supervisor as long-running
64
- // command surfaces. The main thread only waits on shared state; it never owns a
65
- // timeout-killed wrapper whose descendants can escape.
66
- const RTK_PROBE_WORKER_SOURCE = String.raw `
67
- (async () => {
68
- const [{ spawn }, { workerData }] = await Promise.all([
69
- import("node:child_process"),
70
- import("node:worker_threads"),
71
- ]);
72
- const state = new Int32Array(workerData.state);
73
- let settled = false;
74
- const finish = (status) => {
75
- if (settled) return;
76
- settled = true;
77
- Atomics.store(state, workerData.statusIndex, status);
78
- Atomics.notify(state, workerData.statusIndex);
79
- };
80
-
81
- try {
82
- const {
83
- createTerminationAwareChild,
84
- terminateOwnedProcessTreeWithEscalation,
85
- } = await import(workerData.supervisorModuleUrl);
86
- if (
87
- typeof createTerminationAwareChild !== "function" ||
88
- typeof terminateOwnedProcessTreeWithEscalation !== "function"
89
- ) {
90
- finish(workerData.cleanupFailed);
91
- return;
92
- }
93
-
94
- const child = spawn("rtk", workerData.args, {
95
- detached: process.platform !== "win32",
96
- stdio: "ignore",
97
- windowsHide: true,
98
- });
99
- if (child.pid) Atomics.store(state, workerData.pidIndex, child.pid);
100
- const tracked = createTerminationAwareChild(child, {
101
- kind: "owned-process-group",
102
- detached: true,
103
- });
104
-
105
- let resolving = false;
106
- let timer;
107
- const resolveAfterCleanup = async (status) => {
108
- if (resolving) return;
109
- resolving = true;
110
- if (timer) clearTimeout(timer);
111
- await terminateOwnedProcessTreeWithEscalation(tracked, {
112
- escalationDelayMs: workerData.escalationDelayMs,
113
- });
114
- finish(status);
115
- };
116
- const settle = (status) => {
117
- void resolveAfterCleanup(status).catch(() => finish(workerData.cleanupFailed));
118
- };
119
-
120
- timer = setTimeout(() => {
121
- settle(workerData.unavailable);
122
- }, workerData.timeoutMs);
123
- child.once("error", () => {
124
- settle(workerData.unavailable);
125
- });
126
- child.once("close", (code) => {
127
- settle(code === 0 ? workerData.available : workerData.unavailable);
128
- });
129
- } catch {
130
- finish(workerData.cleanupFailed);
131
- }
132
- })();
133
- `;
134
- function probeRtkAvailability() {
135
- if (rtkAvailable !== null)
136
- return rtkAvailable;
137
- const supervisorModuleUrl = resolveProbeSupervisorModuleUrl();
138
- if (supervisorModuleUrl === null) {
139
- rtkAvailable = false;
140
- return rtkAvailable;
141
- }
142
- const state = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2));
143
- let worker;
144
- try {
145
- worker = new Worker(RTK_PROBE_WORKER_SOURCE, {
146
- eval: true,
147
- workerData: {
148
- args: RTK_CAPABILITY_ARGS,
149
- available: RTK_PROBE_AVAILABLE,
150
- unavailable: RTK_PROBE_UNAVAILABLE,
151
- cleanupFailed: RTK_PROBE_CLEANUP_FAILED,
152
- statusIndex: RTK_PROBE_STATUS_INDEX,
153
- pidIndex: RTK_PROBE_PID_INDEX,
154
- state: state.buffer,
155
- supervisorModuleUrl,
156
- timeoutMs: RTK_PROBE_TIMEOUT_MS,
157
- escalationDelayMs: RTK_PROBE_ESCALATION_DELAY_MS,
158
- },
159
- });
160
- worker.unref();
161
- Atomics.wait(state, RTK_PROBE_STATUS_INDEX, RTK_PROBE_PENDING, RTK_PROBE_ADAPTER_TIMEOUT_MS);
162
- const status = Atomics.load(state, RTK_PROBE_STATUS_INDEX);
163
- if (status === RTK_PROBE_PENDING || status === RTK_PROBE_CLEANUP_FAILED) {
164
- forceReclaimProbe(Atomics.load(state, RTK_PROBE_PID_INDEX));
165
- }
166
- rtkAvailable = status === RTK_PROBE_AVAILABLE;
167
- }
168
- catch {
169
- rtkAvailable = false;
170
- }
171
- finally {
172
- void worker?.terminate();
173
- }
174
- return rtkAvailable;
175
- }
176
- function resolveProbeSupervisorModuleUrl() {
177
- const sourceExtension = import.meta.url.endsWith(".ts") ? "ts" : "js";
178
- const adjacent = new URL(`../utils/process-supervisor.${sourceExtension}`, import.meta.url);
179
- try {
180
- if (adjacent.protocol === "file:" && existsSync(fileURLToPath(adjacent))) {
181
- return adjacent.href;
182
- }
183
- }
184
- catch {
185
- // Native runtimes can expose a virtual import.meta.url; fall back to the package root.
186
- }
187
- const packageRoot = resolveAgentKitPackageRoot({ moduleUrl: import.meta.url });
188
- if (packageRoot === null)
189
- return null;
190
- for (const candidate of [
191
- join(packageRoot, "dist", "esm", "utils", "process-supervisor.js"),
192
- join(packageRoot, "src", "utils", "process-supervisor.ts"),
193
- ]) {
194
- if (existsSync(candidate))
195
- return pathToFileURL(candidate).href;
196
- }
197
- return null;
198
- }
199
- function forceReclaimProbe(pid) {
200
- if (!Number.isInteger(pid) || pid <= 0)
201
- return;
202
- const target = process.platform === "win32" ? pid : -pid;
203
- try {
204
- process.kill(target, "SIGKILL");
205
- }
206
- catch (error) {
207
- if (error.code !== "ESRCH")
208
- throw error;
209
- return;
210
- }
211
- const deadline = Date.now() + RTK_PROBE_FORCE_KILL_OBSERVE_MS;
212
- const sleeper = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
213
- while (isProcessTargetAlive(target) && Date.now() < deadline) {
214
- Atomics.wait(sleeper, 0, 0, 20);
215
- }
216
- }
217
- function isProcessTargetAlive(target) {
218
- try {
219
- process.kill(target, 0);
220
- return true;
221
- }
222
- catch (error) {
223
- return error.code !== "ESRCH";
224
- }
225
- }
35
+ // Capability probing lives in managed-rtk (absolute binary + `gain --help`).
36
+ // Hermetic tests use setRtkAvailabilityProbeForTest to force wrap/degrade.
226
37
  export function setRtkAvailabilityProbeForTest(value) {
227
38
  rtkAvailable = value;
228
39
  }
@@ -232,11 +43,24 @@ export function resolveOutputPolicy(outputPolicy, filterOutput) {
232
43
  function withOptionalRtk(resolution, outputPolicy) {
233
44
  if (outputPolicy !== "rtk-filtered")
234
45
  return resolution;
235
- if (!probeRtkAvailability())
46
+ // Hermetic test seam: setRtkAvailabilityProbeForTest(true) wraps with bare "rtk".
47
+ if (rtkAvailable === true) {
48
+ return {
49
+ ...resolution,
50
+ command: "rtk",
51
+ args: [resolution.command, ...resolution.args],
52
+ };
53
+ }
54
+ if (rtkAvailable === false)
55
+ return resolution;
56
+ if (!isManagedRtkAvailable())
57
+ return resolution;
58
+ const rtkBin = resolveManagedRtkExecutable();
59
+ if (!rtkBin)
236
60
  return resolution;
237
61
  return {
238
62
  ...resolution,
239
- command: "rtk",
63
+ command: rtkBin,
240
64
  args: [resolution.command, ...resolution.args],
241
65
  };
242
66
  }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Locate an installed package's root directory from its bare name.
3
+ *
4
+ * The obvious spelling — `require.resolve("<name>/package.json")` — is a trap.
5
+ * Node honours the target package's `exports` map, and a package that declares
6
+ * only `"."` (rulesync) makes that throw `ERR_PACKAGE_PATH_NOT_EXPORTED`. Every
7
+ * caller in this repo wrapped it in `try {} catch { return null }`, so the throw
8
+ * turned into a silently missing capability rather than a visible failure.
9
+ *
10
+ * Resolving the package's exported entry works instead, because any package
11
+ * meant to be imported has one. From there, walk up to the directory whose
12
+ * `package.json` actually declares that name — the entry may sit several levels
13
+ * deep (`<root>/dist/index.cjs`).
14
+ *
15
+ * Caveat worth knowing before reusing this: `resolve` runs under the CJS
16
+ * `require` condition. A package whose every export condition is `import`
17
+ * (ESM-only, as `@webpresso/agent-kit` itself is) has no entry resolvable this
18
+ * way at all. Such a package must export a condition-free `"./package.json"`
19
+ * target instead; this helper cannot rescue it.
20
+ */
21
+ /** Minimal slice of `NodeRequire` needed here — keeps callers and tests honest. */
22
+ export interface ModuleResolver {
23
+ resolve(specifier: string): string;
24
+ }
25
+ export declare function readPackageManifest(packageRoot: string): Record<string, unknown> | null;
26
+ /**
27
+ * @param name Bare package name, scoped or unscoped (`rulesync`, `@scope/pkg`).
28
+ * @returns The package root directory, or `null` when it cannot be resolved.
29
+ */
30
+ export declare function resolvePackageRoot(name: string, resolver: ModuleResolver, maxDepth?: number): string | null;
31
+ /** Convenience wrapper: the installed `version` of `name`, or `null`. */
32
+ export declare function resolvePackageVersion(name: string, resolver: ModuleResolver): string | null;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Locate an installed package's root directory from its bare name.
3
+ *
4
+ * The obvious spelling — `require.resolve("<name>/package.json")` — is a trap.
5
+ * Node honours the target package's `exports` map, and a package that declares
6
+ * only `"."` (rulesync) makes that throw `ERR_PACKAGE_PATH_NOT_EXPORTED`. Every
7
+ * caller in this repo wrapped it in `try {} catch { return null }`, so the throw
8
+ * turned into a silently missing capability rather than a visible failure.
9
+ *
10
+ * Resolving the package's exported entry works instead, because any package
11
+ * meant to be imported has one. From there, walk up to the directory whose
12
+ * `package.json` actually declares that name — the entry may sit several levels
13
+ * deep (`<root>/dist/index.cjs`).
14
+ *
15
+ * Caveat worth knowing before reusing this: `resolve` runs under the CJS
16
+ * `require` condition. A package whose every export condition is `import`
17
+ * (ESM-only, as `@webpresso/agent-kit` itself is) has no entry resolvable this
18
+ * way at all. Such a package must export a condition-free `"./package.json"`
19
+ * target instead; this helper cannot rescue it.
20
+ */
21
+ import { existsSync, readFileSync } from "node:fs";
22
+ import { dirname, join } from "node:path";
23
+ const DEFAULT_MAX_WALK_DEPTH = 8;
24
+ export function readPackageManifest(packageRoot) {
25
+ const manifestPath = join(packageRoot, "package.json");
26
+ if (!existsSync(manifestPath))
27
+ return null;
28
+ try {
29
+ return JSON.parse(readFileSync(manifestPath, "utf-8"));
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ function declaresPackageName(packageRoot, name) {
36
+ return readPackageManifest(packageRoot)?.name === name;
37
+ }
38
+ /**
39
+ * @param name Bare package name, scoped or unscoped (`rulesync`, `@scope/pkg`).
40
+ * @returns The package root directory, or `null` when it cannot be resolved.
41
+ */
42
+ export function resolvePackageRoot(name, resolver, maxDepth = DEFAULT_MAX_WALK_DEPTH) {
43
+ let dir;
44
+ try {
45
+ // Prefer the explicit manifest subpath when the package exports it, since
46
+ // that lands on the root directly; fall back to the package entry.
47
+ dir = dirname(resolveFirst(resolver, [`${name}/package.json`, name]));
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ for (let depth = 0; depth < maxDepth; depth += 1) {
53
+ // Skip nested manifests (a bundled dependency) until the name matches.
54
+ if (declaresPackageName(dir, name))
55
+ return dir;
56
+ const parent = dirname(dir);
57
+ if (parent === dir)
58
+ return null;
59
+ dir = parent;
60
+ }
61
+ return null;
62
+ }
63
+ function resolveFirst(resolver, specifiers) {
64
+ let lastError;
65
+ for (const specifier of specifiers) {
66
+ try {
67
+ return resolver.resolve(specifier);
68
+ }
69
+ catch (error) {
70
+ lastError = error;
71
+ }
72
+ }
73
+ throw lastError instanceof Error ? lastError : new Error(`Cannot resolve ${specifiers[0]}`);
74
+ }
75
+ /** Convenience wrapper: the installed `version` of `name`, or `null`. */
76
+ export function resolvePackageVersion(name, resolver) {
77
+ const root = resolvePackageRoot(name, resolver);
78
+ if (!root)
79
+ return null;
80
+ const version = readPackageManifest(root)?.version;
81
+ return typeof version === "string" ? version : null;
82
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Git-level worktree admin metadata reconciliation.
3
+ *
4
+ * agent-kit keeps its own managed worktree registry, but git keeps a SEPARATE
5
+ * admin record per linked worktree under `.git/worktrees/<id>`. Deleting a
6
+ * worktree directory by hand (`rm -rf`) leaves that git record behind, still
7
+ * claiming the branch it was checked out on — so a later `git checkout <branch>`
8
+ * fails with `'<branch>' is already used by worktree at <deleted path>` and the
9
+ * registry-only prune cannot clear it.
10
+ *
11
+ * Only `git worktree prune` drops those records, so every agent-kit prune path
12
+ * runs it BEFORE reading git liveness: a stale record otherwise reports the
13
+ * deleted path as live and pins the matching registry entry in place too.
14
+ */
15
+ /** Minimal shape needed from a `git worktree list --porcelain` entry. */
16
+ export interface PrunableWorktreeCandidate {
17
+ readonly path: string;
18
+ readonly prunable?: boolean;
19
+ }
20
+ export interface GitWorktreeMetadataPruneResult {
21
+ /** Paths git reported as prunable immediately before the prune ran. */
22
+ readonly prunedPaths: readonly string[];
23
+ /** Non-null when `git worktree prune` itself failed; nothing was reconciled. */
24
+ readonly error: string | null;
25
+ }
26
+ /**
27
+ * Clear git's stale linked-worktree admin records for `repoRoot`.
28
+ *
29
+ * `entries` is the caller's already-fetched porcelain listing and is used only
30
+ * to report WHICH paths were cleared; the prune itself is unconditional so a
31
+ * missing or stale listing can never leave a wedged branch behind.
32
+ */
33
+ export declare function pruneStaleGitWorktreeMetadata(repoRoot: string, entries?: readonly PrunableWorktreeCandidate[]): GitWorktreeMetadataPruneResult;
34
+ /**
35
+ * True when `path` no longer has a usable working tree on disk — either git
36
+ * already flagged the entry prunable, or the directory itself is gone.
37
+ *
38
+ * A missing directory makes `git status` fail, which dirty checks would
39
+ * otherwise report as "uncommitted changes" and refuse to act on.
40
+ */
41
+ export declare function isStaleWorktreeRegistration(candidate: PrunableWorktreeCandidate | undefined, path: string, directoryExists: (target: string) => boolean): boolean;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Git-level worktree admin metadata reconciliation.
3
+ *
4
+ * agent-kit keeps its own managed worktree registry, but git keeps a SEPARATE
5
+ * admin record per linked worktree under `.git/worktrees/<id>`. Deleting a
6
+ * worktree directory by hand (`rm -rf`) leaves that git record behind, still
7
+ * claiming the branch it was checked out on — so a later `git checkout <branch>`
8
+ * fails with `'<branch>' is already used by worktree at <deleted path>` and the
9
+ * registry-only prune cannot clear it.
10
+ *
11
+ * Only `git worktree prune` drops those records, so every agent-kit prune path
12
+ * runs it BEFORE reading git liveness: a stale record otherwise reports the
13
+ * deleted path as live and pins the matching registry entry in place too.
14
+ */
15
+ import { spawnSync } from "node:child_process";
16
+ const GIT_PRUNE_TIMEOUT_MS = 10_000;
17
+ /**
18
+ * Clear git's stale linked-worktree admin records for `repoRoot`.
19
+ *
20
+ * `entries` is the caller's already-fetched porcelain listing and is used only
21
+ * to report WHICH paths were cleared; the prune itself is unconditional so a
22
+ * missing or stale listing can never leave a wedged branch behind.
23
+ */
24
+ export function pruneStaleGitWorktreeMetadata(repoRoot, entries = []) {
25
+ const prunedPaths = entries.filter((entry) => entry.prunable === true).map((entry) => entry.path);
26
+ const pruned = spawnSync("git", ["worktree", "prune"], {
27
+ cwd: repoRoot,
28
+ encoding: "utf8",
29
+ // Bounded like every other external probe here: this is a fast local
30
+ // metadata rewrite, so exceeding the bound means git is wedged, not slow.
31
+ timeout: GIT_PRUNE_TIMEOUT_MS,
32
+ });
33
+ if (pruned.status !== 0) {
34
+ const detail = String(pruned.stderr || pruned.stdout || "").trim();
35
+ return { prunedPaths: [], error: detail || "git worktree prune failed" };
36
+ }
37
+ return { prunedPaths, error: null };
38
+ }
39
+ /**
40
+ * True when `path` no longer has a usable working tree on disk — either git
41
+ * already flagged the entry prunable, or the directory itself is gone.
42
+ *
43
+ * A missing directory makes `git status` fail, which dirty checks would
44
+ * otherwise report as "uncommitted changes" and refuse to act on.
45
+ */
46
+ export function isStaleWorktreeRegistration(candidate, path, directoryExists) {
47
+ return candidate?.prunable === true || !directoryExists(path);
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpresso/agent-kit",
3
- "version": "3.3.3",
3
+ "version": "3.3.5",
4
4
  "private": false,
5
5
  "description": "TypeScript-first agent harness for guarded develop/deploy workflows: wp CLI gates, MCP tools, hooks, memory, worktrees, secrets, audits, and evidence checks.",
6
6
  "keywords": [
@@ -300,12 +300,7 @@
300
300
  "default": "./dist/esm/local.js"
301
301
  }
302
302
  },
303
- "./oxlint": {
304
- "import": {
305
- "types": "./dist/esm/config/oxlint/index.d.ts",
306
- "default": "./dist/esm/config/oxlint/index.js"
307
- }
308
- },
303
+ "./package.json": "./package.json",
309
304
  "./symlinker": {
310
305
  "import": {
311
306
  "types": "./dist/esm/symlinker/index.d.ts",
@@ -355,7 +350,7 @@
355
350
  },
356
351
  "scripts": {
357
352
  "setup:agent": "./bin/wp setup",
358
- "build": "tshy && ./bin/wp format --file package.json && bun src/build/blueprint-migration-assets.ts && bun src/build/sast-rule-assets.ts && bun src/build/generate-oxlintrc.ts && bun src/build/normalize-tsconfig-json-exports.ts && vp run chmod-bins",
353
+ "build": "pnpm --filter @webpresso/agent-core run build && pnpm --filter @webpresso/agent-config run build && tshy && ./bin/wp format --file package.json && bun src/build/blueprint-migration-assets.ts && bun src/build/sast-rule-assets.ts && bun src/build/generate-oxlintrc.ts && bun src/build/vendor-oxlint-plugins.ts && bun src/build/normalize-tsconfig-json-exports.ts && vp run chmod-bins",
359
354
  "build:runtime-binaries": "bun scripts/build-runtime-binaries.ts",
360
355
  "bench:hooks": "bun scripts/bench-hooks.ts",
361
356
  "stage:plugin-runtime": "bun scripts/stage-plugin-runtime-artifacts.ts",
@@ -446,7 +441,7 @@
446
441
  "ora": "^9.4.1",
447
442
  "proper-lockfile": "^4.1.2",
448
443
  "react": "^19.2.7",
449
- "rulesync": "14.0.1",
444
+ "rulesync": "15.0.0",
450
445
  "secretlint": "^13.0.2",
451
446
  "typescript": "^7.0.2",
452
447
  "yaml": "^2.9.0",
@@ -490,7 +485,7 @@
490
485
  "./e2e-preset/playwright": "./src/config/e2e-preset/playwright.ts",
491
486
  "./format": "./src/format/index.ts",
492
487
  "./local": "./src/local.ts",
493
- "./oxlint": "./src/config/oxlint/index.ts",
488
+ "./package.json": "./package.json",
494
489
  "./symlinker": "./src/symlinker/index.ts",
495
490
  "./test": "./src/test/index.ts",
496
491
  "./test-preset": "./src/config/test-preset/index.ts",
@@ -519,16 +514,16 @@
519
514
  "setupWpActionRef": "c2c71a7a4be446fc6858e6b57bf55a11ccfa2d88"
520
515
  },
521
516
  "optionalDependencies": {
522
- "@webpresso/agent-kit-runtime-darwin-arm64": "3.3.3",
523
- "@webpresso/agent-kit-runtime-darwin-x64": "3.3.3",
524
- "@webpresso/agent-kit-runtime-linux-x64": "3.3.3",
525
- "@webpresso/agent-kit-runtime-linux-arm64": "3.3.3",
526
- "@webpresso/agent-kit-runtime-windows-x64": "3.3.3",
527
- "@webpresso/agent-kit-session-memory-darwin-x64": "3.3.3",
528
- "@webpresso/agent-kit-session-memory-darwin-arm64": "3.3.3",
529
- "@webpresso/agent-kit-session-memory-linux-x64": "3.3.3",
530
- "@webpresso/agent-kit-session-memory-linux-arm64": "3.3.3",
531
- "@webpresso/agent-kit-session-memory-win32-x64": "3.3.3",
532
- "@webpresso/agent-kit-session-memory-win32-arm64": "3.3.3"
517
+ "@webpresso/agent-kit-runtime-darwin-arm64": "3.3.5",
518
+ "@webpresso/agent-kit-runtime-darwin-x64": "3.3.5",
519
+ "@webpresso/agent-kit-runtime-linux-x64": "3.3.5",
520
+ "@webpresso/agent-kit-runtime-linux-arm64": "3.3.5",
521
+ "@webpresso/agent-kit-runtime-windows-x64": "3.3.5",
522
+ "@webpresso/agent-kit-session-memory-darwin-x64": "3.3.5",
523
+ "@webpresso/agent-kit-session-memory-darwin-arm64": "3.3.5",
524
+ "@webpresso/agent-kit-session-memory-linux-x64": "3.3.5",
525
+ "@webpresso/agent-kit-session-memory-linux-arm64": "3.3.5",
526
+ "@webpresso/agent-kit-session-memory-win32-x64": "3.3.5",
527
+ "@webpresso/agent-kit-session-memory-win32-arm64": "3.3.5"
533
528
  }
534
529
  }
@@ -1,6 +0,0 @@
1
- import type { MergeOptions, MergeResult } from "#cli/commands/init/merge";
2
- export interface ScaffoldSubagentsInput {
3
- repoRoot: string;
4
- options: MergeOptions;
5
- }
6
- export declare function scaffoldSubagents(input: ScaffoldSubagentsInput): MergeResult[];
@@ -1,77 +0,0 @@
1
- import { existsSync, lstatSync, mkdirSync, readdirSync, readlinkSync, rmSync, symlinkSync, } from "node:fs";
2
- import { dirname, join, relative } from "node:path";
3
- import { readPackageJson } from "#cli/commands/init/detect-consumer";
4
- import { resolveAgentKitPackageRootOrThrow } from "#cli/commands/init/package-root";
5
- function detectMode(repoRoot) {
6
- const pkg = readPackageJson(repoRoot).info;
7
- if (pkg?.name === "@webpresso/agent-kit") {
8
- return {
9
- mode: "self",
10
- sourceRoot: join(repoRoot, "catalog", "agent", "agents"),
11
- };
12
- }
13
- const installedPackageJsonPath = join(repoRoot, "node_modules", "@webpresso", "agent-kit", "package.json");
14
- const installedAgentsRoot = join(repoRoot, "node_modules", "@webpresso", "agent-kit", "catalog", "agent", "agents");
15
- if (existsSync(installedPackageJsonPath) && existsSync(installedAgentsRoot)) {
16
- return {
17
- mode: "consumer",
18
- sourceRoot: installedAgentsRoot,
19
- };
20
- }
21
- return {
22
- mode: "package-fallback",
23
- sourceRoot: join(resolveCurrentPackageRoot(), "catalog", "agent", "agents"),
24
- };
25
- }
26
- function resolveCurrentPackageRoot() {
27
- return resolveAgentKitPackageRootOrThrow("wp init: could not locate the webpresso package root for subagents fallback.", { requireCatalog: true });
28
- }
29
- export function scaffoldSubagents(input) {
30
- const { repoRoot, options } = input;
31
- const mode = detectMode(repoRoot);
32
- const sourceRoot = mode.sourceRoot;
33
- const targetRoot = join(repoRoot, ".claude", "agents");
34
- const results = [];
35
- if (!existsSync(sourceRoot))
36
- return results;
37
- const entries = readdirSync(sourceRoot).filter((f) => f.endsWith(".md") && f !== "README.md");
38
- if (entries.length === 0)
39
- return results;
40
- if (!options.dryRun) {
41
- mkdirSync(targetRoot, { recursive: true });
42
- }
43
- for (const name of entries) {
44
- const sourcePath = join(sourceRoot, name);
45
- const targetPath = join(targetRoot, name);
46
- const symTarget = relative(dirname(targetPath), sourcePath);
47
- if (options.dryRun) {
48
- results.push({ targetPath, action: "created" });
49
- continue;
50
- }
51
- try {
52
- const stat = lstatSync(targetPath);
53
- if (stat.isSymbolicLink()) {
54
- const currentTarget = readlinkSync(targetPath);
55
- if (currentTarget === symTarget) {
56
- results.push({ targetPath, action: "identical" });
57
- }
58
- else if (options.overwrite) {
59
- rmSync(targetPath);
60
- symlinkSync(symTarget, targetPath);
61
- results.push({ targetPath, action: "overwritten" });
62
- }
63
- else {
64
- results.push({ targetPath, action: "drifted" });
65
- }
66
- }
67
- else {
68
- results.push({ targetPath, action: "identical" });
69
- }
70
- }
71
- catch {
72
- symlinkSync(symTarget, targetPath);
73
- results.push({ targetPath, action: "created" });
74
- }
75
- }
76
- return results;
77
- }
@@ -1,18 +0,0 @@
1
- declare const plugin: {
2
- meta: {
3
- name: string;
4
- };
5
- rules: {
6
- "as-any-audit": {
7
- create(context: any): {
8
- TSAsExpression(node: any): void;
9
- };
10
- };
11
- "no-swallowed-errors": {
12
- create(context: any): {
13
- CatchClause(node: any): void;
14
- };
15
- };
16
- };
17
- };
18
- export default plugin;
@@ -1,21 +0,0 @@
1
- declare const plugin: {
2
- meta: {
3
- name: string;
4
- };
5
- rules: {
6
- "no-framework-imports": {
7
- create(context: any): {
8
- ImportDeclaration?: undefined;
9
- ExportNamedDeclaration?: undefined;
10
- ExportAllDeclaration?: undefined;
11
- ImportExpression?: undefined;
12
- } | {
13
- ImportDeclaration(node: any): void;
14
- ExportNamedDeclaration(node: any): void;
15
- ExportAllDeclaration(node: any): void;
16
- ImportExpression(node: any): void;
17
- };
18
- };
19
- };
20
- };
21
- export default plugin;
@@ -1,23 +0,0 @@
1
- declare const plugin: {
2
- meta: {
3
- name: string;
4
- };
5
- rules: {
6
- "no-singular-graphql-fields": {
7
- create(context: any): {
8
- TemplateLiteral(node: any): void;
9
- Literal(node: any): void;
10
- };
11
- };
12
- "no-inline-graphql-in-app": {
13
- create(context: any): {
14
- TaggedTemplateExpression?: undefined;
15
- TemplateLiteral?: undefined;
16
- } | {
17
- TaggedTemplateExpression(node: any): void;
18
- TemplateLiteral(node: any): void;
19
- };
20
- };
21
- };
22
- };
23
- export default plugin;