mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
feat(offload): implement unified control plane with log tailing and flexible attachment
This commit is contained in:
@@ -2,24 +2,73 @@ import { TaskRunner } from '../TaskRunner.js';
|
||||
import path from 'path';
|
||||
import { spawnSync } from 'child_process';
|
||||
|
||||
import { TaskRunner } from '../TaskRunner.js';
|
||||
import path from 'path';
|
||||
import { spawnSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
|
||||
export async function runImplementPlaybook(issueNumber: string, workDir: string, policyPath: string, geminiBin: string) {
|
||||
const runner = new TaskRunner(
|
||||
path.join(workDir, `.gemini/logs/offload-issue-${issueNumber}`),
|
||||
`🚀 Offload | IMPLEMENT | Issue #${issueNumber}`
|
||||
);
|
||||
console.log(`🚀 Offload | IMPLEMENT (Supervisor Loop) | Issue #${issueNumber}`);
|
||||
|
||||
const ghView = spawnSync('gh', ['issue', 'view', issueNumber, '--json', 'title,body', '-q', '{title:.title,body:.body}'], { shell: true });
|
||||
const meta = JSON.parse(ghView.stdout.toString());
|
||||
const branchName = `impl/${issueNumber}-${meta.title.toLowerCase().replace(/[^a-z0-9]/g, '-')}`.slice(0, 50);
|
||||
|
||||
console.log(`🔍 Fetching metadata for Issue #${issueNumber}...`);
|
||||
const ghView = spawnSync('gh', ['issue', 'view', issueNumber, '--json', 'title', '-q', '.title'], { shell: true });
|
||||
const title = ghView.stdout.toString().trim() || `issue-${issueNumber}`;
|
||||
const branchName = `impl/${issueNumber}-${title.toLowerCase().replace(/[^a-z0-9]/g, '-')}`.slice(0, 50);
|
||||
// 1. Initial Research & Test Creation
|
||||
console.log('\n🧠 Phase 1: Research & Reproduction...');
|
||||
spawnSync(geminiBin, [
|
||||
'--policy', policyPath, '--cwd', workDir,
|
||||
'-p', `Research Issue #${issueNumber}: "${meta.title}".
|
||||
Description: ${meta.body}.
|
||||
ACTION: Create a NEW Vitest test file in 'tests/repro_issue_${issueNumber}.test.ts' that demonstrates the issue or feature.
|
||||
Ensure this test fails currently.`
|
||||
], { stdio: 'inherit' });
|
||||
|
||||
runner.register([
|
||||
{ id: 'branch', name: 'Create Branch', cmd: `git checkout -b ${branchName}` },
|
||||
{ id: 'research', name: 'Codebase Research', cmd: `${geminiBin} --policy ${policyPath} -p "Research the requirements for issue #${issueNumber} using 'gh issue view ${issueNumber}'. Map out the files that need to be changed."`, dep: 'branch' },
|
||||
{ id: 'implement', name: 'Implementation', cmd: `${geminiBin} --policy ${policyPath} -p "Implement the changes for issue #${issueNumber} based on your research. Ensure all code follows project standards."`, dep: 'research' },
|
||||
{ id: 'verify', name: 'Verification', cmd: `npm run build && npm test`, dep: 'implement' },
|
||||
{ id: 'pr', name: 'Create Pull Request', cmd: `git add . && git commit -m "feat: implement issue #${issueNumber}" && git push origin ${branchName} && gh pr create --title "${title}" --body "Closes #${issueNumber}"`, dep: 'verify' }
|
||||
]);
|
||||
// 2. The Self-Healing Loop
|
||||
let attempts = 0;
|
||||
const maxAttempts = 5;
|
||||
let success = false;
|
||||
|
||||
return runner.run();
|
||||
console.log('\n🛠️ Phase 2: Implementation Loop...');
|
||||
while (attempts < maxAttempts && !success) {
|
||||
attempts++;
|
||||
console.log(`\n👉 Attempt ${attempts}/${maxAttempts}...`);
|
||||
|
||||
// Run the specific repro test
|
||||
const testRun = spawnSync('npx', ['vitest', 'run', `tests/repro_issue_${issueNumber}.test.ts`], { cwd: workDir });
|
||||
|
||||
if (testRun.status === 0) {
|
||||
console.log('✅ Reproduction test PASSED!');
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
console.log('❌ Test failed. Asking Gemini to fix the implementation...');
|
||||
const testError = testRun.stdout.toString() + testRun.stderr.toString();
|
||||
|
||||
spawnSync(geminiBin, [
|
||||
'--policy', policyPath, '--cwd', workDir,
|
||||
'-p', `The reproduction test for Issue #${issueNumber} is still failing.
|
||||
ERROR OUTPUT:
|
||||
${testError.slice(-2000)}
|
||||
|
||||
ACTION: Modify the source code to fix this error and make the test pass.
|
||||
Do not modify the test itself unless it has a syntax error.`
|
||||
], { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
// 3. Final Verification
|
||||
if (success) {
|
||||
console.log('\n🧪 Phase 3: Final Verification...');
|
||||
const finalCheck = spawnSync('npm', ['test'], { cwd: workDir, stdio: 'inherit' });
|
||||
if (finalCheck.status === 0) {
|
||||
console.log('\n🎉 Implementation complete and verified!');
|
||||
spawnSync('git', ['add', '.'], { cwd: workDir });
|
||||
spawnSync('git', ['commit', '-m', `feat: implement issue #${issueNumber}`], { cwd: workDir });
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
console.error('\n❌ Supervisor: Failed to reach a passing state within retry limit.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user