FileGenerator
ARFA Generator runs iteratively through emit → write → cleanup, allowing multiple plugins to modify files in sequence until the codebase stabilizes. Checkers then validate the result, followed by the remaining lifecycle steps.
ts | CI mode; defaults to |
ts | The root directory for plugin glob patterns and watch mode. The generator does not modify files outside it |
ts | Plugins that propose files to write. Executed in declaration order on each iteration |
ts | Checks that run after the plugins and all cascading changes have completed |
ts | A function for formatting the content of files proposed by plugins. Called before comparing the content with the file on disk and writing it |
ts | Common text prepended to each generated file unless the plugin sets |
ts | The absolute path of the JSONL file containing run results. History is neither read nor written when Place the file outside |
API
runFullCycle
Runs one full generation cycle, including checks and lifecycle callbacks.
runFullCycle(params?: {
onStart?: () => void;
afterCheckers?: (args: TypeAfterCheckersArgs) => void | Promise<void>;
afterFinish?: (args: TypeAfterFinishArgs) => void | Promise<void>;
}): Promise<void>
type TypeAfterCheckersArgs = {
changedFiles: Array<{ relativePath: string; absolutePath: string }>;
deletedFiles: Array<{ relativePath: string; absolutePath: string }>;
isFullGeneration: boolean;
};
type TypeAfterFinishArgs = TypeAfterCheckersArgs & { logs: TypeLogs };onStart is a synchronous callback that runs before the plugins.
afterCheckers is a synchronous or asynchronous callback that runs after the plugins and checkers finish. It can be used to run a bundler or other operations that do not modify code.
afterFinish is a synchronous or asynchronous callback that runs after afterCheckers. It receives the same data and logs, including errors from the previous callback. Use it for log output and custom logic, such as signaling the browser to reload or starting an LLM agent. Operations that modify code are allowed here: they trigger a new generation cycle, but only in watch mode. Errors from this callback are printed directly to the console, bypassing logs, and saved to JSONL.
changedFiles contains existing files changed by plugins or by the user during the watch process, while deletedFiles contains deleted files.
isFullGeneration is true for this method.
startWatch
Starts watching for changes in rootDir without running an initial generation. When ci: true, it immediately returns a resolved Promise.
startWatch(params?: {
onStart?: () => void;
afterCheckers?: (args: TypeAfterCheckersArgs) => void | Promise<void>;
afterFinish?: (args: TypeAfterFinishArgs) => void | Promise<void>;
aggregationTimeout?: number;
}): Promise<void>onStart is a synchronous callback called after changes have been collected over aggregationTimeout, before the plugins run.
isFullGeneration is false for this method. File changes during afterFinish trigger a new full generation cycle.
aggregationTimeout (in milliseconds, 0 by default) acts as a debounce to prevent unnecessary runs when changes occur in quick succession or when IDE File Watchers are configured (for example, to reformat a file on save).
startWatch watches for changes only within rootDir. Changes to external files such as tsconfig, .env, or dependencies do not restart generation. Neither do changes to the generator’s own configuration — these require manually restarting the process.
INFO
This is a long-running process that almost never throws exceptions. Instead, it records errors in logs for console output and in JSONL, so you can fix code errors on the fly.
stopWatch
Stops watching, but does not cancel a generation cycle already in progress.
stopWatch(): Promise<void>getControlledFiles
Returns a list of existing files controlled by the generator, including obsolete files that plugins plan to delete during the cleanup phase.
getControlledFiles(): Promise<Array<{
relativePath: string;
absolutePath: string;
}>>const files = await generator.getControlledFiles();
console.log(files.map(({relativePath}) => relativePath));removeGeneratedFiles
Calls the configured plugins, but deletes their files instead of creating them. Useful when changing the configuration. Empty folders created by plugins may remain: folders are not deleted recursively.
removeGeneratedFiles(): Promise<void>Example: Codex in afterFinish
In watch mode, // AGENT in changed files triggers Codex, which completes the task and removes the marker to prevent an infinite loop
async afterFinish({ changedFiles }) {
const codex = new Codex();
const thread = codex.startThread();
for (const { absolutePath } of changedFiles || []) {
const content = fs.readFileSync(absolutePath, 'utf8');
const prompt = content.match(/\/\/ AGENT (.+)/)?.[1];
if (!prompt) continue;
await thread.run(`
File: ${absolutePath}
Task: ${prompt}
Edit the file directly and remove the completed // AGENT command.
`);
}
},
Run Sandbox
[ARFA-GEN] finished in 0ms