@wix/pathgrade 0.37.0 → 1.0.1

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 (119) hide show
  1. package/README.md +116 -32
  2. package/dist/adapter-kit/index.d.ts +17 -0
  3. package/dist/adapter-kit/index.js +14 -0
  4. package/dist/adapters/jest/index.d.ts +3 -0
  5. package/dist/adapters/jest/index.js +3 -0
  6. package/dist/adapters/jest/invocation-adapter.d.ts +15 -0
  7. package/dist/adapters/jest/invocation-adapter.js +115 -0
  8. package/dist/adapters/jest/lifecycle.d.ts +21 -0
  9. package/dist/adapters/jest/lifecycle.js +62 -0
  10. package/dist/adapters/jest/metadata.d.ts +6 -0
  11. package/dist/adapters/jest/metadata.js +35 -0
  12. package/dist/adapters/jest/reporter.cjs +10 -0
  13. package/dist/adapters/jest/reporter.d.ts +5 -0
  14. package/dist/adapters/jest/reporter.js +67 -0
  15. package/dist/adapters/jest/results.d.ts +31 -0
  16. package/dist/adapters/jest/results.js +144 -0
  17. package/dist/adapters/jest/runner-adapter.d.ts +9 -0
  18. package/dist/adapters/jest/runner-adapter.js +49 -0
  19. package/dist/adapters/jest/setup.d.ts +1 -0
  20. package/dist/adapters/jest/setup.js +10 -0
  21. package/dist/adapters/node-test/index.d.ts +8 -0
  22. package/dist/adapters/node-test/index.js +106 -0
  23. package/dist/adapters/node-test/invocation-adapter.d.ts +5 -0
  24. package/dist/adapters/node-test/invocation-adapter.js +47 -0
  25. package/dist/adapters/node-test/runner-adapter.d.ts +2 -0
  26. package/dist/adapters/node-test/runner-adapter.js +116 -0
  27. package/dist/adapters/vitest/index.d.ts +9 -0
  28. package/dist/adapters/vitest/index.js +82 -0
  29. package/dist/adapters/vitest/lifecycle.d.ts +24 -0
  30. package/dist/adapters/vitest/lifecycle.js +41 -0
  31. package/dist/adapters/vitest/reporter.d.ts +12 -0
  32. package/dist/adapters/vitest/reporter.js +75 -0
  33. package/dist/adapters/vitest/setup.d.ts +1 -0
  34. package/dist/adapters/vitest/setup.js +3 -0
  35. package/dist/agents/claude/sdk-options.js +6 -0
  36. package/dist/agents/codex-app-server/agent.js +5 -1
  37. package/dist/agents/codex-app-server/protocol/index.js +3 -3
  38. package/dist/agents/codex-app-server/turn-completion.d.ts +1 -0
  39. package/dist/agents/codex-app-server/turn-completion.js +9 -0
  40. package/dist/commands/affected.js +22 -15
  41. package/dist/commands/run-args.d.ts +4 -3
  42. package/dist/commands/run-args.js +12 -6
  43. package/dist/commands/run-changed.d.ts +4 -4
  44. package/dist/commands/run-changed.js +45 -58
  45. package/dist/config/pathgrade.d.ts +48 -0
  46. package/dist/config/pathgrade.js +204 -0
  47. package/dist/pathgrade.d.ts +2 -2
  48. package/dist/pathgrade.js +35 -34
  49. package/dist/plugin/index.d.ts +2 -9
  50. package/dist/plugin/index.js +1 -81
  51. package/dist/plugin/lifecycle.d.ts +1 -45
  52. package/dist/plugin/lifecycle.js +1 -146
  53. package/dist/plugin/reporter.d.ts +1 -37
  54. package/dist/plugin/reporter.js +1 -280
  55. package/dist/plugin/setup.d.ts +1 -1
  56. package/dist/plugin/setup.js +1 -3
  57. package/dist/providers/copy-filter.d.ts +5 -0
  58. package/dist/providers/copy-filter.js +9 -0
  59. package/dist/providers/credentials.js +1 -1
  60. package/dist/providers/sandbox-lifecycle.d.ts +22 -0
  61. package/dist/providers/sandbox-lifecycle.js +133 -0
  62. package/dist/providers/sandbox.js +4 -3
  63. package/dist/providers/workspace.js +4 -14
  64. package/dist/reporters/diagnostics.d.ts +2 -40
  65. package/dist/reporters/diagnostics.js +1 -80
  66. package/dist/reporters/report-summary.d.ts +6 -0
  67. package/dist/reporters/report-summary.js +29 -0
  68. package/dist/reporting/artifacts.d.ts +2 -0
  69. package/dist/reporting/artifacts.js +20 -0
  70. package/dist/reporting/core.d.ts +2 -0
  71. package/dist/reporting/core.js +173 -0
  72. package/dist/reporting/types.d.ts +57 -0
  73. package/dist/reporting/types.js +1 -0
  74. package/dist/reporting/vitest-edge.d.ts +1 -0
  75. package/dist/reporting/vitest-edge.js +1 -0
  76. package/dist/runners/adapter-loader.d.ts +17 -0
  77. package/dist/runners/adapter-loader.js +67 -0
  78. package/dist/runners/adapter.d.ts +65 -0
  79. package/dist/runners/adapter.js +1 -0
  80. package/dist/runners/invocation.d.ts +10 -0
  81. package/dist/runners/invocation.js +1 -0
  82. package/dist/runners/lifecycle-hooks.d.ts +2 -0
  83. package/dist/runners/lifecycle-hooks.js +18 -0
  84. package/dist/runners/model-builders.d.ts +4 -0
  85. package/dist/runners/model-builders.js +67 -0
  86. package/dist/runners/model-validation.d.ts +12 -0
  87. package/dist/runners/model-validation.js +213 -0
  88. package/dist/runners/model.d.ts +117 -0
  89. package/dist/runners/model.js +1 -0
  90. package/dist/runners/orchestrator.d.ts +30 -0
  91. package/dist/runners/orchestrator.js +74 -0
  92. package/dist/runners/report-projection.d.ts +6 -0
  93. package/dist/runners/report-projection.js +62 -0
  94. package/dist/runners/selection.d.ts +6 -0
  95. package/dist/runners/selection.js +10 -0
  96. package/dist/runners/vitest-adapter.d.ts +8 -0
  97. package/dist/runners/vitest-adapter.js +108 -0
  98. package/dist/runners/vitest-invocation.d.ts +10 -0
  99. package/dist/runners/vitest-invocation.js +44 -0
  100. package/dist/runners/vitest-lifecycle.d.ts +43 -0
  101. package/dist/runners/vitest-lifecycle.js +150 -0
  102. package/dist/sdk/agent.js +15 -16
  103. package/dist/sdk/case-context.d.ts +20 -0
  104. package/dist/sdk/case-context.js +31 -0
  105. package/dist/sdk/diagnostics.d.ts +40 -0
  106. package/dist/sdk/diagnostics.js +80 -0
  107. package/dist/sdk/eval-runtime.d.ts +4 -0
  108. package/dist/sdk/eval-runtime.js +11 -1
  109. package/dist/sdk/evaluate.js +2 -1
  110. package/dist/sdk/index.d.ts +2 -0
  111. package/dist/sdk/index.js +1 -0
  112. package/dist/sdk/lifecycle.d.ts +40 -0
  113. package/dist/sdk/lifecycle.js +170 -0
  114. package/dist/sdk/result-capture.d.ts +18 -0
  115. package/dist/sdk/result-capture.js +49 -0
  116. package/dist/sdk/types.d.ts +1 -1
  117. package/dist/types.d.ts +2 -2
  118. package/dist/utils/llm-providers/anthropic.js +9 -2
  119. package/package.json +56 -4
