@webpresso/agent-kit 0.21.3 → 0.21.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 (99) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +105 -41
  4. package/catalog/AGENTS.md.tpl +3 -1
  5. package/catalog/agent/rules/changeset-release.md +13 -16
  6. package/catalog/agent/skills/plan-refine/SKILL.md +5 -4
  7. package/catalog/base-kit/commitlint.config.ts.tmpl +1 -3
  8. package/catalog/base-kit/e2e/fixtures/smoke.html.tmpl +13 -0
  9. package/catalog/base-kit/e2e/smoke.spec.ts.tmpl +13 -0
  10. package/catalog/base-kit/oxlint.config.ts.tmpl +26 -0
  11. package/catalog/base-kit/playwright.config.ts.tmpl +10 -0
  12. package/catalog/base-kit/src/quality-sample.test.ts.tmpl +19 -0
  13. package/catalog/base-kit/src/quality-sample.ts.tmpl +11 -0
  14. package/catalog/base-kit/stryker.config.ts.tmpl +14 -0
  15. package/catalog/base-kit/tsconfig.json.tmpl +9 -0
  16. package/catalog/base-kit/vitest.config.ts.tmpl +10 -0
  17. package/catalog/docs/templates/adr.md +1 -1
  18. package/catalog/docs/templates/blueprint.md +1 -0
  19. package/catalog/docs/templates/blueprint.yaml +6 -3
  20. package/catalog/docs/templates/guide.md +1 -1
  21. package/catalog/docs/templates/postmortem.md +1 -1
  22. package/catalog/docs/templates/research.md +1 -1
  23. package/catalog/docs/templates/runbook.md +1 -1
  24. package/catalog/docs/templates/system.md +12 -3
  25. package/catalog/docs/templates/tech-debt.md +1 -0
  26. package/commands/blueprint.md +37 -4
  27. package/dist/esm/audit/resolve-audit-script.d.ts +24 -0
  28. package/dist/esm/audit/resolve-audit-script.js +27 -0
  29. package/dist/esm/blueprint/db/enums.d.ts +1 -1
  30. package/dist/esm/blueprint/index.d.ts +0 -1
  31. package/dist/esm/blueprint/index.js +0 -2
  32. package/dist/esm/blueprint/local.d.ts +0 -3
  33. package/dist/esm/blueprint/local.js +0 -2
  34. package/dist/esm/blueprint/service/BlueprintCreationService.js +5 -2
  35. package/dist/esm/blueprint/utils/package-assets.d.ts +11 -0
  36. package/dist/esm/blueprint/utils/package-assets.js +33 -4
  37. package/dist/esm/build/sync-catalog-doc-templates.d.ts +23 -0
  38. package/dist/esm/build/sync-catalog-doc-templates.js +93 -0
  39. package/dist/esm/cli/commands/audit.js +2 -7
  40. package/dist/esm/cli/commands/blueprint/router.js +5 -2
  41. package/dist/esm/cli/commands/blueprint/template-resolver.js +8 -4
  42. package/dist/esm/cli/commands/init/host-visibility.js +4 -2
  43. package/dist/esm/cli/commands/init/index.js +46 -7
  44. package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +12 -0
  45. package/dist/esm/cli/commands/init/scaffold-base-kit.js +141 -6
  46. package/dist/esm/cli/commands/typecheck.js +10 -4
  47. package/dist/esm/e2e/command-builder.js +26 -7
  48. package/dist/esm/e2e/execution.js +4 -0
  49. package/dist/esm/e2e/run-planner.js +1 -0
  50. package/dist/esm/e2e/types.d.ts +1 -0
  51. package/dist/esm/format/index.js +7 -1
  52. package/dist/esm/lint/index.js +3 -1
  53. package/dist/esm/mcp/blueprint-server.js +361 -66
  54. package/dist/esm/mcp/tools/audit.js +2 -8
  55. package/dist/esm/mcp/tools/e2e.d.ts +1 -1
  56. package/dist/esm/package.json +3 -0
  57. package/dist/esm/secret-gate/runner.js +4 -0
  58. package/dist/esm/test/command-builder.d.ts +1 -0
  59. package/dist/esm/test/command-builder.js +8 -2
  60. package/dist/esm/test-helpers/hermetic-env.d.ts +25 -0
  61. package/dist/esm/test-helpers/hermetic-env.js +31 -0
  62. package/dist/esm/tool-runtime/index.d.ts +5 -0
  63. package/dist/esm/tool-runtime/index.js +23 -0
  64. package/dist/esm/tool-runtime/resolve-runner.d.ts +13 -0
  65. package/dist/esm/tool-runtime/resolve-runner.js +40 -0
  66. package/package.json +12 -18
  67. package/skills/plan-refine/SKILL.md +5 -4
  68. package/dist/esm/blueprint/dag/cycle-detector.d.ts +0 -12
  69. package/dist/esm/blueprint/dag/cycle-detector.js +0 -46
  70. package/dist/esm/blueprint/dag/executor.d.ts +0 -140
  71. package/dist/esm/blueprint/dag/executor.js +0 -292
  72. package/dist/esm/blueprint/dag/index.d.ts +0 -20
  73. package/dist/esm/blueprint/dag/index.js +0 -17
  74. package/dist/esm/blueprint/dag/interfaces.d.ts +0 -56
  75. package/dist/esm/blueprint/dag/interfaces.js +0 -13
  76. package/dist/esm/blueprint/dag/local/independence.d.ts +0 -107
  77. package/dist/esm/blueprint/dag/local/independence.js +0 -231
  78. package/dist/esm/blueprint/dag/local/index.d.ts +0 -14
  79. package/dist/esm/blueprint/dag/local/index.js +0 -14
  80. package/dist/esm/blueprint/dag/local/package-graph.d.ts +0 -66
  81. package/dist/esm/blueprint/dag/local/package-graph.js +0 -148
  82. package/dist/esm/blueprint/dag/plan-parser.d.ts +0 -54
  83. package/dist/esm/blueprint/dag/plan-parser.js +0 -236
  84. package/dist/esm/blueprint/dag/task-graph-algorithms.d.ts +0 -13
  85. package/dist/esm/blueprint/dag/task-graph-algorithms.js +0 -236
  86. package/dist/esm/blueprint/dag/task-graph.d.ts +0 -171
  87. package/dist/esm/blueprint/dag/task-graph.js +0 -370
  88. package/dist/esm/blueprint/dag/types.d.ts +0 -17
  89. package/dist/esm/blueprint/dag/types.js +0 -2
  90. package/dist/esm/blueprint/graph/index.d.ts +0 -5
  91. package/dist/esm/blueprint/graph/index.js +0 -5
  92. package/dist/esm/blueprint/graph/mermaid-parser.d.ts +0 -3
  93. package/dist/esm/blueprint/graph/mermaid-parser.js +0 -93
  94. package/dist/esm/blueprint/graph/mermaid-serializer.d.ts +0 -3
  95. package/dist/esm/blueprint/graph/mermaid-serializer.js +0 -20
  96. package/dist/esm/blueprint/graph/schema.d.ts +0 -89
  97. package/dist/esm/blueprint/graph/schema.js +0 -104
  98. package/dist/esm/blueprint/graph/task-graph-adapter.d.ts +0 -6
  99. package/dist/esm/blueprint/graph/task-graph-adapter.js +0 -30
