agent-gauntlet 0.1.10 → 0.1.11
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 +1 -1
- package/package.json +4 -2
- package/src/cli-adapters/claude.ts +139 -108
- package/src/cli-adapters/codex.ts +141 -117
- package/src/cli-adapters/cursor.ts +152 -0
- package/src/cli-adapters/gemini.ts +171 -139
- package/src/cli-adapters/github-copilot.ts +153 -0
- package/src/cli-adapters/index.ts +77 -48
- package/src/commands/check.test.ts +24 -20
- package/src/commands/check.ts +65 -59
- package/src/commands/detect.test.ts +38 -32
- package/src/commands/detect.ts +74 -61
- package/src/commands/health.test.ts +67 -53
- package/src/commands/health.ts +167 -145
- package/src/commands/help.test.ts +37 -37
- package/src/commands/help.ts +30 -22
- package/src/commands/index.ts +9 -9
- package/src/commands/init.test.ts +118 -107
- package/src/commands/init.ts +514 -417
- package/src/commands/list.test.ts +87 -70
- package/src/commands/list.ts +28 -24
- package/src/commands/rerun.ts +142 -119
- package/src/commands/review.test.ts +26 -20
- package/src/commands/review.ts +65 -59
- package/src/commands/run.test.ts +22 -20
- package/src/commands/run.ts +64 -58
- package/src/commands/shared.ts +44 -35
- package/src/config/loader.test.ts +112 -90
- package/src/config/loader.ts +132 -123
- package/src/config/schema.ts +49 -47
- package/src/config/types.ts +15 -13
- package/src/config/validator.ts +521 -454
- package/src/core/change-detector.ts +122 -104
- package/src/core/entry-point.test.ts +60 -62
- package/src/core/entry-point.ts +76 -67
- package/src/core/job.ts +69 -59
- package/src/core/runner.ts +261 -230
- package/src/gates/check.ts +78 -69
- package/src/gates/result.ts +7 -7
- package/src/gates/review.test.ts +174 -138
- package/src/gates/review.ts +716 -561
- package/src/index.ts +16 -15
- package/src/output/console.ts +253 -214
- package/src/output/logger.ts +64 -52
- package/src/templates/run_gauntlet.template.md +18 -0
- package/src/utils/diff-parser.ts +64 -62
- package/src/utils/log-parser.ts +227 -206
- package/src/utils/sanitizer.ts +1 -1
|
@@ -1,79 +1,108 @@
|
|
|
1
1
|
export interface CLIAdapterHealth {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
available: boolean;
|
|
3
|
+
status: "healthy" | "missing" | "unhealthy";
|
|
4
|
+
message?: string;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export function isUsageLimit(output: string): boolean {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
const lower = output.toLowerCase();
|
|
9
|
+
return (
|
|
10
|
+
lower.includes("usage limit") ||
|
|
11
|
+
lower.includes("quota exceeded") ||
|
|
12
|
+
lower.includes("quota will reset") ||
|
|
13
|
+
lower.includes("credit balance is too low") ||
|
|
14
|
+
lower.includes("out of extra usage") ||
|
|
15
|
+
lower.includes("out of usage")
|
|
16
|
+
);
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export interface CLIAdapter {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
20
|
+
name: string;
|
|
21
|
+
isAvailable(): Promise<boolean>;
|
|
22
|
+
checkHealth(options?: {
|
|
23
|
+
checkUsageLimit?: boolean;
|
|
24
|
+
}): Promise<CLIAdapterHealth>;
|
|
25
|
+
execute(opts: {
|
|
26
|
+
prompt: string;
|
|
27
|
+
diff: string;
|
|
28
|
+
model?: string;
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Returns the project-scoped command directory path (relative to project root).
|
|
33
|
+
* Returns null if the CLI only supports user-level commands.
|
|
34
|
+
*/
|
|
35
|
+
getProjectCommandDir(): string | null;
|
|
36
|
+
/**
|
|
37
|
+
* Returns the user-level command directory path (absolute path).
|
|
38
|
+
* Returns null if the CLI doesn't support user-level commands.
|
|
39
|
+
*/
|
|
40
|
+
getUserCommandDir(): string | null;
|
|
41
|
+
/**
|
|
42
|
+
* Returns the command file extension used by this CLI.
|
|
43
|
+
*/
|
|
44
|
+
getCommandExtension(): string;
|
|
45
|
+
/**
|
|
46
|
+
* Returns true if this adapter can use symlinks (same format as source Markdown).
|
|
47
|
+
*/
|
|
48
|
+
canUseSymlink(): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Transforms gauntlet command content to this CLI's format.
|
|
51
|
+
* The source content is always Markdown with YAML frontmatter.
|
|
52
|
+
*/
|
|
53
|
+
transformCommand(markdownContent: string): string;
|
|
45
54
|
}
|
|
46
55
|
|
|
47
|
-
import {
|
|
48
|
-
import { CodexAdapter } from
|
|
49
|
-
import {
|
|
56
|
+
import { ClaudeAdapter } from "./claude.js";
|
|
57
|
+
import { CodexAdapter } from "./codex.js";
|
|
58
|
+
import { CursorAdapter } from "./cursor.js";
|
|
59
|
+
import { GeminiAdapter } from "./gemini.js";
|
|
60
|
+
import { GitHubCopilotAdapter } from "./github-copilot.js";
|
|
50
61
|
|
|
51
|
-
export {
|
|
62
|
+
export {
|
|
63
|
+
GeminiAdapter,
|
|
64
|
+
CodexAdapter,
|
|
65
|
+
ClaudeAdapter,
|
|
66
|
+
GitHubCopilotAdapter,
|
|
67
|
+
CursorAdapter,
|
|
68
|
+
};
|
|
52
69
|
|
|
70
|
+
// Adapter registry: keys should use lowercase with hyphens for multi-word names
|
|
53
71
|
const adapters: Record<string, CLIAdapter> = {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
gemini: new GeminiAdapter(),
|
|
73
|
+
codex: new CodexAdapter(),
|
|
74
|
+
claude: new ClaudeAdapter(),
|
|
75
|
+
"github-copilot": new GitHubCopilotAdapter(),
|
|
76
|
+
cursor: new CursorAdapter(),
|
|
57
77
|
};
|
|
58
78
|
|
|
59
79
|
export function getAdapter(name: string): CLIAdapter | undefined {
|
|
60
|
-
|
|
80
|
+
return adapters[name];
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
export function getAllAdapters(): CLIAdapter[] {
|
|
64
|
-
|
|
84
|
+
return Object.values(adapters);
|
|
65
85
|
}
|
|
66
86
|
|
|
67
87
|
/**
|
|
68
88
|
* Returns all adapters that support project-scoped commands.
|
|
69
89
|
*/
|
|
70
90
|
export function getProjectCommandAdapters(): CLIAdapter[] {
|
|
71
|
-
|
|
91
|
+
return Object.values(adapters).filter(
|
|
92
|
+
(a) => a.getProjectCommandDir() !== null,
|
|
93
|
+
);
|
|
72
94
|
}
|
|
73
95
|
|
|
74
96
|
/**
|
|
75
97
|
* Returns all adapters that support user-level commands.
|
|
76
98
|
*/
|
|
77
99
|
export function getUserCommandAdapters(): CLIAdapter[] {
|
|
78
|
-
|
|
100
|
+
return Object.values(adapters).filter((a) => a.getUserCommandDir() !== null);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Returns all valid CLI tool names (adapter registry keys).
|
|
105
|
+
*/
|
|
106
|
+
export function getValidCLITools(): string[] {
|
|
107
|
+
return Object.keys(adapters);
|
|
79
108
|
}
|
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Command } from
|
|
3
|
-
import { registerCheckCommand } from
|
|
1
|
+
import { beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { registerCheckCommand } from "./check.js";
|
|
4
4
|
|
|
5
|
-
describe(
|
|
6
|
-
|
|
5
|
+
describe("Check Command", () => {
|
|
6
|
+
let program: Command;
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
program = new Command();
|
|
10
|
+
registerCheckCommand(program);
|
|
11
|
+
});
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
it("should register the check command", () => {
|
|
14
|
+
const checkCmd = program.commands.find((cmd) => cmd.name() === "check");
|
|
15
|
+
expect(checkCmd).toBeDefined();
|
|
16
|
+
expect(checkCmd?.description()).toBe(
|
|
17
|
+
"Run only applicable checks for detected changes",
|
|
18
|
+
);
|
|
19
|
+
});
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
it("should have correct options", () => {
|
|
22
|
+
const checkCmd = program.commands.find((cmd) => cmd.name() === "check");
|
|
23
|
+
expect(checkCmd?.options.some((opt) => opt.long === "--gate")).toBe(true);
|
|
24
|
+
expect(checkCmd?.options.some((opt) => opt.long === "--commit")).toBe(true);
|
|
25
|
+
expect(checkCmd?.options.some((opt) => opt.long === "--uncommitted")).toBe(
|
|
26
|
+
true,
|
|
27
|
+
);
|
|
28
|
+
});
|
|
25
29
|
});
|
package/src/commands/check.ts
CHANGED
|
@@ -1,72 +1,78 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { loadConfig } from
|
|
4
|
-
import { ChangeDetector } from
|
|
5
|
-
import { EntryPointExpander } from
|
|
6
|
-
import { JobGenerator } from
|
|
7
|
-
import { Runner } from
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { rotateLogs } from
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import { loadConfig } from "../config/loader.js";
|
|
4
|
+
import { ChangeDetector } from "../core/change-detector.js";
|
|
5
|
+
import { EntryPointExpander } from "../core/entry-point.js";
|
|
6
|
+
import { JobGenerator } from "../core/job.js";
|
|
7
|
+
import { Runner } from "../core/runner.js";
|
|
8
|
+
import { ConsoleReporter } from "../output/console.js";
|
|
9
|
+
import { Logger } from "../output/logger.js";
|
|
10
|
+
import { rotateLogs } from "./shared.js";
|
|
11
11
|
|
|
12
12
|
export function registerCheckCommand(program: Command): void {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
program
|
|
14
|
+
.command("check")
|
|
15
|
+
.description("Run only applicable checks for detected changes")
|
|
16
|
+
.option("-g, --gate <name>", "Run specific check gate only")
|
|
17
|
+
.option("-c, --commit <sha>", "Use diff for a specific commit")
|
|
18
|
+
.option(
|
|
19
|
+
"-u, --uncommitted",
|
|
20
|
+
"Use diff for current uncommitted changes (staged and unstaged)",
|
|
21
|
+
)
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
try {
|
|
24
|
+
const config = await loadConfig();
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
// Rotate logs before starting
|
|
27
|
+
await rotateLogs(config.project.log_dir);
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
console.log(chalk.dim('Detecting changes...'));
|
|
34
|
-
const changes = await changeDetector.getChangedFiles();
|
|
35
|
-
|
|
36
|
-
if (changes.length === 0) {
|
|
37
|
-
console.log(chalk.green('No changes detected.'));
|
|
38
|
-
process.exit(0);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
console.log(chalk.dim(`Found ${changes.length} changed files.`));
|
|
29
|
+
const changeDetector = new ChangeDetector(config.project.base_branch, {
|
|
30
|
+
commit: options.commit,
|
|
31
|
+
uncommitted: options.uncommitted,
|
|
32
|
+
});
|
|
33
|
+
const expander = new EntryPointExpander();
|
|
34
|
+
const jobGen = new JobGenerator(config);
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
console.log(chalk.dim("Detecting changes..."));
|
|
37
|
+
const changes = await changeDetector.getChangedFiles();
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
if (changes.length === 0) {
|
|
40
|
+
console.log(chalk.green("No changes detected."));
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
jobs = jobs.filter(j => j.name === options.gate);
|
|
51
|
-
}
|
|
44
|
+
console.log(chalk.dim(`Found ${changes.length} changed files.`));
|
|
52
45
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
const entryPoints = await expander.expand(
|
|
47
|
+
config.project.entry_points,
|
|
48
|
+
changes,
|
|
49
|
+
);
|
|
50
|
+
let jobs = jobGen.generateJobs(entryPoints);
|
|
57
51
|
|
|
58
|
-
|
|
52
|
+
// Filter to only checks
|
|
53
|
+
jobs = jobs.filter((j) => j.type === "check");
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
if (options.gate) {
|
|
56
|
+
jobs = jobs.filter((j) => j.name === options.gate);
|
|
57
|
+
}
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
if (jobs.length === 0) {
|
|
60
|
+
console.log(chalk.yellow("No applicable checks for these changes."));
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
console.log(chalk.dim(`Running ${jobs.length} check(s)...`));
|
|
65
|
+
|
|
66
|
+
const logger = new Logger(config.project.log_dir);
|
|
67
|
+
const reporter = new ConsoleReporter();
|
|
68
|
+
const runner = new Runner(config, logger, reporter);
|
|
69
|
+
|
|
70
|
+
const success = await runner.run(jobs);
|
|
71
|
+
process.exit(success ? 0 : 1);
|
|
72
|
+
} catch (error: unknown) {
|
|
73
|
+
const err = error as { message?: string };
|
|
74
|
+
console.error(chalk.red("Error:"), err.message);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
72
78
|
}
|
|
@@ -1,37 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Command } from
|
|
3
|
-
import { registerDetectCommand } from
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { registerDetectCommand } from "./detect.js";
|
|
4
4
|
|
|
5
|
-
describe(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
describe("Detect Command", () => {
|
|
6
|
+
let program: Command;
|
|
7
|
+
const originalConsoleLog = console.log;
|
|
8
|
+
const originalConsoleError = console.error;
|
|
9
|
+
let logs: string[];
|
|
10
|
+
let errors: string[];
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
program = new Command();
|
|
14
|
+
registerDetectCommand(program);
|
|
15
|
+
logs = [];
|
|
16
|
+
errors = [];
|
|
17
|
+
console.log = (...args: unknown[]) => {
|
|
18
|
+
logs.push(args.join(" "));
|
|
19
|
+
};
|
|
20
|
+
console.error = (...args: unknown[]) => {
|
|
21
|
+
errors.push(args.join(" "));
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
console.log = originalConsoleLog;
|
|
27
|
+
console.error = originalConsoleError;
|
|
28
|
+
});
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
it("should register the detect command", () => {
|
|
31
|
+
const detectCmd = program.commands.find((cmd) => cmd.name() === "detect");
|
|
32
|
+
expect(detectCmd).toBeDefined();
|
|
33
|
+
expect(detectCmd?.description()).toBe(
|
|
34
|
+
"Show what gates would run for detected changes (without executing them)",
|
|
35
|
+
);
|
|
36
|
+
expect(detectCmd?.options.some((opt) => opt.long === "--commit")).toBe(
|
|
37
|
+
true,
|
|
38
|
+
);
|
|
39
|
+
expect(detectCmd?.options.some((opt) => opt.long === "--uncommitted")).toBe(
|
|
40
|
+
true,
|
|
41
|
+
);
|
|
42
|
+
});
|
|
37
43
|
});
|
package/src/commands/detect.ts
CHANGED
|
@@ -1,69 +1,82 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { loadConfig } from
|
|
4
|
-
import { ChangeDetector } from
|
|
5
|
-
import { EntryPointExpander } from
|
|
6
|
-
import {
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import { loadConfig } from "../config/loader.js";
|
|
4
|
+
import { ChangeDetector } from "../core/change-detector.js";
|
|
5
|
+
import { EntryPointExpander } from "../core/entry-point.js";
|
|
6
|
+
import { type Job, JobGenerator } from "../core/job.js";
|
|
7
7
|
|
|
8
8
|
export function registerDetectCommand(program: Command): void {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
console.log(chalk.green('No changes detected.'));
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
console.log(chalk.dim(`Found ${changes.length} changed files:`));
|
|
33
|
-
changes.forEach(file => console.log(chalk.dim(` - ${file}`)));
|
|
34
|
-
console.log();
|
|
9
|
+
program
|
|
10
|
+
.command("detect")
|
|
11
|
+
.description(
|
|
12
|
+
"Show what gates would run for detected changes (without executing them)",
|
|
13
|
+
)
|
|
14
|
+
.option("-c, --commit <sha>", "Use diff for a specific commit")
|
|
15
|
+
.option(
|
|
16
|
+
"-u, --uncommitted",
|
|
17
|
+
"Use diff for current uncommitted changes (staged and unstaged)",
|
|
18
|
+
)
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
try {
|
|
21
|
+
const config = await loadConfig();
|
|
22
|
+
const changeDetector = new ChangeDetector(config.project.base_branch, {
|
|
23
|
+
commit: options.commit,
|
|
24
|
+
uncommitted: options.uncommitted,
|
|
25
|
+
});
|
|
26
|
+
const expander = new EntryPointExpander();
|
|
27
|
+
const jobGen = new JobGenerator(config);
|
|
35
28
|
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
console.log(chalk.dim("Detecting changes..."));
|
|
30
|
+
const changes = await changeDetector.getChangedFiles();
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
if (changes.length === 0) {
|
|
33
|
+
console.log(chalk.green("No changes detected."));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
43
36
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (!jobsByEntryPoint.has(job.entryPoint)) {
|
|
50
|
-
jobsByEntryPoint.set(job.entryPoint, []);
|
|
51
|
-
}
|
|
52
|
-
jobsByEntryPoint.get(job.entryPoint)!.push(job);
|
|
53
|
-
}
|
|
37
|
+
console.log(chalk.dim(`Found ${changes.length} changed files:`));
|
|
38
|
+
changes.forEach((file) => {
|
|
39
|
+
console.log(chalk.dim(` - ${file}`));
|
|
40
|
+
});
|
|
41
|
+
console.log();
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
console.log();
|
|
62
|
-
}
|
|
43
|
+
const entryPoints = await expander.expand(
|
|
44
|
+
config.project.entry_points,
|
|
45
|
+
changes,
|
|
46
|
+
);
|
|
47
|
+
const jobs = jobGen.generateJobs(entryPoints);
|
|
63
48
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
if (jobs.length === 0) {
|
|
50
|
+
console.log(chalk.yellow("No applicable gates for these changes."));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log(chalk.bold(`Would run ${jobs.length} gate(s):\n`));
|
|
55
|
+
|
|
56
|
+
// Group jobs by entry point for better display
|
|
57
|
+
const jobsByEntryPoint = new Map<string, Job[]>();
|
|
58
|
+
for (const job of jobs) {
|
|
59
|
+
if (!jobsByEntryPoint.has(job.entryPoint)) {
|
|
60
|
+
jobsByEntryPoint.set(job.entryPoint, []);
|
|
61
|
+
}
|
|
62
|
+
jobsByEntryPoint.get(job.entryPoint)?.push(job);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const [entryPoint, entryJobs] of jobsByEntryPoint.entries()) {
|
|
66
|
+
console.log(chalk.cyan(`Entry point: ${entryPoint}`));
|
|
67
|
+
for (const job of entryJobs) {
|
|
68
|
+
const typeLabel =
|
|
69
|
+
job.type === "check"
|
|
70
|
+
? chalk.yellow("check")
|
|
71
|
+
: chalk.blue("review");
|
|
72
|
+
console.log(` ${typeLabel} ${chalk.bold(job.name)}`);
|
|
73
|
+
}
|
|
74
|
+
console.log();
|
|
75
|
+
}
|
|
76
|
+
} catch (error: unknown) {
|
|
77
|
+
const err = error as { message?: string };
|
|
78
|
+
console.error(chalk.red("Error:"), err.message);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
69
82
|
}
|