@@ -0,0 +1,48 @@
1
+ export interface PathgradeConfig {
2
+ runner?: {
3
+ adapter?: string;
4
+ args?: string[];
5
+ };
6
+ evals?: {
7
+ include?: string[];
8
+ exclude?: string[];
9
+ };
10
+ affected?: {
11
+ global?: string[];
12
+ };
13
+ reporter?: 'cli' | 'browser' | 'json';
14
+ diagnostics?: boolean;
15
+ verbose?: boolean;
16
+ ci?: {
17
+ threshold?: number;
18
+ };
19
+ }
20
+ export interface ResolvedPathgradeConfig {
21
+ runner: {
22
+ adapter: string;
23
+ args: string[];
24
+ };
25
+ evals: {
26
+ include: string[];
27
+ exclude: string[];
28
+ };
29
+ affected: {
30
+ global: string[];
31
+ };
32
+ reporter?: 'cli' | 'browser' | 'json';
33
+ diagnostics: boolean;
34
+ verbose: boolean;
35
+ ci: {
36
+ threshold?: number;
37
+ };
38
+ }
39
+ export declare const DEFAULT_EVAL_INCLUDE: string[];
40
+ export declare const DEFAULT_EVAL_EXCLUDE: string[];
41
+ export declare function defaultPathgradeConfig(): ResolvedPathgradeConfig;
42
+ export declare function resolvePathgradeConfig(input: {
43
+ cwd: string;
44
+ cli?: PathgradeConfig;
45
+ configPath?: string;
46
+ legacyVitestConfigPath?: string;
47
+ warn?: (message: string) => void;
48
+ }): Promise<ResolvedPathgradeConfig>;
@@ -0,0 +1,204 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { createJiti } from 'jiti';
4
+ export const DEFAULT_EVAL_INCLUDE = ['**/*.eval.ts'];
5
+ export const DEFAULT_EVAL_EXCLUDE = [
6
+ '**/node_modules/**',
7
+ '**/.git/**',
8
+ '.worktrees/**',
9
+ 'worktrees/**',
10
+ '**/fixtures/**',
11
+ ];
12
+ const PATHGRADE_CONFIG_CANDIDATES = [
13
+ 'pathgrade.config.ts',
14
+ 'pathgrade.config.mts',
15
+ 'pathgrade.config.js',
16
+ 'pathgrade.config.mjs',
17
+ ];
18
+ export function defaultPathgradeConfig() {
19
+ return {
20
+ runner: {
21
+ adapter: 'vitest',
22
+ args: [],
23
+ },
24
+ evals: {
25
+ include: [...DEFAULT_EVAL_INCLUDE],
26
+ exclude: [...DEFAULT_EVAL_EXCLUDE],
27
+ },
28
+ affected: {
29
+ global: [],
30
+ },
31
+ diagnostics: false,
32
+ verbose: false,
33
+ ci: {},
34
+ };
35
+ }
36
+ export async function resolvePathgradeConfig(input) {
37
+ const fileConfig = await loadPathgradeConfigFile(input.cwd, input.configPath);
38
+ const legacyConfig = await loadLegacyVitestConfig(input.cwd, input.legacyVitestConfigPath, input.warn);
39
+ return mergePathgradeConfig(mergePathgradeConfig(mergePathgradeConfig(defaultPathgradeConfig(), legacyConfig), fileConfig), input.cli);
40
+ }
41
+ function mergePathgradeConfig(base, override) {
42
+ if (!override)
43
+ return base;
44
+ return {
45
+ runner: {
46
+ adapter: override.runner?.adapter ?? base.runner.adapter,
47
+ args: override.runner?.args ?? base.runner.args,
48
+ },
49
+ evals: {
50
+ include: override.evals?.include ?? base.evals.include,
51
+ exclude: override.evals?.exclude ?? base.evals.exclude,
52
+ },
53
+ affected: {
54
+ global: override.affected?.global ?? base.affected.global,
55
+ },
56
+ reporter: override.reporter ?? base.reporter,
57
+ diagnostics: override.diagnostics ?? base.diagnostics,
58
+ verbose: override.verbose ?? base.verbose,
59
+ ci: {
60
+ threshold: override.ci?.threshold ?? base.ci.threshold,
61
+ },
62
+ };
63
+ }
64
+ async function loadPathgradeConfigFile(cwd, configPath) {
65
+ const resolved = configPath
66
+ ? path.resolve(cwd, configPath)
67
+ : PATHGRADE_CONFIG_CANDIDATES
68
+ .map(candidate => path.join(cwd, candidate))
69
+ .find(candidate => fs.existsSync(candidate));
70
+ if (!resolved)
71
+ return undefined;
72
+ try {
73
+ const jiti = createJiti(cwd, { interopDefault: true });
74
+ const loaded = await jiti.import(resolved, { default: true });
75
+ return validatePathgradeConfig(loaded, path.relative(cwd, resolved));
76
+ }
77
+ catch (err) {
78
+ if (err instanceof InvalidPathgradeConfigError) {
79
+ throw err;
80
+ }
81
+ throw new Error(`pathgrade: failed to load ${path.relative(cwd, resolved)}: ${errMsg(err)}`);
82
+ }
83
+ }
84
+ function isObject(value) {
85
+ return value !== null && typeof value === 'object';
86
+ }
87
+ class InvalidPathgradeConfigError extends Error {
88
+ }
89
+ function validatePathgradeConfig(value, label) {
90
+ if (!isObject(value)) {
91
+ throw invalidConfig(label, 'default export must be an object');
92
+ }
93
+ validateOptionalObject(value.runner, label, 'runner');
94
+ const runner = asOptionalObject(value.runner);
95
+ validateOptionalString(runner?.adapter, label, 'runner.adapter');
96
+ validateOptionalStringArray(runner?.args, label, 'runner.args');
97
+ validateOptionalObject(value.evals, label, 'evals');
98
+ const evals = asOptionalObject(value.evals);
99
+ validateOptionalStringArray(evals?.include, label, 'evals.include');
100
+ validateOptionalStringArray(evals?.exclude, label, 'evals.exclude');
101
+ validateOptionalObject(value.affected, label, 'affected');
102
+ const affected = asOptionalObject(value.affected);
103
+ validateOptionalStringArray(affected?.global, label, 'affected.global');
104
+ if (value.reporter !== undefined && !['cli', 'browser', 'json'].includes(String(value.reporter))) {
105
+ throw invalidConfig(label, 'reporter must be one of cli, browser, or json');
106
+ }
107
+ validateOptionalBoolean(value.diagnostics, label, 'diagnostics');
108
+ validateOptionalBoolean(value.verbose, label, 'verbose');
109
+ validateOptionalObject(value.ci, label, 'ci');
110
+ const ci = asOptionalObject(value.ci);
111
+ validateOptionalNumber(ci?.threshold, label, 'ci.threshold');
112
+ return value;
113
+ }
114
+ function asOptionalObject(value) {
115
+ return isObject(value) ? value : undefined;
116
+ }
117
+ function validateOptionalObject(value, label, field) {
118
+ if (value !== undefined && !isObject(value)) {
119
+ throw invalidConfig(label, `${field} must be an object`);
120
+ }
121
+ }
122
+ function validateOptionalString(value, label, field) {
123
+ if (value !== undefined && typeof value !== 'string') {
124
+ throw invalidConfig(label, `${field} must be a string`);
125
+ }
126
+ }
127
+ function validateOptionalStringArray(value, label, field) {
128
+ if (value !== undefined && (!Array.isArray(value) || value.some(item => typeof item !== 'string'))) {
129
+ throw invalidConfig(label, `${field} must be an array of strings`);
130
+ }
131
+ }
132
+ function validateOptionalBoolean(value, label, field) {
133
+ if (value !== undefined && typeof value !== 'boolean') {
134
+ throw invalidConfig(label, `${field} must be a boolean`);
135
+ }
136
+ }
137
+ function validateOptionalNumber(value, label, field) {
138
+ if (value !== undefined && typeof value !== 'number') {
139
+ throw invalidConfig(label, `${field} must be a number`);
140
+ }
141
+ }
142
+ function invalidConfig(label, reason) {
143
+ return new InvalidPathgradeConfigError(`pathgrade: invalid ${label}: ${reason}`);
144
+ }
145
+ function errMsg(err) {
146
+ return err instanceof Error ? err.message : String(err);
147
+ }
148
+ const VITEST_CONFIG_CANDIDATES = [
149
+ 'vitest.config.ts',
150
+ 'vitest.config.mts',
151
+ 'vitest.config.js',
152
+ 'vitest.config.mjs',
153
+ ];
154
+ async function loadLegacyVitestConfig(cwd, configPath, warn = () => { }) {
155
+ const resolved = configPath
156
+ ? path.resolve(cwd, configPath)
157
+ : VITEST_CONFIG_CANDIDATES
158
+ .map(candidate => path.join(cwd, candidate))
159
+ .find(candidate => fs.existsSync(candidate));
160
+ if (!resolved)
161
+ return undefined;
162
+ let loaded;
163
+ try {
164
+ const jiti = createJiti(cwd, { interopDefault: true });
165
+ loaded = await jiti.import(resolved, { default: true });
166
+ }
167
+ catch (err) {
168
+ const message = `pathgrade: failed to load ${path.relative(cwd, resolved)}: ${errMsg(err)}`;
169
+ if (configPath)
170
+ throw new Error(message);
171
+ warn(message);
172
+ return undefined;
173
+ }
174
+ const plugins = findPluginsList(loaded);
175
+ const pathgradePlugin = plugins?.find(plugin => isPathgradePlugin(plugin));
176
+ const opts = isObject(pathgradePlugin)
177
+ ? pathgradePlugin.__pathgradeOptions
178
+ : undefined;
179
+ if (!isObject(opts))
180
+ return undefined;
181
+ return {
182
+ evals: {
183
+ ...(Array.isArray(opts.include) ? { include: opts.include } : {}),
184
+ ...(Array.isArray(opts.exclude) ? { exclude: opts.exclude } : {}),
185
+ },
186
+ affected: {
187
+ global: isObject(opts.affected) && Array.isArray(opts.affected.global)
188
+ ? opts.affected.global
189
+ : [],
190
+ },
191
+ };
192
+ }
193
+ function findPluginsList(config) {
194
+ if (!isObject(config))
195
+ return null;
196
+ if (Array.isArray(config.plugins))
197
+ return config.plugins;
198
+ if (isObject(config.test) && Array.isArray(config.test.plugins))
199
+ return config.test.plugins;
200
+ return null;
201
+ }
202
+ function isPathgradePlugin(plugin) {
203
+ return isObject(plugin) && (plugin.name === 'pathgrade' || '__pathgradeOptions' in plugin);
204
+ }
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * pathgrade CLI — thin wrapper around vitest
3
+ * pathgrade CLI — runner-neutral wrapper around Pathgrade adapters
4
4
  *
