Skip to content

Commit f406ef9

Browse files
committed
refactor: move more common code into base agent class
Moves the instructions and ignored files into the agent base class.
1 parent 362e266 commit f406ef9

File tree

6 files changed

+135
-140
lines changed

6 files changed

+135
-140
lines changed

runner/codegen/base-cli-agent-runner.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,54 @@ export abstract class BaseCliAgentRunner {
8686
this.pendingProcesses.clear();
8787
}
8888

89+
/** Gets patterns of files that likely all agents need to ignore. */
90+
protected getCommonIgnorePatterns() {
91+
return {
92+
directories: [
93+
'/dist',
94+
'/tmp',
95+
'/out-tsc',
96+
'/bazel-out',
97+
'/node_modules',
98+
'/.angular/cache',
99+
'.sass-cache/',
100+
'.DS_Store',
101+
],
102+
files: [
103+
'npm-debug.log',
104+
'yarn-error.log',
105+
'.editorconfig',
106+
'.postcssrc.json',
107+
'.gitignore',
108+
'yarn.lock',
109+
'pnpm-lock.yaml',
110+
'package-lock.json',
111+
'pnpm-workspace.yaml',
112+
'Thumbs.db',
113+
],
114+
};
115+
}
116+
117+
/** Gets the common system instructions for all agents. */
118+
protected getCommonInstructions(options: LlmGenerateFilesRequestOptions) {
119+
return [
120+
`# Important Rules`,
121+
`The following instructions dictate how you should behave. It is CRITICAL that you follow them AS CLOSELY AS POSSIBLE:`,
122+
`- Do NOT attempt to improve the existing code, only implement the user request.`,
123+
`- STOP once you've implemented the user request, do NOT try to clean up the project.`,
124+
`- You ARE NOT ALLOWED to install dependencies. Assume that all necessary dependencies are already installed.`,
125+
`- Do NOT clean up unused files.`,
126+
`- Do NOT run the dev server, use \`${options.context.buildCommand}\` to verify the build correctness instead.`,
127+
`- Do NOT use \`git\` or any other versioning software.`,
128+
`- Do NOT attempt to lint the project.`,
129+
'',
130+
`Following the rules is VERY important and should be done with the utmost care!`,
131+
'',
132+
'',
133+
options.context.systemInstructions,
134+
].join('\n');
135+
}
136+
89137
private resolveBinaryPath(binaryName: string): string {
90138
let dir = import.meta.dirname;
91139
let closestRoot: string | null = null;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import {LlmGenerateFilesRequestOptions, LlmRunner} from './llm-runner.js';
2+
import {join} from 'path';
3+
import {mkdirSync} from 'fs';
4+
import {writeFile} from 'fs/promises';
5+
import {BaseCliAgentRunner} from './base-cli-agent-runner.js';
6+
7+
const SUPPORTED_MODELS = ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite'];
8+
9+
/** Runner that generates code using the Gemini CLI. */
10+
export class GeminiCliRunner extends BaseCliAgentRunner implements LlmRunner {
11+
readonly id = 'gemini-cli';
12+
readonly displayName = 'Gemini CLI';
13+
readonly hasBuiltInRepairLoop = true;
14+
protected ignoredFilePatterns = ['**/GEMINI.md', '**/.geminiignore'];
15+
protected binaryName = 'gemini';
16+
17+
getSupportedModels(): string[] {
18+
return SUPPORTED_MODELS;
19+
}
20+
21+
protected getCommandLineFlags(options: LlmGenerateFilesRequestOptions): string[] {
22+
return [
23+
'--prompt',
24+
options.context.executablePrompt,
25+
'--model',
26+
options.model,
27+
// Skip all confirmations.
28+
'--approval-mode',
29+
'yolo',
30+
];
31+
}
32+
33+
protected async writeAgentFiles(options: LlmGenerateFilesRequestOptions): Promise<void> {
34+
const {context} = options;
35+
const ignoreFilePath = join(context.directory, '.geminiignore');
36+
const instructionFilePath = join(context.directory, 'GEMINI.md');
37+
const settingsDir = join(context.directory, '.gemini');
38+
39+
mkdirSync(settingsDir);
40+
41+
const commonIgnorePatterns = super.getCommonIgnorePatterns();
42+
const ignoreFileContent = [
43+
...commonIgnorePatterns.directories,
44+
...commonIgnorePatterns.files,
45+
].join('\n');
46+
47+
const promises: Promise<unknown>[] = [
48+
writeFile(ignoreFilePath, ignoreFileContent),
49+
writeFile(instructionFilePath, super.getCommonInstructions(options)),
50+
];
51+
52+
if (context.packageManager) {
53+
writeFile(
54+
join(settingsDir, 'settings.json'),
55+
this.getGeminiSettingsFile(context.packageManager, context.possiblePackageManagers),
56+
);
57+
}
58+
59+
await Promise.all(promises);
60+
}
61+
62+
private getGeminiSettingsFile(packageManager: string, possiblePackageManagers: string[]): string {
63+
const config = {
64+
excludeTools: [
65+
// Prevent Gemini from using version control and package
66+
// managers since doing so via prompting doesn't always work.
67+
'run_shell_command(git)',
68+
...possiblePackageManagers
69+
.filter(m => m !== packageManager)
70+
.map(m => `run_shell_command(${m})`),
71+
72+
// Note that we don't block all commands,
73+
// because the build commands also go through it.
74+
`run_shell_command(${packageManager} install)`,
75+
`run_shell_command(${packageManager} add)`,
76+
`run_shell_command(${packageManager} remove)`,
77+
`run_shell_command(${packageManager} update)`,
78+
`run_shell_command(${packageManager} list)`,
79+
],
80+
};
81+
82+
return JSON.stringify(config, null, 2);
83+
}
84+
}

runner/codegen/gemini-cli/gemini-cli-runner.ts

Lines changed: 0 additions & 64 deletions
This file was deleted.

runner/codegen/gemini-cli/gemini-files.ts

Lines changed: 0 additions & 73 deletions
This file was deleted.

runner/codegen/runner-creation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {UserFacingError} from '../utils/errors.js';
2-
import type {GeminiCliRunner} from './gemini-cli/gemini-cli-runner.js';
2+
import type {GeminiCliRunner} from './gemini-cli-runner.js';
33
import type {GenkitRunner} from './genkit/genkit-runner.js';
44

55
interface AvailableRunners {
@@ -22,7 +22,7 @@ export async function getRunnerByName<T extends RunnerName>(name: T): Promise<Av
2222
m => new m.GenkitRunner() as AvailableRunners[T],
2323
);
2424
case 'gemini-cli':
25-
return import('./gemini-cli/gemini-cli-runner.js').then(
25+
return import('./gemini-cli-runner.js').then(
2626
m => new m.GeminiCliRunner() as AvailableRunners[T],
2727
);
2828
default:

runner/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export {generateCodeAndAssess} from './orchestration/generate.js';
2424
export {groupSimilarReports} from './orchestration/grouping.js';
2525
export {type LlmRunner} from './codegen/llm-runner.js';
2626
export {GenkitRunner} from './codegen/genkit/genkit-runner.js';
27-
export {GeminiCliRunner} from './codegen/gemini-cli/gemini-cli-runner.js';
27+
export {GeminiCliRunner} from './codegen/gemini-cli-runner.js';
2828
export {getRunnerByName, type RunnerName} from './codegen/runner-creation.js';
2929
export {getEnvironmentByPath} from './configuration/environment-resolution.js';
3030
export {type Environment} from './configuration/environment.js';

0 commit comments

Comments
 (0)