feat(FN-692): add setupScript setting for automatic worktree initialization

- Add `setupScript` property to ProjectSettings to reference a named script
- Execute the referenced setup script after worktreeInitCommand on fresh worktrees
- Log success/failure of setup script execution; warn and skip if script name not found
- Skip setup script for pooled/recycled worktrees (fresh only)
- Add changeset (minor) for the new setting
This commit is contained in:
gsxdsm
2026-04-01 09:26:20 -07:00
parent 5e949ff4aa
commit 57d996fb9b
3 changed files with 33 additions and 0 deletions

View File

@@ -673,6 +673,12 @@ export interface ProjectSettings {
* Key is the script name, value is the shell command to execute.
* Script names must be alphanumeric with hyphens and underscores only. */
scripts?: Record<string, string>;
/** Name of a script from the `scripts` map to run automatically when
* fresh worktrees are created. Runs after `worktreeInitCommand` (if both
* are configured). Has no effect on pooled/recycled worktrees.
* If the referenced script doesn't exist, a warning is logged and
* the task continues. */
setupScript?: string;
}
/**
@@ -739,6 +745,7 @@ export const DEFAULT_PROJECT_SETTINGS: ProjectSettings = {
titleSummarizerProvider: undefined,
titleSummarizerModelId: undefined,
scripts: {},
setupScript: undefined,
};
/**
@@ -802,6 +809,7 @@ export const PROJECT_SETTINGS_KEYS: ReadonlyArray<keyof ProjectSettings> = [
"titleSummarizerProvider",
"titleSummarizerModelId",
"scripts",
"setupScript",
] as const;
export interface BoardConfig {

View File

@@ -448,6 +448,26 @@ export class TaskExecutor {
await this.store.logEntry(task.id, `Worktree init command failed: ${message}`);
}
}
// Run setup script for fresh worktrees (after worktreeInitCommand)
if (settings.setupScript) {
const scriptCommand = settings.scripts?.[settings.setupScript];
if (scriptCommand) {
try {
execSync(scriptCommand, {
cwd: worktreePath,
stdio: "pipe",
timeout: 120_000,
});
await this.store.logEntry(task.id, `Setup script '${settings.setupScript}' completed`, scriptCommand);
} catch (err: any) {
const message = err.stderr?.toString() || err.message || "Unknown error";
await this.store.logEntry(task.id, `Setup script '${settings.setupScript}' failed: ${message}`);
}
} else {
await this.store.logEntry(task.id, `Setup script '${settings.setupScript}' not found in scripts map — skipping`);
}
}
}
} else if (task.worktree) {
// Task already had a worktree assigned and it exists on disk — reuse it