5
5
  * Usage:
6
- * pathgrade run [-- vitest-args] Run evals via vitest (loads .env, validates API keys)
6
+ * pathgrade run [-- runner-args] Run evals via the selected adapter
7
7
  * pathgrade init [--force] Generate eval scaffolding
8
8
  * pathgrade preview [browser] View results (CLI default, or browser)
9
9
  */
package/dist/pathgrade.js CHANGED
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * pathgrade CLI — thin wrapper around vitest
3
+ * pathgrade CLI — runner-neutral wrapper around Pathgrade adapters
4
4
  *
5
5
  * Usage:
6
- * pathgrade run [-- vitest-args] Run evals via vitest (loads .env, validates API keys)
6
+ * pathgrade run [-- runner-args] Run evals via the selected adapter
7
7
  * pathgrade init [--force] Generate eval scaffolding
8
8
  * pathgrade preview [browser] View results (CLI default, or browser)
9
9
  */
10
10
  import * as fs from 'fs';
11
11
  import * as path from 'path';
12
- import { spawn } from 'child_process';
13
12
  import { runInit } from './commands/init.js';
14
13
  import { runAnalyze } from './commands/analyze.js';
15
14
  import { runValidate, runValidateAffected } from './commands/validate.js';
@@ -20,6 +19,8 @@ import { runReport } from './commands/report.js';
20
19
  import { runAffected } from './commands/affected.js';