@@ -1,6 +1,30 @@
1
1
  import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { writeFileMerged } from './merge.js';
4
+ const AUTHORING_TIME_DEPENDENCIES = [
5
+ 'vitest',
6
+ '@playwright/test',
7
+ '@testing-library/jest-dom',
8
+ 'typescript',
9
+ ];
10
+ const EXECUTION_ONLY_REVIEW_DEPENDENCIES = [
11
+ 'oxlint',
12
+ 'oxfmt',
13
+ 'prettier',
14
+ 'markdownlint-cli2',
15
+ 'stryker',
16
+ ];
17
+ export function collectRuntimeContractGuidance(packageJson) {
18
+ const deps = {
19
+ ...readDependencyBucket(packageJson?.['dependencies']),
20
+ ...readDependencyBucket(packageJson?.['devDependencies']),
21
+ };
22
+ const installed = new Set(Object.keys(deps));
23
+ return {
24
+ keepLocalAuthoringDeps: AUTHORING_TIME_DEPENDENCIES.filter((name) => installed.has(name)),
25
+ reviewForRemovalDeps: EXECUTION_ONLY_REVIEW_DEPENDENCIES.filter((name) => installed.has(name)),
26
+ };
27
+ }
4
28
  /** Template files relative to `catalog/base-kit/`, and their target paths relative to repoRoot. */
