#!/usr/bin/env bun import { accessSync, constants, existsSync, realpathSync } from 'node:fs'; import { dirname, join, normalize, resolve, sep } from 'node:path'; import { fileURLToPath } from 'node:url'; import process from 'node:process'; import { analyzeGitExecution, evaluateGitPolicies } from '../scripts/git-policy'; const commandArgs = ['git', ...process.argv.slice(2)]; const context = analyzeGitExecution(commandArgs, process.cwd()); const gitBinary = findRealGitBinary(); if (!gitBinary) { console.error('Unable to locate the system git binary outside the Sweetistics git shim.'); process.exit(1); } if (!shouldEnforcePolicies(context)) { process.exit(await runGitPassthrough(gitBinary)); } const evaluation = evaluateGitPolicies(context); if (evaluation.requiresCommitHelper) { console.error( 'Direct git add/commit is disabled. Use ./scripts/committer "chore(runner): describe change" "scripts/runner.ts" instead—see AGENTS.md and ./scripts/committer for details. The helper auto-stashes unrelated files before committing.' ); process.exit(1); } if (evaluation.requiresExplicitConsent) { if (process.env.RUNNER_THE_USER_GAVE_ME_CONSENT === '1') { if (process.env.RUNNER_DEBUG === '1') { console.error('[git-shim] Proceeding with guarded git command because RUNNER_THE_USER_GAVE_ME_CONSENT=1.'); } } else { console.error( `Using git ${context.subcommand ?? ''} requires consent. Set RUNNER_THE_USER_GAVE_ME_CONSENT=1 after verifying with the user, or ask them explicitly before proceeding.` ); process.exit(1); } } if (evaluation.isDestructive) { console.error( 'Destructive git commands require explicit user consent. If you are trying to revert an individual file, carefully undo your changes from memory. Other agents work in this repository and a blanket reset would risk overriding their non-committed work.' ); process.exit(1); } process.exit(await runGitPassthrough(gitBinary)); function findRealGitBinary(): string | null { const scriptDir = normalize(dirname(fileURLToPath(import.meta.url))); const pathCandidates = (process.env.PATH ?? '') .split(':') .map((segment) => segment.trim()) .filter((segment) => segment.length > 0); for (const segment of pathCandidates) { const normalized = normalize(resolve(segment)); if (normalized === scriptDir) { continue; } const candidate = join(normalized, 'git'); if (isExecutable(candidate)) { return candidate; } } for (const fallback of ['/usr/bin/git', '/usr/local/bin/git', '/opt/homebrew/bin/git']) { if (isExecutable(fallback)) { return fallback; } } return null; } function isExecutable(candidate: string): boolean { if (!existsSync(candidate)) { return false; } try { accessSync(candidate, constants.X_OK); return true; } catch { return false; } } async function runGitPassthrough(binary: string): Promise { const proc = Bun.spawn([binary, ...process.argv.slice(2)], { stdin: 'inherit', stdout: 'inherit', stderr: 'inherit', env: process.env, }); return proc.exited; } function shouldEnforcePolicies(context: ReturnType): boolean { const scriptDir = dirname(fileURLToPath(import.meta.url)); let repoRoot: string | null = null; try { repoRoot = realpathSync(resolve(scriptDir, '..')); } catch { return false; } const candidatePaths = collectCandidatePaths(context); for (const path of candidatePaths) { if (path && isPathInsideRepo(path, repoRoot)) { return true; } } return false; } function collectCandidatePaths(context: ReturnType): Set { const candidates = new Set(); if (context.workDir) { candidates.add(context.workDir); } const argv = toArray(context.invocation?.argv); for (let index = 1; index < argv.length; index += 1) { const token = argv[index]; if (token === '-C' || token === '--git-dir' || token === '--work-tree') { const value = argv[index + 1]; if (value) { candidates.add(resolve(context.workDir, value)); } index += 1; continue; } if (token.startsWith('-C') && token.length > 2) { candidates.add(resolve(context.workDir, token.slice(2))); continue; } if (token.startsWith('--git-dir=')) { candidates.add(resolve(context.workDir, token.slice('--git-dir='.length))); continue; } if (token.startsWith('--work-tree=')) { candidates.add(resolve(context.workDir, token.slice('--work-tree='.length))); } } return candidates; } function isPathInsideRepo(candidate: string, repoRoot: string): boolean { try { const realCandidate = realpathSync(candidate); if (realCandidate === repoRoot) { return true; } return realCandidate.startsWith(repoRoot.endsWith(sep) ? repoRoot : `${repoRoot}${sep}`); } catch { return false; } } function toArray(value: T | T[] | null | undefined): T[] { if (value == null) { return []; } if (Array.isArray(value)) { return value; } return [value]; }