Skip to content

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?: boolean

CI mode; defaults to false. When true, startWatch() calls and the historyPath option are ignored, and errors cause an exception to be thrown

ts
rootDir: string

The root directory for plugin glob patterns and watch mode. The generator does not modify files outside it

ts
plugins: Array<BasePlugin>

Plugins that propose files to write. Executed in declaration order on each iteration

ts
checkers?: Array<BaseChecker>

Checks that run after the plugins and all cascading changes have completed

ts
format?: (files: {
  plugin: string;
  content: string;
  filePath: string;
}) => {
  plugin: string;
  content: string;
  filePath: string;
} | Promise<{
  plugin: string;
  content: string;
  filePath: string;
}>

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
headerTemplate?: string

Common text prepended to each generated file unless the plugin sets skipHeader

ts
historyPath?: string

The absolute path of the JSONL file containing run results. History is neither read nor written when ci: true or when this option is omitted

Place the file outside rootDir so that updates do not trigger watch events

API ​

runFullCycle ​

Runs one full generation cycle, including checks and lifecycle callbacks.

ts
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.

ts
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.

ts
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.

ts
getControlledFiles(): Promise<Array<{
  relativePath: string;
  absolutePath: string;
}>>
ts
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.

ts
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

ts
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
USER: fibonacci.ts was created with content:
ts
ARFA Generator shows logs:
txt
[ARFA-GEN] finished in 0ms
afterFinish updated project files:
ts

MIT License