co-mono/packages/coding-agent/examples/hooks/dirty-repo-guard.ts
Mario Zechner ddda8b124c refactor(coding-agent): fix compaction for branched sessions, consolidate hook context types
Compaction API:
- prepareCompaction() now takes (pathEntries, settings) only
- CompactionPreparation restructured: removed cutPoint/messagesToKeep/boundaryStart, added turnPrefixMessages/isSplitTurn/previousSummary/fileOps/settings
- compact() now takes (preparation, model, apiKey, customInstructions?, signal?)
- Fixed token overflow by using getPath() instead of getEntries()

Hook types:
- HookEventContext renamed to HookContext
- HookCommandContext removed, RegisteredCommand.handler takes (args, ctx)
- HookContext now includes model field
- SessionBeforeCompactEvent: removed previousCompactions/model, added branchEntries
- SessionBeforeTreeEvent: removed model (use ctx.model)
- HookRunner.initialize() added for modes to set up callbacks
2025-12-31 02:24:24 +01:00

55 lines
1.5 KiB
TypeScript

/**
* Dirty Repo Guard Hook
*
* Prevents session changes when there are uncommitted git changes.
* Useful to ensure work is committed before switching context.
*/
import type { HookAPI, HookContext } from "@mariozechner/pi-coding-agent/hooks";
async function checkDirtyRepo(pi: HookAPI, ctx: HookContext, action: string): Promise<{ cancel: boolean } | undefined> {
// Check for uncommitted changes
const { stdout, code } = await pi.exec("git", ["status", "--porcelain"]);
if (code !== 0) {
// Not a git repo, allow the action
return;
}
const hasChanges = stdout.trim().length > 0;
if (!hasChanges) {
return;
}
if (!ctx.hasUI) {
// In non-interactive mode, block by default
return { cancel: true };
}
// Count changed files
const changedFiles = stdout.trim().split("\n").filter(Boolean).length;
const choice = await ctx.ui.select(`You have ${changedFiles} uncommitted file(s). ${action} anyway?`, [
"Yes, proceed anyway",
"No, let me commit first",
]);
if (choice !== "Yes, proceed anyway") {
ctx.ui.notify("Commit your changes first", "warning");
return { cancel: true };
}
}
export default function (pi: HookAPI) {
pi.on("session_before_new", async (_event, ctx) => {
return checkDirtyRepo(pi, ctx, "new session");
});
pi.on("session_before_switch", async (_event, ctx) => {
return checkDirtyRepo(pi, ctx, "switch session");
});
pi.on("session_before_branch", async (_event, ctx) => {
return checkDirtyRepo(pi, ctx, "branch");
});
}