21
20
  import { runChanged } from './commands/run-changed.js';
22
21
  import { clearSidecar } from './affected/sidecar.js';
22
+ import { resolvePathgradeConfig } from './config/pathgrade.js';
23
+ import { loadRunnerInvocationAdapter } from './runners/adapter-loader.js';
23
24
  import { fmt } from './utils/cli.js';
24
25
  import { shutdown } from './utils/shutdown.js';
25
26
  function loadDotenv() {
@@ -52,7 +53,7 @@ function validateApiKeys() {
52
53
  const hasClaude = !!process.env.HOME; // Claude CLI uses OS keychain, just check it exists
53
54
  if (!hasAnthropic && !hasOpenAI) {
54
55
  console.log(`\n ${fmt.dim('warning:')} No API keys found. Set ANTHROPIC_API_KEY or OPENAI_API_KEY in .env or environment.\n` +
55
- ` ${fmt.dim(' Claude CLI auth (keychain) and Codex cached login (~/.codex/auth.json) may still work if installed.')}\n`);
56
+ ` ${fmt.dim(' Claude CLI auth (keychain) and Codex exec cached login (~/.codex/auth.json) may still work if installed.')}\n`);
56
57
  }
57
58
  }
58
59
  async function main() {
@@ -144,7 +145,7 @@ async function main() {
144
145
  return;
145
146
  }
146
147
  if (command === 'run' || !command || command.startsWith('-')) {
147
- // pathgrade run [--changed [--since=…|--changed-files=…]] [--] [vitest-args]
148
+ // pathgrade run [--changed [--since=…|--changed-files=…]] [--] [runner-args]
148
149
  loadDotenv();
149
150
  validateApiKeys();
150
151
  const parsed = parsePathgradeRunArgs(command === 'run' ? args.slice(1) : args);
@@ -155,18 +156,6 @@ async function main() {
155
156
  const exitCode = await runChanged({
156
157
  cwd: process.cwd(),
157
158
  parsed,
158
- spawnVitest: ({ argv }) => new Promise(resolve => {
159
- const child = spawn('npx', ['vitest', ...argv], {
160
- stdio: 'inherit',
161
- env: {
162
- ...process.env,
163
- ...(parsed.forceDiagnostics ? { PATHGRADE_DIAGNOSTICS: '1' } : {}),
164
- ...(parsed.forceVerbose ? { PATHGRADE_VERBOSE: '1' } : {}),
165
- },
166
- shell: true,
167
- });
168
- child.on('close', code => resolve(code ?? 0));
169
- }),
170
159
  });
171
160
  process.exitCode = exitCode;
172
161
  return;
@@ -175,18 +164,28 @@ async function main() {
175
164
  // previous `--changed` run so it doesn't leak into this full-suite
176
165
  // run (the reporter would otherwise merge old metadata).
177
166
  await clearSidecar(process.cwd());
178
- const child = spawn('npx', ['vitest', 'run', ...parsed.vitestArgs], {
179
- stdio: 'inherit',
180
- env: {
181
- ...process.env,
182
- ...(parsed.forceDiagnostics ? { PATHGRADE_DIAGNOSTICS: '1' } : {}),
183
- ...(parsed.forceVerbose ? { PATHGRADE_VERBOSE: '1' } : {}),
184
- },
185
- shell: true,
186
- });
187
- child.on('close', (code) => {
188
- process.exitCode = code ?? 0;
189
- });
167
+ const env = {
168
+ ...process.env,
169
+ ...(parsed.forceDiagnostics ? { PATHGRADE_DIAGNOSTICS: '1' } : {}),
170
+ ...(parsed.forceVerbose ? { PATHGRADE_VERBOSE: '1' } : {}),
171
+ };
172
+ try {
173
+ const config = await resolvePathgradeConfig({ cwd: process.cwd() });
174
+ const runner = await loadRunnerInvocationAdapter({
175
+ adapterName: parsed.adapterName ?? config.runner.adapter,
176
+ cwd: process.cwd(),
177
+ config,
178
+ });
179
+ process.exitCode = await runner.run({
180
+ cwd: process.cwd(),
181
+ runnerArgs: [...config.runner.args, ...parsed.runnerArgs],
182
+ env,
183
+ });
184
+ }
185
+ catch (err) {
186
+ console.error(err instanceof Error ? err.message : String(err));
187
+ process.exitCode = 1;
188
+ }
190
189
  return;
191
190
  }
192
191
  console.error(`Unknown command: ${command}`);
@@ -195,13 +194,15 @@ async function main() {
195
194
  }
196
195
  function printHelp() {
197
196
  console.log(`
198
- pathgrade - Evaluate AI agent skills with vitest
197
+ pathgrade - Evaluate AI agent skills with a runner adapter
199
198
 
200
199
  Usage:
201
- pathgrade run [-- vitest-args] Run evals (loads .env, delegates to vitest)
200
+ pathgrade run [-- runner-args] Run evals (loads .env, delegates to the selected adapter)
202
201
  [--changed] Run only evals affected by the current PR/change-set
203
202
  [--since=<ref>] Override base ref (implies git mode)
204
203
  [--changed-files=<path>] Use an explicit newline-delimited file list
204
+ [--adapter=<name|path>] Select built-in or third-party runner adapter
205
+ [--diagnostics] Print full diagnostics for passing evals too
205
206
  [--quiet] Suppress the run-start summary
206
207
  [--verbose|-v] Stream live per-turn events to stderr during the run
207
208
  pathgrade init [--force] Generate eval scaffolding
@@ -223,9 +224,9 @@ function printHelp() {
223
224
  [--json] Emit structured JSON (snake_case) to stdout
224
225
 
225
226
  Environment:
226
- PATHGRADE_AGENT=codex Override agent for all trials
227
- ANTHROPIC_API_KEY API key for Claude
228
- OPENAI_API_KEY API key for Codex (optional if Codex CLI is already logged in)
227
+ PATHGRADE_AGENT=codex Fallback agent when createAgent({ agent }) is omitted
228
+ ANTHROPIC_API_KEY API key for Claude and Anthropic-backed judges
229
+ OPENAI_API_KEY API key for Codex and OpenAI-backed judges
229
230
 
230
231
  Examples:
231
232
  pathgrade run # run all *.eval.ts files
@@ -1,9 +1,2 @@
1
- import type { PathgradePluginOptions } from '../sdk/types.js';
2
- export type { PathgradePluginOptions };
3
- /**
4
- * Vitest plugin for pathgrade evaluations.
5
- *
6
- * Configures test file patterns, timeout, auto-dispose lifecycle hooks,
7
- * and the pathgrade reporter for aggregate statistics.
8
- */
9
- export declare function pathgrade(opts?: PathgradePluginOptions): any;
1
+ export { pathgrade } from '../adapters/vitest/index.js';
2
+ export type { PathgradePluginOptions } from '../adapters/vitest/index.js';
@@ -1,81 +1 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import { configDefaults } from 'vitest/config';
4
- import { PathgradeReporter } from './reporter.js';
5
- import { discoverPathgradeEvalFiles } from '../evals/discovery.js';
6
- const DEFAULT_EXCLUDE = [...configDefaults.exclude, '.worktrees/**', 'worktrees/**'];
7
- function resolveSetupFile() {
8
- // Prefer .ts (running from source via vitest) over .js (built dist)
9
- const tsPath = path.resolve(import.meta.dirname, 'setup.ts');
10
- if (fs.existsSync(tsPath))
11
- return tsPath;
12
- return path.resolve(import.meta.dirname, 'setup.js');
13
- }
14
- /**
15
- * Vitest plugin for pathgrade evaluations.
16
- *
17
- * Configures test file patterns, timeout, auto-dispose lifecycle hooks,
18
- * and the pathgrade reporter for aggregate statistics.
19
- */
20
- export function pathgrade(opts) {
21
- const timeoutSec = opts?.timeout ?? 300;
22
- // Verbose live-streaming seam: the SDK (createAgent) reads
23
- // `process.env.PATHGRADE_VERBOSE` to decide whether to emit. Wiring the
24
- // plugin option through the env var keeps a single source of truth and
25
- // preserves CLI/env precedence — we only set the env if the user hasn't
26
- // already set it via their shell or the `pathgrade run --verbose` CLI.
27
- if (opts?.verbose === true && process.env.PATHGRADE_VERBOSE === undefined) {
28
- process.env.PATHGRADE_VERBOSE = '1';
29
- }
30
- return {
31
- name: 'pathgrade',
32
- // Surface the user's options to pre-vitest CLI commands
33
- // (`pathgrade affected`, `pathgrade run --changed`) so they can
34
- // read `affected.global` after loading vitest.config.ts.
35
- // See `src/affected/config.ts`.
36
- __pathgradeOptions: opts ?? {},
37
- config() {
38
- return {
39
- resolve: {
40
- alias: resolveLocalAliases(),
41
- },
42
- test: {
43
- include: opts?.include ?? ['**/*.eval.ts'],
44
- exclude: opts?.exclude ?? DEFAULT_EXCLUDE,
45
- testTimeout: (timeoutSec + 30) * 1000,
46
- setupFiles: [resolveSetupFile()],
47
- reporters: [
48
- 'default',
49
- new PathgradeReporter(opts),
50
- ],
51
- },
52
- };
53
- },
54
- configResolved(resolved) {
55
- const root = resolved.root ?? process.cwd();
56
- const include = resolved.test?.include ?? opts?.include ?? ['**/*.eval.ts'];
57
- const exclude = resolved.test?.exclude ?? opts?.exclude ?? DEFAULT_EXCLUDE;
58
- const discovered = discoverPathgradeEvalFiles({ cwd: root, include, exclude });
59
- if (!resolved.test) {
60
- resolved.test = {};
61
- }
62
- resolved.test.include = discovered;
63
- },
64
- };
65
- }
66
- /**
67
- * When running from source (not installed via npm), resolve
68
- * `@wix/pathgrade` imports to the local src/ directory so examples
69
- * can use canonical package imports while developing locally.
70
- */
71
- function resolveLocalAliases() {
72
- const sdkSource = path.resolve(import.meta.dirname, '..', 'sdk', 'index.ts');
73
- if (!fs.existsSync(sdkSource))
74
- return undefined;
75
- const src = path.resolve(import.meta.dirname, '..');
76
- return {
77
- '@wix/pathgrade/mcp-mock': path.join(src, 'core', 'mcp-mock.ts'),
78
- '@wix/pathgrade/plugin': path.join(src, 'plugin', 'index.ts'),
79
- '@wix/pathgrade': path.join(src, 'sdk', 'index.ts'),
80
- };
81
- }
1
+ export { pathgrade } from '../adapters/vitest/index.js';
@@ -1,45 +1 @@
1
- import type { Agent, PathgradeTestMeta, RecordedEvalResult } from '../sdk/types.js';
2
- declare module 'vitest' {
3
- interface TaskMeta {
4
- pathgrade?: PathgradeTestMeta[];
5
- }
6
- }
7
- type AfterEachFn = (fn: (ctx: {
8
- task: {
9
- id: string;
10
- meta: Record<string, unknown>;
11
- };
12
- }) => Promise<void>) => void;
13
- type AfterAllFn = (fn: () => Promise<void>) => void;
14
- declare function trackAgent(agent: Agent): void;
15
- declare function untrackAgent(agent: Agent): void;
16
- /**
17
- * Flush results and dispose agents for the current test. Agents created in
18
- * this test's scope are disposed after flushing. Shared agents (created in a
19
- * different scope, e.g. module-level or beforeAll) have their pending results
20
- * flushed but are kept alive for subsequent tests.
21
- */
22
- declare function flush(task: {
23
- id: string;
24
- meta: Pick<import('vitest').TaskMeta, 'pathgrade'>;
25
- }): Promise<void>;
26
- declare function onResult(result: RecordedEvalResult, agent: Agent): void;
27
- /**
28
- * Dispose any agents still pending at end-of-file. Shared agents (created at
29
- * module scope or in beforeAll) have a stored task id that won't match any
30
- * per-test id in afterEach, so afterAll is their only disposal opportunity.
31
- * Without this, sandboxes leak and `debug: true` folders never get written.
32
- */
33
- declare function flushAll(): Promise<void>;
34
- declare function reset(): void;
35
- declare function install(afterEach: AfterEachFn, afterAll?: AfterAllFn): void;
36
- export declare const lifecycle: {
37
- trackAgent: typeof trackAgent;
38
- untrackAgent: typeof untrackAgent;
39
- flush: typeof flush;
40
- flushAll: typeof flushAll;
41
- onResult: typeof onResult;
42
- reset: typeof reset;
43
- install: typeof install;
44
- };
45
- export {};
1
+ export { lifecycle } from '../adapters/vitest/lifecycle.js';