// tools/git/git-helpers.ts — Shared git utilities
import { execFile as execFileCb } from 'node:child_process';
import { promisify } from 'node:util';
import { knowledgeStore } from '../../storage/index.js';
export const execFile = promisify(execFileCb);
/** Default branch prefixes by spec type. */
export const DEFAULT_BRANCH_PREFIXES = {
    feature: 'feat',
    refactor: 'refactor',
    bugfix: 'fix',
    infra: 'infra',
    docs: 'docs',
    project: 'project',
};
/** Default protected branches. */
export const DEFAULT_PROTECTED_BRANCHES = ['main', 'master', 'develop', 'production'];
/** Default staleness threshold (commits behind). */
export const DEFAULT_STALENESS_THRESHOLD = 50;
/** Execute a git command in the project directory. */
export async function git(projectPath, args) {
    try {
        const result = await execFile('git', args, {
            cwd: projectPath,
            maxBuffer: 1024 * 1024 * 10,
            timeout: 30_000,
        });
        return { stdout: result.stdout, stderr: result.stderr };
    }
    catch (err) {
        const error = err;
        throw new Error(`git ${args.join(' ')} failed: ${error.stderr ?? error.message ?? 'unknown error'}`, { cause: err });
    }
}
/** Resolve the project path from project knowledge. */
export async function resolveProjectPath(projectId) {
    const knowledge = await knowledgeStore.getKnowledge(projectId);
    if (!knowledge) {
        throw new Error(`Project not found. Run init_project first.`);
    }
    return knowledge.projectPath;
}
/** Generate a slug from a spec title. */
export function slugify(title) {
    return title
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/^-|-$/g, '')
        .slice(0, 50);
}
/** Get merged git config with defaults. */
export function mergeConfig(config) {
    return {
        branchPrefix: config?.branchPrefix ?? DEFAULT_BRANCH_PREFIXES,
        commitFormat: config?.commitFormat ?? 'conventional',
        autoCreateBranch: config?.autoCreateBranch ?? false,
        requireSpecInCommit: config?.requireSpecInCommit ?? true,
        protectedBranches: config?.protectedBranches ?? DEFAULT_PROTECTED_BRANCHES,
        customCommitTemplate: config?.customCommitTemplate,
        baseBranch: config?.baseBranch ?? 'develop',
        stalenessThreshold: config?.stalenessThreshold ?? DEFAULT_STALENESS_THRESHOLD,
    };
}
/** Resolve base branch (develop → main → master → ''). */
export async function resolveBaseBranch(projectPath, preferred) {
    const candidates = preferred
        ? [preferred, 'develop', 'main', 'master']
        : ['develop', 'main', 'master'];
    for (const branch of candidates) {
        try {
            await git(projectPath, ['rev-parse', '--verify', branch]);
            return branch;
        }
        catch {
            // try next
        }
    }
    return '';
}
/** Count commits that current branch is behind a base branch. */
export async function countCommitsBehind(projectPath, baseBranch) {
    try {
        const { stdout } = await git(projectPath, ['rev-list', '--count', `HEAD..${baseBranch}`]);
        return parseInt(stdout.trim(), 10) || 0;
    }
    catch {
        /* v8 ignore start */
        return 0;
        /* v8 ignore end */
    }
}
/** Detect git flow type by inspecting branches. */
export async function detectGitFlowType(projectPath) {
    try {
        // Check if develop branch exists → gitflow
        await git(projectPath, ['rev-parse', '--verify', 'develop']);
        return 'gitflow';
    }
    catch {
        // Check if frequent commits to main without PRs → trunk-based
        try {
            const { stdout } = await git(projectPath, ['branch', '--list', 'main', 'master']);
            if (stdout.trim()) {
                return 'github-flow';
            }
        }
        catch {
            // ignore
        }
        return 'trunk-based';
    }
}
//# sourceMappingURL=git-helpers.js.map