feat: add plan2code-bot with --resume capability and state cleanup

Add the autonomous workflow test runner (plan2code-bot) that uses the
Claude Agent SDK to run plan2code end-to-end. Includes --resume flag
to continue incomplete runs from saved state, and automatic state file
cleanup on successful completion.
This commit is contained in:
2026-03-01 21:30:45 -08:00
parent 63656d339d
commit c92865c395
22 changed files with 2267 additions and 3 deletions
+48
View File
@@ -0,0 +1,48 @@
import fs from 'fs-extra';
import path from 'path';
import type { BotState } from './types.js';
const STATE_FILE = '.plan2code-bot-state.json';
export function saveState(state: BotState): void {
const filePath = path.join(state.config.projectDir, STATE_FILE);
fs.writeJsonSync(filePath, state, { spaces: 2 });
}
export function loadState(projectDir: string): BotState | null {
const filePath = path.join(projectDir, STATE_FILE);
if (!fs.existsSync(filePath)) {
return null;
}
return fs.readJsonSync(filePath) as BotState;
}
export function deleteState(projectDir: string): void {
const filePath = path.join(projectDir, STATE_FILE);
if (fs.existsSync(filePath)) {
fs.removeSync(filePath);
}
}
/**
* Search for an existing state file in workDir (enhancement mode)
* or in immediate subdirectories (new-project mode).
*/
export function findExistingState(workDir: string): BotState | null {
// Enhancement mode: state is in workDir directly
const direct = loadState(workDir);
if (direct) return direct;
// New-project mode: state is in a subdirectory
try {
for (const entry of fs.readdirSync(workDir, { withFileTypes: true })) {
if (entry.isDirectory()) {
const sub = loadState(path.join(workDir, entry.name));
if (sub) return sub;
}
}
} catch {
// workDir not readable — ignore
}
return null;
}