diff --git a/.agents/hooks/lf.ts b/.agents/hooks/lf.ts new file mode 100644 index 0000000..36b09f1 --- /dev/null +++ b/.agents/hooks/lf.ts @@ -0,0 +1,4 @@ +import { runHook } from "./run-hook"; + +const exitCode = runHook(["bun", "run", "lf"]); +process.exit(exitCode); diff --git a/.agents/hooks/madge.ts b/.agents/hooks/madge.ts new file mode 100644 index 0000000..cb713d1 --- /dev/null +++ b/.agents/hooks/madge.ts @@ -0,0 +1,4 @@ +import { runHook } from "./run-hook"; + +const exitCode = runHook(["bun", "run", "madge"]); +process.exit(exitCode); diff --git a/.agents/hooks/run-hook.ts b/.agents/hooks/run-hook.ts index 37208f6..a2bc877 100644 --- a/.agents/hooks/run-hook.ts +++ b/.agents/hooks/run-hook.ts @@ -17,6 +17,25 @@ function writeFailure(format: HookFormat, output: string): void { process.stderr.write(output); } +function writeSuccess(format: HookFormat, output: string): number { + if (format === "codex") return 0; + process.stdout.write(JSON.stringify({ output })); + return 0; +} + +// Fails closed: a non-zero git status (not a repo, git missing, index locked) +// reports changes so the gates still run. Only a confirmed-empty status skips. +// --porcelain=v1 lists untracked files too, so a new file counts as a change. +function worktreeHasChanges(): boolean { + const r = spawnSync("git status --porcelain=v1", { + encoding: "utf8", + shell: true, + stdio: ["ignore", "pipe", "pipe"], + }); + if ((r.status ?? -1) !== 0) return true; + return (r.stdout ?? "").trim().length > 0; +} + // Helper for agent hook scripts. Each hook file (lint.ts, test.ts, ...) is a // thin wrapper that calls runHook(["bun", "run", "