agent-gauntlet 0.10.0 → 0.11.0
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 +25 -23
- package/dist/index.js +9226 -0
- package/dist/index.js.map +65 -0
- package/dist/scripts/status.js +280 -0
- package/dist/scripts/status.js.map +10 -0
- package/package.json +22 -8
- package/src/built-in-reviews/code-quality.md +0 -25
- package/src/built-in-reviews/index.ts +0 -28
- package/src/bun-plugins.d.ts +0 -4
- package/src/cli-adapters/claude.ts +0 -327
- package/src/cli-adapters/codex.ts +0 -290
- package/src/cli-adapters/cursor.ts +0 -128
- package/src/cli-adapters/gemini.ts +0 -510
- package/src/cli-adapters/github-copilot.ts +0 -141
- package/src/cli-adapters/index.ts +0 -250
- package/src/cli-adapters/thinking-budget.ts +0 -23
- package/src/commands/check.ts +0 -311
- package/src/commands/ci/index.ts +0 -15
- package/src/commands/ci/init.ts +0 -96
- package/src/commands/ci/list-jobs.ts +0 -90
- package/src/commands/clean.ts +0 -54
- package/src/commands/detect.ts +0 -173
- package/src/commands/health.ts +0 -169
- package/src/commands/help.ts +0 -34
- package/src/commands/index.ts +0 -13
- package/src/commands/init.ts +0 -1878
- package/src/commands/list.ts +0 -33
- package/src/commands/review.ts +0 -311
- package/src/commands/run.ts +0 -29
- package/src/commands/shared.ts +0 -267
- package/src/commands/stop-hook.ts +0 -567
- package/src/commands/validate.ts +0 -20
- package/src/commands/wait-ci.ts +0 -518
- package/src/config/ci-loader.ts +0 -33
- package/src/config/ci-schema.ts +0 -28
- package/src/config/global.ts +0 -87
- package/src/config/loader.ts +0 -301
- package/src/config/schema.ts +0 -165
- package/src/config/stop-hook-config.ts +0 -130
- package/src/config/types.ts +0 -65
- package/src/config/validator.ts +0 -592
- package/src/core/change-detector.ts +0 -137
- package/src/core/diff-stats.ts +0 -442
- package/src/core/entry-point.ts +0 -190
- package/src/core/job.ts +0 -96
- package/src/core/run-executor.ts +0 -621
- package/src/core/runner.ts +0 -290
- package/src/gates/check.ts +0 -118
- package/src/gates/resolve-check-command.ts +0 -21
- package/src/gates/result.ts +0 -54
- package/src/gates/review.ts +0 -1333
- package/src/hooks/adapters/claude-stop-hook.ts +0 -99
- package/src/hooks/adapters/cursor-stop-hook.ts +0 -122
- package/src/hooks/adapters/types.ts +0 -94
- package/src/hooks/stop-hook-handler.ts +0 -748
- package/src/index.ts +0 -47
- package/src/output/app-logger.ts +0 -214
- package/src/output/console-log.ts +0 -168
- package/src/output/console.ts +0 -359
- package/src/output/logger.ts +0 -126
- package/src/output/sinks/console-sink.ts +0 -59
- package/src/output/sinks/file-sink.ts +0 -110
- package/src/scripts/status.ts +0 -433
- package/src/templates/workflow.yml +0 -79
- package/src/types/gauntlet-status.ts +0 -79
- package/src/utils/debug-log.ts +0 -392
- package/src/utils/diff-parser.ts +0 -103
- package/src/utils/execution-state.ts +0 -472
- package/src/utils/log-parser.ts +0 -696
- package/src/utils/sanitizer.ts +0 -3
- package/src/utils/session-ref.ts +0 -91
package/src/core/entry-point.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { Glob } from "bun";
|
|
4
|
-
import type { EntryPointConfig } from "../config/types.js";
|
|
5
|
-
|
|
6
|
-
export interface ExpandedEntryPoint {
|
|
7
|
-
path: string; // The specific directory (e.g., "engines/billing")
|
|
8
|
-
config: EntryPointConfig; // The config that generated this (e.g., "engines/*")
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class EntryPointExpander {
|
|
12
|
-
async expand(
|
|
13
|
-
entryPoints: EntryPointConfig[],
|
|
14
|
-
changedFiles: string[],
|
|
15
|
-
): Promise<ExpandedEntryPoint[]> {
|
|
16
|
-
const results: ExpandedEntryPoint[] = [];
|
|
17
|
-
const rootEntryPoint = entryPoints.find((ep) => ep.path === ".");
|
|
18
|
-
|
|
19
|
-
// Always include root entry point if configured and there are ANY changes
|
|
20
|
-
if (changedFiles.length > 0) {
|
|
21
|
-
const rootConfig = rootEntryPoint ?? { path: "." };
|
|
22
|
-
// Apply exclusion filtering for root if configured
|
|
23
|
-
const filteredRootChanges = this.filterExcludedFiles(
|
|
24
|
-
changedFiles,
|
|
25
|
-
rootConfig.exclude,
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
if (filteredRootChanges.length > 0) {
|
|
29
|
-
results.push({ path: ".", config: rootConfig });
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
for (const ep of entryPoints) {
|
|
34
|
-
if (ep.path === ".") continue; // Handled above
|
|
35
|
-
|
|
36
|
-
// Apply exclusion filtering first!
|
|
37
|
-
const filteredChanges = this.filterExcludedFiles(
|
|
38
|
-
changedFiles,
|
|
39
|
-
ep.exclude,
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
// If no relevant files remain, skip this entry point
|
|
43
|
-
if (filteredChanges.length === 0) continue;
|
|
44
|
-
|
|
45
|
-
if (ep.path.endsWith("*") && !ep.path.includes("**")) {
|
|
46
|
-
// Single-level wildcard directory (e.g., "engines/*")
|
|
47
|
-
const parentDir = ep.path.slice(0, -2); // "engines"
|
|
48
|
-
const expandedPaths = await this.expandWildcard(
|
|
49
|
-
parentDir,
|
|
50
|
-
filteredChanges,
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
for (const subDir of expandedPaths) {
|
|
54
|
-
results.push({
|
|
55
|
-
path: subDir,
|
|
56
|
-
config: ep,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
} else if (this.isGlobPattern(ep.path)) {
|
|
60
|
-
// Glob pattern (e.g., "openspec/changes/**/tasks.md")
|
|
61
|
-
if (this.hasMatchingFiles(ep.path, filteredChanges)) {
|
|
62
|
-
results.push({
|
|
63
|
-
path: ep.path,
|
|
64
|
-
config: ep,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
// Fixed directory (e.g., "apps/api")
|
|
69
|
-
if (this.hasChangesInDir(ep.path, filteredChanges)) {
|
|
70
|
-
results.push({
|
|
71
|
-
path: ep.path,
|
|
72
|
-
config: ep,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return results;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async expandAll(
|
|
82
|
-
entryPoints: EntryPointConfig[],
|
|
83
|
-
): Promise<ExpandedEntryPoint[]> {
|
|
84
|
-
const results: ExpandedEntryPoint[] = [];
|
|
85
|
-
|
|
86
|
-
for (const ep of entryPoints) {
|
|
87
|
-
if (ep.path === ".") {
|
|
88
|
-
results.push({ path: ".", config: ep });
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (ep.path.endsWith("*") && !ep.path.includes("**")) {
|
|
93
|
-
// Single-level wildcard directory (e.g., "engines/*")
|
|
94
|
-
const parentDir = ep.path.slice(0, -2);
|
|
95
|
-
const subDirs = await this.listSubDirectories(parentDir);
|
|
96
|
-
for (const subDir of subDirs) {
|
|
97
|
-
results.push({ path: subDir, config: ep });
|
|
98
|
-
}
|
|
99
|
-
} else if (this.isGlobPattern(ep.path)) {
|
|
100
|
-
// Glob pattern (e.g., "openspec/changes/**/tasks.md")
|
|
101
|
-
// Include as-is for expandAll since it's a virtual entry point
|
|
102
|
-
results.push({ path: ep.path, config: ep });
|
|
103
|
-
} else {
|
|
104
|
-
results.push({ path: ep.path, config: ep });
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return results;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private filterExcludedFiles(files: string[], patterns?: string[]): string[] {
|
|
112
|
-
if (!patterns || patterns.length === 0) {
|
|
113
|
-
return files;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Pre-compile globs
|
|
117
|
-
const globs: Glob[] = [];
|
|
118
|
-
const prefixes: string[] = [];
|
|
119
|
-
|
|
120
|
-
for (const pattern of patterns) {
|
|
121
|
-
if (pattern.match(/[*?[{]/)) {
|
|
122
|
-
globs.push(new Glob(pattern));
|
|
123
|
-
} else {
|
|
124
|
-
prefixes.push(pattern);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return files.filter((file) => {
|
|
129
|
-
// If matches ANY pattern, exclude it
|
|
130
|
-
const isExcluded =
|
|
131
|
-
prefixes.some((p) => file === p || file.startsWith(`${p}/`)) ||
|
|
132
|
-
globs.some((g) => g.match(file));
|
|
133
|
-
|
|
134
|
-
return !isExcluded;
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private async expandWildcard(
|
|
139
|
-
parentDir: string,
|
|
140
|
-
changedFiles: string[],
|
|
141
|
-
): Promise<string[]> {
|
|
142
|
-
const affectedSubDirs = new Set<string>();
|
|
143
|
-
|
|
144
|
-
// Filter changes that are inside this parent directory
|
|
145
|
-
const relevantChanges = changedFiles.filter((f) =>
|
|
146
|
-
f.startsWith(`${parentDir}/`),
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
for (const file of relevantChanges) {
|
|
150
|
-
// file: "engines/billing/src/foo.ts", parentDir: "engines"
|
|
151
|
-
// relPath: "billing/src/foo.ts"
|
|
152
|
-
const relPath = file.slice(parentDir.length + 1);
|
|
153
|
-
const subDirName = relPath.split("/")[0];
|
|
154
|
-
|
|
155
|
-
if (subDirName) {
|
|
156
|
-
affectedSubDirs.add(path.join(parentDir, subDirName));
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return Array.from(affectedSubDirs);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
private async listSubDirectories(parentDir: string): Promise<string[]> {
|
|
164
|
-
try {
|
|
165
|
-
const dirents = await fs.readdir(parentDir, { withFileTypes: true });
|
|
166
|
-
return dirents
|
|
167
|
-
.filter((d) => d.isDirectory())
|
|
168
|
-
.map((d) => path.join(parentDir, d.name));
|
|
169
|
-
} catch {
|
|
170
|
-
return [];
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private hasChangesInDir(dirPath: string, changedFiles: string[]): boolean {
|
|
175
|
-
// Check if any changed file starts with the dirPath
|
|
176
|
-
// Need to ensure exact match or subdirectory (e.g. "app" should not match "apple")
|
|
177
|
-
const dirPrefix = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
|
|
178
|
-
return changedFiles.some((f) => f === dirPath || f.startsWith(dirPrefix));
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private isGlobPattern(pattern: string): boolean {
|
|
182
|
-
// Check if the pattern contains glob characters
|
|
183
|
-
return /[*?[{]/.test(pattern);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
private hasMatchingFiles(pattern: string, changedFiles: string[]): boolean {
|
|
187
|
-
const glob = new Glob(pattern);
|
|
188
|
-
return changedFiles.some((file) => glob.match(file));
|
|
189
|
-
}
|
|
190
|
-
}
|
package/src/core/job.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CheckGateConfig,
|
|
3
|
-
LoadedConfig,
|
|
4
|
-
LoadedReviewGateConfig,
|
|
5
|
-
} from "../config/types.js";
|
|
6
|
-
import type { ExpandedEntryPoint } from "./entry-point.js";
|
|
7
|
-
|
|
8
|
-
export type JobType = "check" | "review";
|
|
9
|
-
|
|
10
|
-
export interface Job {
|
|
11
|
-
id: string; // unique id for logging/tracking
|
|
12
|
-
type: JobType;
|
|
13
|
-
name: string;
|
|
14
|
-
entryPoint: string;
|
|
15
|
-
gateConfig: CheckGateConfig | LoadedReviewGateConfig;
|
|
16
|
-
workingDirectory: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class JobGenerator {
|
|
20
|
-
constructor(private config: LoadedConfig) {}
|
|
21
|
-
|
|
22
|
-
generateJobs(expandedEntryPoints: ExpandedEntryPoint[]): Job[] {
|
|
23
|
-
const jobs: Job[] = [];
|
|
24
|
-
const seenJobs = new Set<string>();
|
|
25
|
-
const isCI =
|
|
26
|
-
process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
|
|
27
|
-
|
|
28
|
-
for (const ep of expandedEntryPoints) {
|
|
29
|
-
// 1. Process Checks
|
|
30
|
-
if (ep.config.checks) {
|
|
31
|
-
for (const checkName of ep.config.checks) {
|
|
32
|
-
const checkConfig = this.config.checks[checkName];
|
|
33
|
-
if (!checkConfig) {
|
|
34
|
-
console.warn(
|
|
35
|
-
`Warning: Check gate '${checkName}' configured in entry point '${ep.path}' but not found in checks definitions.`,
|
|
36
|
-
);
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Filter based on environment
|
|
41
|
-
if (isCI && !checkConfig.run_in_ci) continue;
|
|
42
|
-
if (!isCI && !checkConfig.run_locally) continue;
|
|
43
|
-
|
|
44
|
-
const workingDirectory =
|
|
45
|
-
checkConfig.working_directory === "entrypoint"
|
|
46
|
-
? ep.path
|
|
47
|
-
: checkConfig.working_directory || ep.path;
|
|
48
|
-
const jobKey = `check:${checkName}:${workingDirectory}`;
|
|
49
|
-
|
|
50
|
-
// Skip if we've already created a job for this check/working-directory combination
|
|
51
|
-
if (seenJobs.has(jobKey)) {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
seenJobs.add(jobKey);
|
|
55
|
-
|
|
56
|
-
jobs.push({
|
|
57
|
-
id: `check:${workingDirectory}:${checkName}`,
|
|
58
|
-
type: "check",
|
|
59
|
-
name: checkName,
|
|
60
|
-
entryPoint: ep.path,
|
|
61
|
-
gateConfig: checkConfig,
|
|
62
|
-
workingDirectory: workingDirectory,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// 2. Process Reviews
|
|
68
|
-
if (ep.config.reviews) {
|
|
69
|
-
for (const reviewName of ep.config.reviews) {
|
|
70
|
-
const reviewConfig = this.config.reviews[reviewName];
|
|
71
|
-
if (!reviewConfig) {
|
|
72
|
-
console.warn(
|
|
73
|
-
`Warning: Review gate '${reviewName}' configured in entry point '${ep.path}' but not found in reviews definitions.`,
|
|
74
|
-
);
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Filter based on environment
|
|
79
|
-
if (isCI && !reviewConfig.run_in_ci) continue;
|
|
80
|
-
if (!isCI && !reviewConfig.run_locally) continue;
|
|
81
|
-
|
|
82
|
-
jobs.push({
|
|
83
|
-
id: `review:${ep.path}:${reviewName}`,
|
|
84
|
-
type: "review",
|
|
85
|
-
name: reviewName,
|
|
86
|
-
entryPoint: ep.path,
|
|
87
|
-
gateConfig: reviewConfig,
|
|
88
|
-
workingDirectory: ep.path, // Reviews always run in context of entry point
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return jobs;
|
|
95
|
-
}
|
|
96
|
-
}
|