@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.
- package/README.md +116 -32
- package/dist/adapter-kit/index.d.ts +17 -0
- package/dist/adapter-kit/index.js +14 -0
- package/dist/adapters/jest/index.d.ts +3 -0
- package/dist/adapters/jest/index.js +3 -0
- package/dist/adapters/jest/invocation-adapter.d.ts +15 -0
- package/dist/adapters/jest/invocation-adapter.js +115 -0
- package/dist/adapters/jest/lifecycle.d.ts +21 -0
- package/dist/adapters/jest/lifecycle.js +62 -0
- package/dist/adapters/jest/metadata.d.ts +6 -0
- package/dist/adapters/jest/metadata.js +35 -0
- package/dist/adapters/jest/reporter.cjs +10 -0
- package/dist/adapters/jest/reporter.d.ts +5 -0
- package/dist/adapters/jest/reporter.js +67 -0
- package/dist/adapters/jest/results.d.ts +31 -0
- package/dist/adapters/jest/results.js +144 -0
- package/dist/adapters/jest/runner-adapter.d.ts +9 -0
- package/dist/adapters/jest/runner-adapter.js +49 -0
- package/dist/adapters/jest/setup.d.ts +1 -0
- package/dist/adapters/jest/setup.js +10 -0
- package/dist/adapters/node-test/index.d.ts +8 -0
- package/dist/adapters/node-test/index.js +106 -0
- package/dist/adapters/node-test/invocation-adapter.d.ts +5 -0
- package/dist/adapters/node-test/invocation-adapter.js +47 -0
- package/dist/adapters/node-test/runner-adapter.d.ts +2 -0
- package/dist/adapters/node-test/runner-adapter.js +116 -0
- package/dist/adapters/vitest/index.d.ts +9 -0
- package/dist/adapters/vitest/index.js +82 -0
- package/dist/adapters/vitest/lifecycle.d.ts +24 -0
- package/dist/adapters/vitest/lifecycle.js +41 -0
- package/dist/adapters/vitest/reporter.d.ts +12 -0
- package/dist/adapters/vitest/reporter.js +75 -0
- package/dist/adapters/vitest/setup.d.ts +1 -0
- package/dist/adapters/vitest/setup.js +3 -0
- package/dist/agents/claude/sdk-options.js +6 -0
- package/dist/agents/codex-app-server/agent.js +5 -1
- package/dist/agents/codex-app-server/protocol/index.js +3 -3
- package/dist/agents/codex-app-server/turn-completion.d.ts +1 -0
- package/dist/agents/codex-app-server/turn-completion.js +9 -0
- package/dist/commands/affected.js +22 -15
- package/dist/commands/run-args.d.ts +4 -3
- package/dist/commands/run-args.js +12 -6
- package/dist/commands/run-changed.d.ts +4 -4
- package/dist/commands/run-changed.js +45 -58
- package/dist/config/pathgrade.d.ts +48 -0
- package/dist/config/pathgrade.js +204 -0
- package/dist/pathgrade.d.ts +2 -2
- package/dist/pathgrade.js +35 -34
- package/dist/plugin/index.d.ts +2 -9
- package/dist/plugin/index.js +1 -81
- package/dist/plugin/lifecycle.d.ts +1 -45
- package/dist/plugin/lifecycle.js +1 -146
- package/dist/plugin/reporter.d.ts +1 -37
- package/dist/plugin/reporter.js +1 -280
- package/dist/plugin/setup.d.ts +1 -1
- package/dist/plugin/setup.js +1 -3
- package/dist/providers/copy-filter.d.ts +5 -0
- package/dist/providers/copy-filter.js +9 -0
- package/dist/providers/credentials.js +1 -1
- package/dist/providers/sandbox-lifecycle.d.ts +22 -0
- package/dist/providers/sandbox-lifecycle.js +133 -0
- package/dist/providers/sandbox.js +4 -3
- package/dist/providers/workspace.js +4 -14
- package/dist/reporters/diagnostics.d.ts +2 -40
- package/dist/reporters/diagnostics.js +1 -80
- package/dist/reporters/report-summary.d.ts +6 -0
- package/dist/reporters/report-summary.js +29 -0
- package/dist/reporting/artifacts.d.ts +2 -0
- package/dist/reporting/artifacts.js +20 -0
- package/dist/reporting/core.d.ts +2 -0
- package/dist/reporting/core.js +173 -0
- package/dist/reporting/types.d.ts +57 -0
- package/dist/reporting/types.js +1 -0
- package/dist/reporting/vitest-edge.d.ts +1 -0
- package/dist/reporting/vitest-edge.js +1 -0
- package/dist/runners/adapter-loader.d.ts +17 -0
- package/dist/runners/adapter-loader.js +67 -0
- package/dist/runners/adapter.d.ts +65 -0
- package/dist/runners/adapter.js +1 -0
- package/dist/runners/invocation.d.ts +10 -0
- package/dist/runners/invocation.js +1 -0
- package/dist/runners/lifecycle-hooks.d.ts +2 -0
- package/dist/runners/lifecycle-hooks.js +18 -0
- package/dist/runners/model-builders.d.ts +4 -0
- package/dist/runners/model-builders.js +67 -0
- package/dist/runners/model-validation.d.ts +12 -0
- package/dist/runners/model-validation.js +213 -0
- package/dist/runners/model.d.ts +117 -0
- package/dist/runners/model.js +1 -0
- package/dist/runners/orchestrator.d.ts +30 -0
- package/dist/runners/orchestrator.js +74 -0
- package/dist/runners/report-projection.d.ts +6 -0
- package/dist/runners/report-projection.js +62 -0
- package/dist/runners/selection.d.ts +6 -0
- package/dist/runners/selection.js +10 -0
- package/dist/runners/vitest-adapter.d.ts +8 -0
- package/dist/runners/vitest-adapter.js +108 -0
- package/dist/runners/vitest-invocation.d.ts +10 -0
- package/dist/runners/vitest-invocation.js +44 -0
- package/dist/runners/vitest-lifecycle.d.ts +43 -0
- package/dist/runners/vitest-lifecycle.js +150 -0
- package/dist/sdk/agent.js +15 -16
- package/dist/sdk/case-context.d.ts +20 -0
- package/dist/sdk/case-context.js +31 -0
- package/dist/sdk/diagnostics.d.ts +40 -0
- package/dist/sdk/diagnostics.js +80 -0
- package/dist/sdk/eval-runtime.d.ts +4 -0
- package/dist/sdk/eval-runtime.js +11 -1
- package/dist/sdk/evaluate.js +2 -1
- package/dist/sdk/index.d.ts +2 -0
- package/dist/sdk/index.js +1 -0
- package/dist/sdk/lifecycle.d.ts +40 -0
- package/dist/sdk/lifecycle.js +170 -0
- package/dist/sdk/result-capture.d.ts +18 -0
- package/dist/sdk/result-capture.js +49 -0
- package/dist/sdk/types.d.ts +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/utils/llm-providers/anthropic.js +9 -2
- package/package.json +56 -4
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { PathgradeReporter } from './reporter.js';
|
|
4
|
+
import { discoverPathgradeEvalFiles } from '../../evals/discovery.js';
|
|
5
|
+
import { DEFAULT_EVAL_EXCLUDE, DEFAULT_EVAL_INCLUDE } from '../../config/pathgrade.js';
|
|
6
|
+
function resolveSetupFile() {
|
|
7
|
+
// Prefer .ts (running from source via vitest) over .js (built dist)
|
|
8
|
+
const tsPath = path.resolve(import.meta.dirname, 'setup.ts');
|
|
9
|
+
if (fs.existsSync(tsPath))
|
|
10
|
+
return tsPath;
|
|
11
|
+
return path.resolve(import.meta.dirname, 'setup.js');
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Vitest plugin for pathgrade evaluations.
|
|
15
|
+
*
|
|
16
|
+
* Configures test file patterns, timeout, auto-dispose lifecycle hooks,
|
|
17
|
+
* and the pathgrade reporter for aggregate statistics.
|
|
18
|
+
*/
|
|
19
|
+
export function pathgrade(opts) {
|
|
20
|
+
const timeoutSec = opts?.timeout ?? 300;
|
|
21
|
+
// Verbose live-streaming seam: the SDK (createAgent) reads
|
|
22
|
+
// `process.env.PATHGRADE_VERBOSE` to decide whether to emit. Wiring the
|
|
23
|
+
// plugin option through the env var keeps a single source of truth and
|
|
24
|
+
// preserves CLI/env precedence — we only set the env if the user hasn't
|
|
25
|
+
// already set it via their shell or the `pathgrade run --verbose` CLI.
|
|
26
|
+
if (opts?.verbose === true && process.env.PATHGRADE_VERBOSE === undefined) {
|
|
27
|
+
process.env.PATHGRADE_VERBOSE = '1';
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
name: 'pathgrade',
|
|
31
|
+
// Surface the user's options to pre-vitest CLI commands
|
|
32
|
+
// (`pathgrade affected`, `pathgrade run --changed`) so they can
|
|
33
|
+
// read `affected.global` after loading vitest.config.ts.
|
|
34
|
+
// See `src/affected/config.ts`.
|
|
35
|
+
__pathgradeOptions: opts ?? {},
|
|
36
|
+
config() {
|
|
37
|
+
return {
|
|
38
|
+
resolve: {
|
|
39
|
+
alias: resolveLocalAliases(),
|
|
40
|
+
},
|
|
41
|
+
test: {
|
|
42
|
+
include: opts?.include ?? DEFAULT_EVAL_INCLUDE,
|
|
43
|
+
exclude: opts?.exclude ?? DEFAULT_EVAL_EXCLUDE,
|
|
44
|
+
testTimeout: (timeoutSec + 30) * 1000,
|
|
45
|
+
setupFiles: [resolveSetupFile()],
|
|
46
|
+
reporters: [
|
|
47
|
+
'default',
|
|
48
|
+
new PathgradeReporter(opts),
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
configResolved(resolved) {
|
|
54
|
+
const root = resolved.root ?? process.cwd();
|
|
55
|
+
const include = resolved.test?.include ?? opts?.include ?? DEFAULT_EVAL_INCLUDE;
|
|
56
|
+
const exclude = resolved.test?.exclude ?? opts?.exclude ?? DEFAULT_EVAL_EXCLUDE;
|
|
57
|
+
const discovered = discoverPathgradeEvalFiles({ cwd: root, include, exclude });
|
|
58
|
+
if (!resolved.test) {
|
|
59
|
+
resolved.test = {};
|
|
60
|
+
}
|
|
61
|
+
resolved.test.include = discovered;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* When running from source (not installed via npm), resolve
|
|
67
|
+
* `@wix/pathgrade` imports to the local src/ directory so examples
|
|
68
|
+
* can use canonical package imports while developing locally.
|
|
69
|
+
*/
|
|
70
|
+
function resolveLocalAliases() {
|
|
71
|
+
const sdkSource = path.resolve(import.meta.dirname, '..', '..', 'sdk', 'index.ts');
|
|
72
|
+
if (!fs.existsSync(sdkSource))
|
|
73
|
+
return undefined;
|
|
74
|
+
const src = path.resolve(import.meta.dirname, '..', '..');
|
|
75
|
+
return {
|
|
76
|
+
'@wix/pathgrade/mcp-mock': path.join(src, 'core', 'mcp-mock.ts'),
|
|
77
|
+
'@wix/pathgrade/plugin': path.join(src, 'plugin', 'index.ts'),
|
|
78
|
+
'@wix/pathgrade/plugin/vitest': path.join(src, 'adapters', 'vitest', 'index.ts'),
|
|
79
|
+
'@wix/pathgrade/adapters/vitest': path.join(src, 'adapters', 'vitest', 'index.ts'),
|
|
80
|
+
'@wix/pathgrade': path.join(src, 'sdk', 'index.ts'),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Agent, RecordedEvalResult } from '../../sdk/types.js';
|
|
2
|
+
import { type VitestAfterAllFn, type VitestAfterEachFn, type VitestAroundEachFn } from '../../runners/vitest-lifecycle.js';
|
|
3
|
+
declare function trackAgent(agent: Agent): void;
|
|
4
|
+
declare function untrackAgent(agent: Agent): void;
|
|
5
|
+
declare function releaseAgent(agent: Agent): void;
|
|
6
|
+
declare function flush(task: {
|
|
7
|
+
id: string;
|
|
8
|
+
meta: Record<string, unknown>;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
declare function onResult(result: RecordedEvalResult, agent: Agent): void;
|
|
11
|
+
declare function flushAll(): Promise<void>;
|
|
12
|
+
declare function reset(): void;
|
|
13
|
+
declare function install(afterEach: VitestAfterEachFn, afterAll?: VitestAfterAllFn, aroundEach?: VitestAroundEachFn): void;
|
|
14
|
+
export declare const lifecycle: {
|
|
15
|
+
trackAgent: typeof trackAgent;
|
|
16
|
+
untrackAgent: typeof untrackAgent;
|
|
17
|
+
releaseAgent: typeof releaseAgent;
|
|
18
|
+
flush: typeof flush;
|
|
19
|
+
flushAll: typeof flushAll;
|
|
20
|
+
onResult: typeof onResult;
|
|
21
|
+
reset: typeof reset;
|
|
22
|
+
install: typeof install;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createVitestLifecycleHooks, installVitestLifecycle, releaseVitestAgent, resetVitestLifecycle, trackVitestAgent, untrackVitestAgent, } from '../../runners/vitest-lifecycle.js';
|
|
2
|
+
let installHandle = null;
|
|
3
|
+
function trackAgent(agent) {
|
|
4
|
+
trackVitestAgent(agent, installHandle !== null);
|
|
5
|
+
}
|
|
6
|
+
function untrackAgent(agent) {
|
|
7
|
+
untrackVitestAgent(agent);
|
|
8
|
+
}
|
|
9
|
+
function releaseAgent(agent) {
|
|
10
|
+
releaseVitestAgent(agent);
|
|
11
|
+
}
|
|
12
|
+
async function flush(task) {
|
|
13
|
+
const results = await createVitestLifecycleHooks().flushCase(task.id);
|
|
14
|
+
if (results.length > 0) {
|
|
15
|
+
task.meta.pathgrade = results;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function onResult(result, agent) {
|
|
19
|
+
createVitestLifecycleHooks().onResult({ result, agent });
|
|
20
|
+
}
|
|
21
|
+
async function flushAll() {
|
|
22
|
+
await createVitestLifecycleHooks().cleanupRun();
|
|
23
|
+
}
|
|
24
|
+
function reset() {
|
|
25
|
+
resetVitestLifecycle(installHandle);
|
|
26
|
+
installHandle = null;
|
|
27
|
+
}
|
|
28
|
+
function install(afterEach, afterAll, aroundEach) {
|
|
29
|
+
installHandle?.restore();
|
|
30
|
+
installHandle = installVitestLifecycle({ afterEach, afterAll, aroundEach });
|
|
31
|
+
}
|
|
32
|
+
export const lifecycle = {
|
|
33
|
+
trackAgent,
|
|
34
|
+
untrackAgent,
|
|
35
|
+
releaseAgent,
|
|
36
|
+
flush,
|
|
37
|
+
flushAll,
|
|
38
|
+
onResult,
|
|
39
|
+
reset,
|
|
40
|
+
install,
|
|
41
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Reporter, TestModule } from 'vitest/node';
|
|
2
|
+
import type { PathgradePluginOptions } from '../../sdk/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Custom vitest reporter that layers pathgrade aggregate statistics
|
|
5
|
+
* on top of vitest's default output.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PathgradeReporter implements Reporter {
|
|
8
|
+
private opts;
|
|
9
|
+
constructor(opts?: PathgradePluginOptions);
|
|
10
|
+
onTestRunEnd(testModules: ReadonlyArray<TestModule>): Promise<void>;
|
|
11
|
+
private openBrowserViewer;
|
|
12
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { fmt } from '../../utils/cli.js';
|
|
4
|
+
import { getPathgradeDir } from '../../reporters/results-path.js';
|
|
5
|
+
import { readSidecar } from '../../affected/sidecar.js';
|
|
6
|
+
import { printReportSummary } from '../../reporters/report-summary.js';
|
|
7
|
+
import { createVitestAdapter } from '../../runners/vitest-adapter.js';
|
|
8
|
+
import { runWithAdapter } from '../../runners/orchestrator.js';
|
|
9
|
+
import { resolvePathgradeConfig } from '../../config/pathgrade.js';
|
|
10
|
+
/**
|
|
11
|
+
* Custom vitest reporter that layers pathgrade aggregate statistics
|
|
12
|
+
* on top of vitest's default output.
|
|
13
|
+
*/
|
|
14
|
+
export class PathgradeReporter {
|
|
15
|
+
opts;
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
this.opts = opts ?? {};
|
|
18
|
+
}
|
|
19
|
+
async onTestRunEnd(testModules) {
|
|
20
|
+
const cwd = process.cwd();
|
|
21
|
+
const config = await resolvePathgradeConfig({ cwd });
|
|
22
|
+
const mode = this.opts.reporter ?? config.reporter ?? 'cli';
|
|
23
|
+
const outputDir = getPathgradeDir(cwd);
|
|
24
|
+
const adapter = createVitestAdapter({ testModules });
|
|
25
|
+
const exitCode = await runWithAdapter({
|
|
26
|
+
adapter,
|
|
27
|
+
options: {
|
|
28
|
+
cwd,
|
|
29
|
+
discovery: { cwd },
|
|
30
|
+
runnerArgs: [],
|
|
31
|
+
env: process.env,
|
|
32
|
+
artifactRoot: outputDir,
|
|
33
|
+
reporterMode: mode,
|
|
34
|
+
threshold: this.opts.ci?.threshold ?? config.ci.threshold,
|
|
35
|
+
writeEmptyReport: false,
|
|
36
|
+
warn: warning => console.warn(warning),
|
|
37
|
+
log: () => console.log(`\n ${fmt.dim('Results written to')} ${outputDir}\n`),
|
|
38
|
+
loadSelection: async () => (await readSidecar(cwd, msg => {
|
|
39
|
+
console.warn(`[pathgrade] ${msg}`);
|
|
40
|
+
})) ?? undefined,
|
|
41
|
+
printSummary: summaries => {
|
|
42
|
+
printReportSummary(summaries, {
|
|
43
|
+
forceVerbose: this.opts.diagnostics === true
|
|
44
|
+
|| config.diagnostics
|
|
45
|
+
|| process.env.PATHGRADE_DIAGNOSTICS === '1',
|
|
46
|
+
currentTimeoutMs: this.opts.timeout != null ? this.opts.timeout * 1000 : undefined,
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
openBrowser: () => this.openBrowserViewer(),
|
|
50
|
+
onThresholdFailure: ({ overallPassRate, threshold }) => {
|
|
51
|
+
const avg = overallPassRate;
|
|
52
|
+
const configuredThreshold = this.opts.ci?.threshold ?? threshold;
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
console.log(`\n ${fmt.fail('CI THRESHOLD FAILED')} avg score ${fmt.bold(avg.toFixed(3))} < threshold ${fmt.bold(String(configuredThreshold))}\n`);
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
if (exitCode !== 0 && (process.exitCode === undefined || process.exitCode === 0)) {
|
|
59
|
+
process.exitCode = exitCode;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
openBrowserViewer() {
|
|
63
|
+
const viewerPath = path.resolve(import.meta.dirname, '..', '..', 'viewer.html');
|
|
64
|
+
try {
|
|
65
|
+
const openCmd = process.platform === 'darwin' ? 'open'
|
|
66
|
+
: process.platform === 'win32' ? 'start'
|
|
67
|
+
: 'xdg-open';
|
|
68
|
+
execSync(`${openCmd} "${viewerPath}"`, { stdio: 'ignore' });
|
|
69
|
+
console.log(`\n ${fmt.dim('Opened viewer in browser')}\n`);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
console.log(`\n ${fmt.dim('Open manually:')} ${viewerPath}\n`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -78,6 +78,12 @@ export function buildClaudeSdkOptions(inputs) {
|
|
|
78
78
|
continue;
|
|
79
79
|
env[key] = value;
|
|
80
80
|
}
|
|
81
|
+
// Claude Code normally exposes claude.ai account-level MCP connectors to
|
|
82
|
+
// local OAuth sessions. Keep PathGrade trials isolated from those ambient
|
|
83
|
+
// connectors unless the caller explicitly opts in through their env.
|
|
84
|
+
if (useLocalOAuth && env.ENABLE_CLAUDEAI_MCP_SERVERS === undefined) {
|
|
85
|
+
env.ENABLE_CLAUDEAI_MCP_SERVERS = 'false';
|
|
86
|
+
}
|
|
81
87
|
if (!useLocalOAuth) {
|
|
82
88
|
env.CLAUDE_CONFIG_DIR = path.join(inputs.workspacePath, CLAUDE_CONFIG_SUBDIR);
|
|
83
89
|
}
|
|
@@ -2,11 +2,12 @@ import { BaseAgent, getRuntimeEnv, getWorkspacePath, } from '../../types.js';
|
|
|
2
2
|
import { mountMcpForCodexAppServer } from '../../providers/mcp-runtime-mounting.js';
|
|
3
3
|
import { assertMcpSecretReferencesReady } from '../../providers/mcp-config.js';
|
|
4
4
|
import { buildSummary, enrichSkillEvents, extractSkillNameFromPath, inferCodexExecAction, } from '../../tool-events.js';
|
|
5
|
-
import { requireAskBusForLiveBatches
|
|
5
|
+
import { requireAskBusForLiveBatches } from '../../sdk/ask-bus/bus.js';
|
|
6
6
|
import { toAskUserToolEvent } from '../../sdk/ask-bus/projection.js';
|
|
7
7
|
import { decideMcpToolCall, redactMcpSecrets, } from '../../sdk/mcp-safety.js';
|
|
8
8
|
import { spawnAppServerTransport, } from './transport.js';
|
|
9
9
|
import { normalizeUpstreamQuestion, toWireAnswerMap, } from './wire-translators.js';
|
|
10
|
+
import { extractTurnCompletionFailure } from './turn-completion.js';
|
|
10
11
|
const DEFAULT_MODEL = 'gpt-5.4';
|
|
11
12
|
const TURN_COMPLETED_METHOD = 'turn/completed';
|
|
12
13
|
function recordFromUnknown(value) {
|
|
@@ -308,6 +309,9 @@ export class CodexAppServerAgent extends BaseAgent {
|
|
|
308
309
|
settled = true;
|
|
309
310
|
off();
|
|
310
311
|
closeOff();
|
|
312
|
+
const failure = extractTurnCompletionFailure(n.params);
|
|
313
|
+
if (failure)
|
|
314
|
+
Object.assign(turn, { turnFailed: true, failureMessage: failure });
|
|
311
315
|
resolve();
|
|
312
316
|
}
|
|
313
317
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Curated re-exports for the pathgrade Codex app-server driver and fixture
|
|
2
|
-
// tests. Every re-exported symbol is either
|
|
2
|
+
// tests. Every re-exported symbol is either a curated upstream shape from
|
|
3
3
|
// openai/codex@rust-v0.124.0 (with an upstream-citation header in its file) or
|
|
4
|
-
// a pathgrade-local composition (ClientRequest, ServerRequest, Op) whose
|
|
5
|
-
//
|
|
4
|
+
// a pathgrade-local composition (ClientRequest, ServerRequest, Op) whose header
|
|
5
|
+
// spells out the composition rationale.
|
|
6
6
|
//
|
|
7
7
|
// Refresh policy: to bump the vendored version, update each file's header
|
|
8
8
|
// citation to the new tag, replace the `unknown` aliases with their vendored
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function extractTurnCompletionFailure(params: unknown): string | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function extractTurnCompletionFailure(params) {
|
|
2
|
+
const turn = params?.turn;
|
|
3
|
+
const message = turn?.error?.message;
|
|
4
|
+
const details = turn?.error?.additionalDetails;
|
|
5
|
+
if (typeof message === 'string' && message.length > 0) {
|
|
6
|
+
return typeof details === 'string' && details.length > 0 ? `${message} ${details}` : message;
|
|
7
|
+
}
|
|
8
|
+
return turn?.status === 'failed' ? 'turn failed' : undefined;
|
|
9
|
+
}
|
|
@@ -12,19 +12,11 @@
|
|
|
12
12
|
* and `--explain` / `--json` presentation.
|
|
13
13
|
*/
|
|
14
14
|
import * as fs from 'fs';
|
|
15
|
-
import { configDefaults } from 'vitest/config';
|
|
16
15
|
import { selectAffected } from '../affected/select.js';
|
|
17
16
|
import { resolveBaseRef, computeChangedFiles } from '../affected/git.js';
|
|
18
|
-
import { loadAffectedConfig } from '../affected/config.js';
|
|
19
17
|
import { formatExplain, formatJson } from '../affected/format.js';
|
|
20
18
|
import { discoverPathgradeEvalFiles } from '../evals/discovery.js';
|
|
21
|
-
|
|
22
|
-
...configDefaults.exclude,
|
|
23
|
-
'.worktrees/**',
|
|
24
|
-
'worktrees/**',
|
|
25
|
-
'**/node_modules/**',
|
|
26
|
-
'**/fixtures/**',
|
|
27
|
-
];
|
|
19
|
+
import { DEFAULT_EVAL_EXCLUDE, DEFAULT_EVAL_INCLUDE, resolvePathgradeConfig, } from '../config/pathgrade.js';
|
|
28
20
|
export async function runAffected(opts) {
|
|
29
21
|
const { cwd } = opts;
|
|
30
22
|
let changes;
|
|
@@ -44,9 +36,21 @@ export async function runAffected(opts) {
|
|
|
44
36
|
if (changes.baseRefLine) {
|
|
45
37
|
process.stderr.write(`${changes.baseRefLine}\n`);
|
|
46
38
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
let config;
|
|
40
|
+
try {
|
|
41
|
+
config = await resolvePathgradeConfig({
|
|
42
|
+
cwd,
|
|
43
|
+
warn: w => process.stderr.write(`${w}\n`),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
process.stderr.write(`pathgrade affected: ${errMsg(err)}\n`);
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
const evalFiles = discoverPathgradeEvalFiles({
|
|
51
|
+
cwd,
|
|
52
|
+
include: config.evals.include,
|
|
53
|
+
exclude: config.evals.exclude,
|
|
50
54
|
});
|
|
51
55
|
let result;
|
|
52
56
|
try {
|
|
@@ -55,7 +59,7 @@ export async function runAffected(opts) {
|
|
|
55
59
|
changedFiles: changes.changedFiles,
|
|
56
60
|
repoRoot: cwd,
|
|
57
61
|
baseRef: changes.baseRef,
|
|
58
|
-
global: config.global,
|
|
62
|
+
global: config.affected.global,
|
|
59
63
|
});
|
|
60
64
|
}
|
|
61
65
|
catch (err) {
|
|
@@ -132,6 +136,9 @@ function readChangedFilesList(filePath) {
|
|
|
132
136
|
.map(l => l.trim())
|
|
133
137
|
.filter(l => l.length > 0);
|
|
134
138
|
}
|
|
139
|
+
function errMsg(err) {
|
|
140
|
+
return err instanceof Error ? err.message : String(err);
|
|
141
|
+
}
|
|
135
142
|
/**
|
|
136
143
|
* Discover repo-relative `*.eval.ts` files under `cwd`, honoring the same
|
|
137
144
|
* exclude defaults the vitest plugin uses.
|
|
@@ -139,8 +146,8 @@ function readChangedFilesList(filePath) {
|
|
|
139
146
|
export function discoverEvalFiles(cwd) {
|
|
140
147
|
return discoverPathgradeEvalFiles({
|
|
141
148
|
cwd,
|
|
142
|
-
include:
|
|
143
|
-
exclude:
|
|
149
|
+
include: DEFAULT_EVAL_INCLUDE,
|
|
150
|
+
exclude: DEFAULT_EVAL_EXCLUDE,
|
|
144
151
|
});
|
|
145
152
|
}
|
|
146
153
|
function printSelectedToStdout(result) {
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* Parse `pathgrade run` arguments.
|
|
3
3
|
*
|
|
4
4
|
* Recognized pathgrade-specific flags are consumed here; everything else
|
|
5
|
-
* passes through to
|
|
6
|
-
* args are forwarded verbatim
|
|
5
|
+
* passes through to the selected runner. After a literal `--` separator,
|
|
6
|
+
* all remaining args are forwarded verbatim.
|
|
7
7
|
*/
|
|
8
8
|
export interface PathgradeRunArgs {
|
|
9
|
-
|
|
9
|
+
runnerArgs: string[];
|
|
10
|
+
adapterName?: string;
|
|
10
11
|
forceDiagnostics: boolean;
|
|
11
12
|
forceVerbose: boolean;
|
|
12
13
|
changed: boolean;
|
|
@@ -2,21 +2,22 @@
|
|
|
2
2
|
* Parse `pathgrade run` arguments.
|
|
3
3
|
*
|
|
4
4
|
* Recognized pathgrade-specific flags are consumed here; everything else
|
|
5
|
-
* passes through to
|
|
6
|
-
* args are forwarded verbatim
|
|
5
|
+
* passes through to the selected runner. After a literal `--` separator,
|
|
6
|
+
* all remaining args are forwarded verbatim.
|
|
7
7
|
*/
|
|
8
8
|
export function parsePathgradeRunArgs(args) {
|
|
9
|
-
const
|
|
9
|
+
const runnerArgs = [];
|
|
10
10
|
let forceDiagnostics = false;
|
|
11
11
|
let forceVerbose = false;
|
|
12
12
|
let changed = false;
|
|
13
13
|
let quiet = false;
|
|
14
14
|
let since;
|
|
15
15
|
let changedFilesPath;
|
|
16
|
+
let adapterName;
|
|
16
17
|
let passthrough = false;
|
|
17
18
|
for (const arg of args) {
|
|
18
19
|
if (passthrough) {
|
|
19
|
-
|
|
20
|
+
runnerArgs.push(arg);
|
|
20
21
|
continue;
|
|
21
22
|
}
|
|
22
23
|
if (arg === '--') {
|
|
@@ -39,6 +40,10 @@ export function parsePathgradeRunArgs(args) {
|
|
|
39
40
|
quiet = true;
|
|
40
41
|
continue;
|
|
41
42
|
}
|
|
43
|
+
if (arg.startsWith('--adapter=')) {
|
|
44
|
+
adapterName = arg.slice('--adapter='.length);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
42
47
|
if (arg.startsWith('--since=')) {
|
|
43
48
|
since = arg.slice('--since='.length);
|
|
44
49
|
continue;
|
|
@@ -47,7 +52,7 @@ export function parsePathgradeRunArgs(args) {
|
|
|
47
52
|
changedFilesPath = arg.slice('--changed-files='.length);
|
|
48
53
|
continue;
|
|
49
54
|
}
|
|
50
|
-
|
|
55
|
+
runnerArgs.push(arg);
|
|
51
56
|
}
|
|
52
57
|
const warnings = [];
|
|
53
58
|
if (!changed && since !== undefined) {
|
|
@@ -57,11 +62,12 @@ export function parsePathgradeRunArgs(args) {
|
|
|
57
62
|
warnings.push('--changed-files has no effect without --changed; the flag is being ignored.');
|
|
58
63
|
}
|
|
59
64
|
const base = {
|
|
60
|
-
|
|
65
|
+
runnerArgs,
|
|
61
66
|
forceDiagnostics,
|
|
62
67
|
forceVerbose,
|
|
63
68
|
changed,
|
|
64
69
|
quiet,
|
|
70
|
+
adapterName,
|
|
65
71
|
since,
|
|
66
72
|
changedFilesPath,
|
|
67
73
|
};
|
|
@@ -12,15 +12,15 @@
|
|
|
12
12
|
* Git-resolution failures exit non-zero. The selection pipeline is the
|
|
13
13
|
* only producer of the file list here.
|
|
14
14
|
*/
|
|
15
|
+
import type { RunnerInvocationAdapter } from '../runners/invocation.js';
|
|
16
|
+
import type { SpawnVitest } from '../runners/vitest-invocation.js';
|
|
15
17
|
import type { PathgradeRunArgs } from './run-args.js';
|
|
16
|
-
export
|
|
17
|
-
argv: string[];
|
|
18
|
-
}
|
|
19
|
-
export type SpawnVitest = (req: SpawnVitestRequest) => Promise<number> | number;
|
|
18
|
+
export type { SpawnVitest };
|
|
20
19
|
export interface RunChangedOptions {
|
|
21
20
|
cwd: string;
|
|
22
21
|
parsed: PathgradeRunArgs;
|
|
23
22
|
/** Override for tests; default spawns `npx vitest` inheriting stdio. */
|
|
24
23
|
spawnVitest?: SpawnVitest;
|
|
24
|
+
runnerInvocation?: RunnerInvocationAdapter;
|
|
25
25
|
}
|
|
26
26
|
export declare function runChanged(opts: RunChangedOptions): Promise<number>;
|
|
@@ -13,15 +13,42 @@
|
|
|
13
13
|
* only producer of the file list here.
|
|
14
14
|
*/
|
|
15
15
|
import * as fs from 'fs';
|
|
16
|
-
import picomatch from 'picomatch';
|
|
17
16
|
import { selectAffected } from '../affected/select.js';
|
|
18
17
|
import { resolveBaseRef, computeChangedFiles } from '../affected/git.js';
|
|
19
|
-
import { loadAffectedConfig } from '../affected/config.js';
|
|
20
18
|
import { writeSidecar } from '../affected/sidecar.js';
|
|
21
|
-
import {
|
|
19
|
+
import { discoverPathgradeEvalFiles } from '../evals/discovery.js';
|
|
20
|
+
import { resolvePathgradeConfig } from '../config/pathgrade.js';
|
|
21
|
+
import { loadRunnerInvocationAdapter } from '../runners/adapter-loader.js';
|
|
22
22
|
export async function runChanged(opts) {
|
|
23
23
|
const { cwd, parsed } = opts;
|
|
24
|
-
const
|
|
24
|
+
const runnerEnv = {
|
|
25
|
+
...process.env,
|
|
26
|
+
...(parsed.forceDiagnostics ? { PATHGRADE_DIAGNOSTICS: '1' } : {}),
|
|
27
|
+
...(parsed.forceVerbose ? { PATHGRADE_VERBOSE: '1' } : {}),
|
|
28
|
+
};
|
|
29
|
+
const configPath = findVitestConfigArg(parsed.runnerArgs);
|
|
30
|
+
let config;
|
|
31
|
+
let runnerInvocation;
|
|
32
|
+
try {
|
|
33
|
+
config = await resolvePathgradeConfig({
|
|
34
|
+
cwd,
|
|
35
|
+
legacyVitestConfigPath: configPath,
|
|
36
|
+
warn: w => {
|
|
37
|
+
if (!parsed.quiet)
|
|
38
|
+
process.stderr.write(`${w}\n`);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
runnerInvocation = opts.runnerInvocation ?? await loadRunnerInvocationAdapter({
|
|
42
|
+
adapterName: parsed.adapterName ?? config.runner.adapter,
|
|
43
|
+
cwd,
|
|
44
|
+
config,
|
|
45
|
+
spawnVitest: opts.spawnVitest,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
process.stderr.write(`${errMsg(err)}\n`);
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
25
52
|
// 1. Change-set
|
|
26
53
|
let baseRef;
|
|
27
54
|
let changedFiles;
|
|
@@ -53,22 +80,11 @@ export async function runChanged(opts) {
|
|
|
53
80
|
baseRefLine = `pathgrade: base = ${baseRef} (merge-base with HEAD)`;
|
|
54
81
|
}
|
|
55
82
|
// 2. Selection
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
config
|
|
60
|
-
|
|
61
|
-
onWarning: w => {
|
|
62
|
-
if (!parsed.quiet)
|
|
63
|
-
process.stderr.write(`${w}\n`);
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
process.stderr.write(`${errMsg(err)}\n`);
|
|
69
|
-
return 1;
|
|
70
|
-
}
|
|
71
|
-
const evalFiles = filterEvalFilesForConfig(discoverEvalFiles(cwd), config);
|
|
83
|
+
const evalFiles = discoverPathgradeEvalFiles({
|
|
84
|
+
cwd,
|
|
85
|
+
include: config.evals.include,
|
|
86
|
+
exclude: config.evals.exclude,
|
|
87
|
+
});
|
|
72
88
|
let result;
|
|
73
89
|
try {
|
|
74
90
|
result = selectAffected({
|
|
@@ -76,7 +92,7 @@ export async function runChanged(opts) {
|
|
|
76
92
|
changedFiles,
|
|
77
93
|
repoRoot: cwd,
|
|
78
94
|
baseRef,
|
|
79
|
-
global: config.global,
|
|
95
|
+
global: config.affected.global,
|
|
80
96
|
});
|
|
81
97
|
}
|
|
82
98
|
catch (err) {
|
|
@@ -103,16 +119,16 @@ export async function runChanged(opts) {
|
|
|
103
119
|
return 0;
|
|
104
120
|
}
|
|
105
121
|
const selectedFiles = result.selected.map(s => s.file);
|
|
106
|
-
|
|
107
|
-
process.stderr.write('pathgrade run: --passWithNoTests cannot be used with pathgrade run --changed. ' +
|
|
108
|
-
'The command already exits 0 when no evals are selected; if selected evals resolve to no Vitest files, CI must fail.\n');
|
|
109
|
-
return 1;
|
|
110
|
-
}
|
|
111
|
-
const argv = ['run', ...selectedFiles, ...parsed.vitestArgs];
|
|
122
|
+
const runnerArgs = [...config.runner.args, ...parsed.runnerArgs];
|
|
112
123
|
if (!parsed.quiet) {
|
|
113
|
-
process.stderr.write(`→
|
|
124
|
+
process.stderr.write(`→ ${runnerInvocation.name} run ${selectedFiles.join(' ')}\n`);
|
|
114
125
|
}
|
|
115
|
-
return await
|
|
126
|
+
return await runnerInvocation.run({
|
|
127
|
+
cwd,
|
|
128
|
+
runnerArgs,
|
|
129
|
+
selectedFiles,
|
|
130
|
+
env: runnerEnv,
|
|
131
|
+
});
|
|
116
132
|
}
|
|
117
133
|
function printRunStartSummary(input) {
|
|
118
134
|
const { baseRefLine, totalEvals, changedCount, result } = input;
|
|
@@ -141,15 +157,6 @@ function readChangedFilesList(filePath) {
|
|
|
141
157
|
function errMsg(err) {
|
|
142
158
|
return err instanceof Error ? err.message : String(err);
|
|
143
159
|
}
|
|
144
|
-
function hasPassWithNoTests(args) {
|
|
145
|
-
return args.some(arg => {
|
|
146
|
-
if (arg === '--passWithNoTests')
|
|
147
|
-
return true;
|
|
148
|
-
if (!arg.startsWith('--passWithNoTests='))
|
|
149
|
-
return false;
|
|
150
|
-
return arg.slice('--passWithNoTests='.length).toLowerCase() !== 'false';
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
160
|
function findVitestConfigArg(args) {
|
|
154
161
|
for (let i = 0; i < args.length; i++) {
|
|
155
162
|
const arg = args[i];
|
|
@@ -162,23 +169,3 @@ function findVitestConfigArg(args) {
|
|
|
162
169
|
}
|
|
163
170
|
return undefined;
|
|
164
171
|
}
|
|
165
|
-
function filterEvalFilesForConfig(evalFiles, config) {
|
|
166
|
-
if (!config.include && !config.exclude)
|
|
167
|
-
return evalFiles;
|
|
168
|
-
const includeMatchers = config.include?.map(g => picomatch(g, { dot: true }));
|
|
169
|
-
const excludeMatchers = config.exclude?.map(g => picomatch(g, { dot: true })) ?? [];
|
|
170
|
-
return evalFiles.filter(file => {
|
|
171
|
-
const included = includeMatchers ? includeMatchers.some(m => m(file)) : true;
|
|
172
|
-
return included && !excludeMatchers.some(m => m(file));
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
async function defaultSpawnVitest(req) {
|
|
176
|
-
const { spawn } = await import('child_process');
|
|
177
|
-
return await new Promise(resolve => {
|
|
178
|
-
const child = spawn('npx', ['vitest', ...req.argv], {
|
|
179
|
-
stdio: 'inherit',
|
|
180
|
-
shell: true,
|
|
181
|
-
});
|
|
182
|
-
child.on('close', code => resolve(code ?? 0));
|
|
183
|
-
});
|
|
184
|
-
}
|