// commit-emission.ts — shared commit-emission shim // // Provides a single performCommit function that routes commit emission // through either bypass (execSync — byte-identical to upstream pi-committer) // or review (write message file + return pending) based on the mode argument. // // This module imports ONLY node builtins (child_process, fs, path) — zero // project imports → no circular dependency risk, safe for worker fork. import { execSync } from "node:child_process"; import { writeFileSync } from "node:fs"; import * as path from "node:path"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export type CommitMode = "review" | "bypass"; export interface CommitEmissionContext { /** Repository working directory. */ dir: string; /** Final commit message (already validated). */ message: string; } export interface CommitEmissionResult { /** true if the commit emission succeeded (committed OR handed off). */ success: boolean; /** Commit hash. Present only on bypass success (committed). */ hash?: string; /** true in review-mode — commit handed off to the model. */ pending?: boolean; /** Path to the written message file (review-mode only). */ messageFile?: string; } // --------------------------------------------------------------------------- // performCommit // --------------------------------------------------------------------------- /** * Emit a commit in the given mode. * * - bypass: execSync("git commit -F -") — byte-identical to upstream pi-committer. * - review: write message to .git/gentle_committer_pending_msg, return pending. * * SYNCHRONOUS — both branches complete synchronously (execSync or writeFileSync). * No async needed; keeping it sync avoids touching call-site signatures. */ export function performCommit( mode: CommitMode, ctx: CommitEmissionContext, ): CommitEmissionResult { if (mode === "review") { // Review branch: write the message file and return pending. // The caller (tool handler) returns a directive to the model, which then // runs `git commit -F ` through the bash tool so gentle-ai's gate fires. const messageFile = path.join( ctx.dir, ".git", "gentle_committer_pending_msg", ); try { writeFileSync(messageFile, ctx.message, "utf-8"); } catch (err) { throw new Error( `Failed to write review-mode message file: ${ err instanceof Error ? err.message : String(err) }`, ); } return { success: true, pending: true, messageFile }; } // Bypass branch: byte-identical to the original execSync call at index.ts:1641. // Hash extraction is left to the CALLER (getHeadHash), keeping performCommit // focused on emission only — matches the current inline pattern exactly. // (The execSync call args are unchanged; the try/catch only adds error context.) try { execSync("git commit -F -", { cwd: ctx.dir, encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"], input: ctx.message, }); } catch (err) { throw new Error( `Failed to commit (bypass mode): ${ err instanceof Error ? err.message : String(err) }`, ); } return { success: true }; }