5
29
  const TEMPLATE_MAP = [
6
30
  ['.editorconfig.tmpl', '.editorconfig'],
@@ -18,6 +42,19 @@ const TEMPLATE_MAP = [
18
42
  ['test/.gitkeep.tmpl', 'test/.gitkeep'],
19
43
  ['e2e/.gitkeep.tmpl', 'e2e/.gitkeep'],
20
44
  ];
45
+ /** Consumer-owned quality scaffold: create for fresh repos, never clobber. */
46
+ const QUALITY_BOOTSTRAP_ONLY_MAP = [
47
+ ['tsconfig.json.tmpl', 'tsconfig.json'],
48
+ ['vitest.config.ts.tmpl', 'vitest.config.ts'],
49
+ ['oxlint.config.ts.tmpl', 'oxlint.config.ts'],
50
+ ['stryker.config.ts.tmpl', 'stryker.config.ts'],
51
+ ['playwright.config.ts.tmpl', 'playwright.config.ts'],
52
+ ['src/quality-sample.ts.tmpl', 'src/quality-sample.ts'],
53
+ ['src/quality-sample.test.ts.tmpl', 'src/quality-sample.test.ts'],
54
+ ['e2e/fixtures/smoke.html.tmpl', 'e2e/fixtures/smoke.html'],
55
+ ['e2e/smoke.spec.ts.tmpl', 'e2e/smoke.spec.ts'],
56
+ ];
57
+ export const BASE_KIT_QUALITY_TARGETS = QUALITY_BOOTSTRAP_ONLY_MAP.map(([, targetRel]) => targetRel);
21
58
  /**
22
59
  * Bootstrap-only templates: the scaffolder writes them when absent (so a
23
60
  * fresh repo gets sane defaults) but NEVER overwrites them once they exist
@@ -75,35 +112,85 @@ function mergePackageJson(repoRoot, options, globalInstall = false) {
75
112
  const hasVerifySecrets = typeof scripts['verify:secrets'] === 'string';
76
113
  const hasSecretQuarantineAudit = typeof scripts['audit:secret-provider-quarantine'] === 'string';
77
114
  const hasPrepareScript = typeof scripts['prepare'] === 'string';
115
+ const hasLintScript = typeof scripts['lint'] === 'string';
116
+ const hasTypecheckScript = typeof scripts['typecheck'] === 'string';
117
+ const hasTestScript = typeof scripts['test'] === 'string' && !isNpmInitPlaceholderTestScript(scripts['test']);
118
+ const hasMutationScript = typeof scripts['mutation'] === 'string';
119
+ const hasTestMutationScript = typeof scripts['test:mutation'] === 'string';
120
+ const hasE2eScript = typeof scripts['e2e'] === 'string';
121
+ const hasQaScript = typeof scripts['qa'] === 'string';
78
122
  const verifyPathsScript = 'WP_SKIP_UPDATE_CHECK=1 wp audit absolute-path-policy --root .';
79
123
  const verifySecretsScript = 'bun scripts/check-no-dev-vars.ts';
80
124
  const secretQuarantineAuditScript = 'bun scripts/audit-secret-provider-quarantine.ts';
125
+ const lintScript = 'wp lint src e2e *.config.ts';
126
+ const typecheckScript = 'wp typecheck';
127
+ const testScript = 'wp test --file vitest.config.ts';
128
+ const mutationScript = 'wp test --mutation';
129
+ const testMutationScript = 'stryker run stryker.config.ts';
130
+ const e2eScript = 'wp e2e --config playwright.config.ts';
131
+ const qaScript = [
132
+ 'wp lint src e2e *.config.ts',
133
+ 'wp typecheck',
134
+ 'wp test --file vitest.config.ts',
135
+ 'wp test --mutation',
136
+ 'wp e2e --config playwright.config.ts',
137
+ ].join(' && ');
81
138
  const devDeps = (pkg['devDependencies'] ?? {});
82
- const hasAgentKitDevDep = typeof devDeps['webpresso'] === 'string';
83
- const shouldSkipSelfInstall = packageName === 'webpresso';
139
+ const hasAgentKitDevDep = typeof devDeps['@webpresso/agent-kit'] === 'string';
140
+ const hasLegacyAgentKitDevDep = typeof devDeps['webpresso'] === 'string';
141
+ const shouldSkipSelfInstall = packageName === '@webpresso/agent-kit' || packageName === 'webpresso';
84
142
  const shouldManageAgentKitAsGlobal = globalInstall && !shouldSkipSelfInstall;
143
+ const requiredAuthoringDeps = {
144
+ '@playwright/test': 'latest',
145
+ '@stryker-mutator/core': 'latest',
146
+ '@stryker-mutator/vitest-runner': 'latest',
147
+ '@types/node': 'latest',
148
+ typescript: 'latest',
149
+ vitest: 'latest',
150
+ };
85
151
  if (alreadyHasEngines &&
86
152
  alreadyHasPm &&
87
- (shouldSkipSelfInstall || shouldManageAgentKitAsGlobal || hasAgentKitDevDep) &&
153
+ (shouldSkipSelfInstall ||
154
+ shouldManageAgentKitAsGlobal ||
155
+ hasAgentKitDevDep ||
156
+ hasLegacyAgentKitDevDep) &&
157
+ Object.keys(requiredAuthoringDeps).every((name) => typeof devDeps[name] === 'string') &&
88
158
  (shouldSkipSelfInstall || hasSetupAgent) &&
89
159
  (shouldSkipSelfInstall || hasVerifyPaths) &&
90
160
  (shouldSkipSelfInstall || hasVerifySecrets) &&
91
161
  (shouldSkipSelfInstall || hasSecretQuarantineAudit) &&
92
- (shouldSkipSelfInstall || hasPrepareScript)) {
162
+ (shouldSkipSelfInstall || hasPrepareScript) &&
163
+ hasLintScript &&
164
+ hasTypecheckScript &&
165
+ hasTestScript &&
166
+ hasMutationScript &&
167
+ hasTestMutationScript &&
168
+ hasE2eScript &&
169
+ hasQaScript) {
93
170
  return { targetPath: pkgPath, action: 'identical' };
94
171
  }
95
172
  pkg['engines'] = { ...existing, node: engines.node };
96
173
  if (!alreadyHasPm)
97
174
  pkg['packageManager'] = packageManager;
175
+ if (typeof pkg['type'] !== 'string')
176
+ pkg['type'] = 'module';
98
177
  // Ensure husky is in devDependencies so `vp exec husky init` works
99
178
  if (!devDeps['husky']) {
100
179
  devDeps['husky'] = '^9.0.0';
101
180
  }
102
- if (!shouldSkipSelfInstall && !shouldManageAgentKitAsGlobal && !hasAgentKitDevDep) {
181
+ if (!shouldSkipSelfInstall &&
182
+ !shouldManageAgentKitAsGlobal &&
183
+ !hasAgentKitDevDep &&
184
+ !hasLegacyAgentKitDevDep) {
103
185
  // Keep consumers on the currently published dist-tag rather than a
104
186
  // repo-internal path. Do not wire this through `prepare`: `wp` is not
105
187
  // reliably on PATH during `vp install`, so `setup:agent` stays opt-in.
106
- devDeps['webpresso'] = 'latest';
188
+ devDeps['@webpresso/agent-kit'] = 'latest';
189
+ }
190
+ for (const [name, version] of Object.entries(requiredAuthoringDeps)) {
191
+ if (!devDeps[name]) {
192
+ devDeps[name] = version;
193
+ }
107
194
  }
108
195
  pkg['devDependencies'] = devDeps;
109
196
  if (!shouldSkipSelfInstall && !hasSetupAgent) {
@@ -121,6 +208,27 @@ function mergePackageJson(repoRoot, options, globalInstall = false) {
121
208
  if (!shouldSkipSelfInstall && !hasPrepareScript) {
122
209
  scripts['prepare'] = 'husky';
123
210
  }
211
+ if (!hasLintScript) {
212
+ scripts['lint'] = lintScript;
213
+ }
214
+ if (!hasTypecheckScript) {
215
+ scripts['typecheck'] = typecheckScript;
216
+ }
217
+ if (!hasTestScript) {
218
+ scripts['test'] = testScript;
219
+ }
220
+ if (!hasMutationScript) {
221
+ scripts['mutation'] = mutationScript;
222
+ }
223
+ if (!hasTestMutationScript) {
224
+ scripts['test:mutation'] = testMutationScript;
225
+ }
226
+ if (!hasE2eScript) {
227
+ scripts['e2e'] = e2eScript;
228
+ }
229
+ if (!hasQaScript) {
230
+ scripts['qa'] = qaScript;
231
+ }
124
232
  if (Object.keys(scripts).length > 0) {
125
233
  pkg['scripts'] = scripts;
126
234
  }
@@ -161,6 +269,24 @@ export function scaffoldBaseKit(input) {
161
269
  writeFileSync(targetPath, content);
162
270
  results.push({ targetPath, action: 'created' });
163
271
  }
272
+ for (const [tmplRel, targetRel] of QUALITY_BOOTSTRAP_ONLY_MAP) {
273
+ const tmplPath = join(baseKitDir, tmplRel);
274
+ if (!existsSync(tmplPath))
275
+ continue;
276
+ const targetPath = join(repoRoot, targetRel);
277
+ if (existsSync(targetPath)) {
278
+ results.push({ targetPath, action: 'identical' });
279
+ continue;
280
+ }
281
+ const content = readFileSync(tmplPath, 'utf8');
282
+ if (options.dryRun) {
283
+ results.push({ targetPath, action: 'skipped-dry' });
284
+ continue;
285
+ }
286
+ mkdirSync(dirname(targetPath), { recursive: true });
287
+ writeFileSync(targetPath, content);
288
+ results.push({ targetPath, action: 'created' });
289
+ }
164
290
  // Make husky hook files executable
165
291
  if (!options.dryRun) {
166
292
  for (const [tmplRel, targetRel] of TEMPLATE_MAP) {
@@ -180,4 +306,13 @@ export function scaffoldBaseKit(input) {
180
306
  results.push(mergePackageJson(repoRoot, options, globalInstall));
181
307
  return results;
182
308
  }
309
+ function readDependencyBucket(value) {
310
+ if (!value || typeof value !== 'object') {
311
+ return {};
312
+ }
313
+ return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[0] === 'string' && typeof entry[1] === 'string'));
314
+ }
315
+ function isNpmInitPlaceholderTestScript(value) {
316
+ return /^echo ['"]?Error: no test specified['"]? && exit 1$/u.test(value.trim());
317
+ }
183
318
  //# sourceMappingURL=scaffold-base-kit.js.map
@@ -1,3 +1,4 @@
1
+ import { getManagedRunner } from '#tool-runtime';
1
2
  import { spawnSync } from 'node:child_process';
2
3
  import { existsSync, readFileSync } from 'node:fs';
3
4
  import { join } from 'node:path';
@@ -17,14 +18,19 @@ export function registerTypecheckCommand(cli) {
17
18
  export function buildTypecheckCommand(options = {}) {
18
19
  const cwd = options.cwd ?? process.cwd();
19
20
  if (hasCheckTypesScript(cwd)) {
21
+ const resolution = getManagedRunner('vp');
20
22
  return {
21
- command: 'vp',
22
- args: ['run', 'check-types'],
23
+ command: resolution.command,
24
+ args: [...resolution.args, 'run', 'check-types'],
23
25
  };
24
26
  }
27
+ const resolution = getManagedRunner('tsc', {
28
+ fallbackCommand: 'tsc',
29
+ fallbackArgs: [],
30
+ });
25
31
  return {
26
- command: 'tsc',
27
- args: ['--noEmit', ...(options.pretty ? [] : ['--pretty', 'false'])],
32
+ command: resolution.command,
33
+ args: [...resolution.args, '--noEmit', ...(options.pretty ? [] : ['--pretty', 'false'])],
28
34
  };
29
35
  }
30
36
  export function runTypecheckCommand(options = {}, deps = {}) {
@@ -1,3 +1,4 @@
1
+ import { getManagedRunner } from '#tool-runtime';
1
2
  import path from 'node:path';
2
3
  export function buildE2eCommand(options) {
3
4
  switch (options.step.runner) {
@@ -15,10 +16,11 @@ function buildPlaywrightCommand(options) {
15
16
  throw new Error(`Step ${step.logName} uses runner "playwright" but does not define configPath.`);
16
17
  }
17
18
  const { baseDir, configArg, files } = resolveRunnerPaths(step.configPath, options.files ?? []);
18
- const args = [...buildPnpmExecPrefix(baseDir), 'playwright', 'test', '--config', configArg];
19
+ const resolution = withBaseDir(getManagedRunner('playwright', { filterOutput: options.filterOutput }), baseDir);
20
+ const args = [...resolution.args, 'test', '--config', configArg];
19
21
  appendPlaywrightFlags(args, options);
20
22
  args.push(...(step.fixedArgs ?? []), ...files, ...(options.passthrough ?? []));
21
- return { command: 'pnpm', args };
23
+ return { command: resolution.command, args };
22
24
  }
23
25
  function buildVitestE2eCommand(options) {
24
26
  const { step } = options;
@@ -26,12 +28,13 @@ function buildVitestE2eCommand(options) {
26
28
  throw new Error(`Step ${step.logName} uses runner "vitest" but does not define configPath.`);
27
29
  }
28
30
  const { baseDir, configArg, files } = resolveRunnerPaths(step.configPath, options.files ?? []);
29
- const args = [...buildPnpmExecPrefix(baseDir), 'vitest', 'run', '--config', configArg];
31
+ const resolution = withBaseDir(getManagedRunner('vitest', { filterOutput: options.filterOutput }), baseDir);
32
+ const args = [...resolution.args, 'run', '--config', configArg];
30
33
  if (options.workers !== undefined) {
31
34
  args.push('--poolOptions.threads.maxThreads', String(options.workers));
32
35
  }
33
36
  args.push(...(step.fixedArgs ?? []), ...files, ...(options.passthrough ?? []));
34
- return { command: 'pnpm', args };
37
+ return { command: resolution.command, args };
35
38
  }
36
39
  function buildCustomCommand(options) {
37
40
  const { step } = options;
@@ -69,9 +72,6 @@ function appendPlaywrightFlags(args, options) {
69
72
  args.push('--test-list', options.testList);
70
73
  }
71
74
  }
72
- function buildPnpmExecPrefix(baseDir) {
73
- return baseDir === '.' ? ['exec'] : ['--dir', baseDir, 'exec'];
74
- }
75
75
  function resolveRunnerPaths(configPath, files) {
76
76
  const normalizedConfigPath = configPath.replace(/\\/gu, '/');
77
77
  const baseDir = path.posix.dirname(normalizedConfigPath);
@@ -95,4 +95,23 @@ function resolveRunnerPaths(configPath, files) {
95
95
  }),
96
96
  };
97
97
  }
98
+ function withBaseDir(resolution, baseDir) {
99
+ if (baseDir === '.') {
100
+ return { command: resolution.command, args: [...resolution.args] };
101
+ }
102
+ if (resolution.command === 'vp') {
103
+ return {
104
+ command: resolution.command,
105
+ args: ['--dir', baseDir, ...resolution.args],
106
+ };
107
+ }
108
+ const [wrappedCommand, ...wrappedArgs] = resolution.args;
109
+ if (resolution.command === 'rtk' && wrappedCommand === 'vp') {
110
+ return {
111
+ command: resolution.command,
112
+ args: ['vp', '--dir', baseDir, ...wrappedArgs],
113
+ };
114
+ }
115
+ return { command: resolution.command, args: [...resolution.args] };
116
+ }
98
117
  //# sourceMappingURL=command-builder.js.map
@@ -18,6 +18,7 @@ export async function createE2eExecutionPlan(input, cwd = process.cwd()) {
18
18
  workers: input.workers,
19
19
  testList: input.testList,
20
20
  passthrough: input.passthrough,
21
+ filterOutput: input.filterOutput,
21
22
  });
22
23
  }
23
24
  const hostAdapter = await loadConfiguredHostAdapter(cwd);
@@ -35,6 +36,7 @@ export async function createE2eExecutionPlan(input, cwd = process.cwd()) {
35
36
  workers: input.workers,
36
37
  testList: input.testList,
37
38
  passthrough: input.passthrough,
39
+ filterOutput: input.filterOutput,
38
40
  });
39
41
  }
40
42
  if (hostAdapter.adapter.buildExecutionPlan) {
@@ -49,6 +51,7 @@ export async function createE2eExecutionPlan(input, cwd = process.cwd()) {
49
51
  workers: input.workers,
50
52
  testList: input.testList,
51
53
  passthrough: input.passthrough,
54
+ filterOutput: input.filterOutput,
52
55
  });
53
56
  }
54
57
  return planE2eRun({
@@ -60,6 +63,7 @@ export async function createE2eExecutionPlan(input, cwd = process.cwd()) {
60
63
  workers: input.workers,
61
64
  testList: input.testList,
62
65
  passthrough: input.passthrough,
66
+ filterOutput: input.filterOutput,
63
67
  });
64
68
  }
65
69
  export function plannedGroupsToCommandConfigs(groups) {
@@ -76,6 +76,7 @@ function planE2eRunsFromSuites(options) {
76
76
  workers: options.request.workers,
77
77
  testList: options.request.testList,
78
78
  passthrough: options.request.passthrough,
79
+ filterOutput: options.request.filterOutput,
79
80
  });
80
81
  runs.push({
81
82
  suiteId: suite.id,
@@ -48,6 +48,7 @@ export interface E2eCommandRequest {
48
48
  workers?: number | string;
49
49
  testList?: string;
50
50
  passthrough?: readonly string[];
51
+ filterOutput?: boolean;
51
52
  }
52
53
  export interface E2eExecutionRequest extends E2eCommandRequest {
53
54
  suite?: string;
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { isMissingBinary, isRunFailure, runCommand } from '#mcp/tools/_shared/run-command';
10
10
  import { resolveProjectRoot } from '#mcp/tools/_shared/project-root';
11
+ import { getManagedRunner } from '#tool-runtime';
11
12
  const DEFAULT_FORMAT_TIMEOUT_MS = 5 * 60 * 1_000;
12
13
  /**
13
14
  * Run formatter and return a structured result. Throws a clear error when
@@ -33,7 +34,12 @@ export async function runFormat(options = {}) {
33
34
  args.push('--ignore-path', '.gitignore');
34
35
  if (options.files && options.files.length > 0)
35
36
  args.push(...options.files);
36
- const outcome = await runCommand('oxfmt', args, runOptions);
37
+ const resolution = getManagedRunner('oxfmt', {
38
+ fallbackCommand: 'oxfmt',
39
+ fallbackArgs: [],
40
+ filterOutput: false,
41
+ });
42
+ const outcome = await runCommand(resolution.command, [...resolution.args, ...args], runOptions);
37
43
  if (isRunFailure(outcome)) {
38
44
  if (isMissingBinary(outcome)) {
39
45
  throw new Error("oxfmt binary not found on PATH. Install it as a devDependency: 'vp install -D oxfmt'");
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { isRunFailure, runCommand } from '#mcp/tools/_shared/run-command';
10
10
  import { resolveProjectRoot } from '#mcp/tools/_shared/project-root';
11
+ import { getManagedRunner } from '#tool-runtime';
11
12
  const DEFAULT_LINT_TIMEOUT_MS = 5 * 60 * 1_000;
12
13
  /**
13
14
  * Parse oxlint's `--format=json` output (ESLint-compatible array shape) into
@@ -92,7 +93,8 @@ export async function runLint(options = {}) {
92
93
  else {
93
94
  lintArgs.push('.');
94
95
  }
95
- const vpOutcome = await runCommand('vp', lintArgs, runOptions);
96
+ const resolution = getManagedRunner('vp', { filterOutput: false });
97
+ const vpOutcome = await runCommand(resolution.command, [...resolution.args, ...lintArgs], runOptions);
96
98
  if (isRunFailure(vpOutcome)) {
97
99
  return {
98
100
  passed: false,