/**
 * Server-side git operations service.
 *
 * Executes git and `gh` CLI commands in the requested working directory.
 * Adapted from the t3code GitService/GitManager pattern but running
 * directly through child_process on the gateway.
 */
export type GitStackedAction = "commit" | "commit_push" | "commit_push_pr";
export interface GitStatusFile {
    path: string;
    insertions: number;
    deletions: number;
    /** 'A' = added, 'M' = modified, 'D' = deleted, 'R' = renamed, '?' = untracked */
    status: string;
}
export interface GitStatusPr {
    number: number;
    title: string;
    url: string;
    baseBranch: string;
    headBranch: string;
    state: "open" | "closed" | "merged";
}
export interface GitStatusResult {
    branch: string | null;
    hasWorkingTreeChanges: boolean;
    index: {
        files: GitStatusFile[];
        insertions: number;
        deletions: number;
    };
    workingTree: {
        files: GitStatusFile[];
        insertions: number;
        deletions: number;
    };
    hasUpstream: boolean;
    aheadCount: number;
    behindCount: number;
    pr: GitStatusPr | null;
    /** Whether GitHub CLI (`gh`) is installed and authenticated. */
    ghAvailable: boolean;
    /** Hosting provider inferred from the preferred remote URL. */
    prProvider: GitRemoteProvider;
    /** HTTPS remote URL for the primary remote (origin/Jait/etc.) */
    remoteUrl: string | null;
}
export interface GitBranch {
    name: string;
    isRemote: boolean;
    current: boolean;
    isDefault: boolean;
    worktreePath: string | null;
}
export interface GitListBranchesResult {
    branches: GitBranch[];
    isRepo: boolean;
}
export interface GitStepResult {
    commit: {
        status: "created" | "skipped_no_changes";
        commitSha?: string;
        subject?: string;
    };
    push: {
        status: "pushed" | "skipped_not_requested" | "skipped_up_to_date" | "skipped_no_remote" | "failed";
        branch?: string;
        upstreamBranch?: string;
        setUpstream?: boolean;
        createPrUrl?: string;
        error?: string;
    };
    branch: {
        status: "created" | "skipped_not_requested";
        name?: string;
    };
    pr: {
        status: "created" | "opened_existing" | "skipped_not_requested" | "skipped_no_remote";
        url?: string;
        number?: number;
        baseBranch?: string;
        headBranch?: string;
        title?: string;
    };
}
export interface GitDiffResult {
    diff: string;
    files: string[];
    hasChanges: boolean;
}
export interface GitDiffStatsResult {
    files: number;
    insertions: number;
    deletions: number;
    hasChanges: boolean;
}
export interface FileDiffEntry {
    path: string;
    /** Original (HEAD) content, empty for new files */
    original: string;
    /** Current working-tree content, empty for deleted files */
    modified: string;
    /** 'A' = added, 'M' = modified, 'D' = deleted, 'R' = renamed, '?' = untracked */
    status: string;
}
export interface GitPullResult {
    status: "pulled" | "skipped_up_to_date";
    branch: string;
    upstreamBranch: string | null;
}
export interface GitSyncResult {
    branch: string;
    upstreamBranch: string | null;
    pull: {
        status: "pulled" | "skipped_up_to_date" | "skipped_no_upstream";
    };
    push: {
        status: "pushed" | "skipped_up_to_date" | "skipped_no_remote";
    };
}
export interface GitVersionBumpResult {
    previousVersion: string;
    nextVersion: string;
    files: string[];
}
export interface GitCommitFlowResult {
    version: GitVersionBumpResult;
    sync: {
        status: "pulled" | "skipped_up_to_date" | "skipped_no_upstream";
        branch: string | null;
        upstreamBranch: string | null;
    };
    git: GitStepResult;
}
export interface GitFetchResult {
    status: "fetched" | "skipped_no_remote";
    remote: string | null;
    allRemotes: boolean;
}
export interface GitWorktreeResult {
    path: string;
    branch: string;
}
export interface GitIdentity {
    name: string | null;
    email: string | null;
}
export interface PrCheck {
    name: string;
    state: string;
    conclusion: string;
    startedAt: string;
    completedAt: string;
    detailsUrl: string;
}
export type GitRemoteProvider = "github" | "azure-devops" | "gitlab" | "bitbucket" | "gitea" | "unknown" | "none";
export interface ParsedRemote {
    provider: Exclude<GitRemoteProvider, "none" | "unknown">;
    host: string;
    normalizedUrl: string;
    repo: string;
    owner?: string;
    organization?: string;
    project?: string;
}
export declare function parseGitRemote(raw: string | null): ParsedRemote | null;
export declare function detectGitRemoteProvider(raw: string | null): GitRemoteProvider;
export declare class GitService {
    isRepo(cwd: string): Promise<boolean>;
    init(cwd: string): Promise<void>;
    getIdentity(cwd: string): Promise<GitIdentity>;
    setIdentity(cwd: string, name: string, email: string): Promise<GitIdentity>;
    status(cwd: string, requestedBranch?: string, githubToken?: string): Promise<GitStatusResult>;
    /** Fetch CI check statuses for a PR branch via `gh pr checks`. */
    prChecks(cwd: string, branch: string): Promise<PrCheck[]>;
    listBranches(cwd: string): Promise<GitListBranchesResult>;
    pull(cwd: string): Promise<GitPullResult>;
    bumpProjectPackageVersions(cwd: string): Promise<GitVersionBumpResult>;
    runVersionBumpCommitPushFlow(cwd: string, githubToken?: string): Promise<GitCommitFlowResult>;
    sync(cwd: string): Promise<GitSyncResult>;
    fetch(cwd: string, allRemotes?: boolean): Promise<GitFetchResult>;
    /** Discard working-tree changes for specific files, or all files if paths is empty. */
    discardChanges(cwd: string, paths?: string[]): Promise<{
        discardedCount: number;
    }>;
    /** Stage specific files, or all if paths is empty. */
    stage(cwd: string, paths?: string[]): Promise<void>;
    /** Unstage specific files, or all if paths is empty. */
    unstage(cwd: string, paths?: string[]): Promise<void>;
    runStackedAction(cwd: string, action: GitStackedAction, commitMessage?: string, featureBranch?: boolean, baseBranch?: string, githubToken?: string, expectedBranch?: string): Promise<GitStepResult>;
    checkout(cwd: string, branch: string): Promise<void>;
    createBranch(cwd: string, branch: string): Promise<void>;
    /** Delete a local branch. No-ops if the branch doesn't exist. */
    deleteBranch(cwd: string, branch: string): Promise<void>;
    /**
     * Clone a GitHub repo to a local path for gateway-side operation.
     * Clones live under ~/.jait/clones/{repoName}.
     * If the clone already exists, fetches the latest instead.
     * Returns the path to the clone.
     */
    cloneOrFetch(repoUrl: string, repoName: string, defaultBranch?: string): Promise<string>;
    /**
     * Create a git worktree for a new branch.
     * Worktrees live under ~/.jait/worktrees/{repoName}/{sanitizedBranch}.
     * Uses `git worktree add -b <newBranch> <path> <baseBranch>`.
     */
    createWorktree(cwd: string, baseBranch: string, newBranch: string, customPath?: string): Promise<GitWorktreeResult>;
    /** Remove a git worktree. */
    removeWorktree(cwd: string, worktreePath: string, force?: boolean): Promise<void>;
    /**
     * Clean up a worktree directory created for a thread.
     * Resolves the main repo root, runs `git worktree remove --force`,
     * deletes the associated branch, and falls back to deleting the
     * directory if the worktree remove fails.
     * No-ops silently when the path is not a worktree or doesn't exist.
     */
    cleanupWorktree(worktreePath: string, branch?: string | null): Promise<void>;
    cleanupWorktreeWithOptions(worktreePath: string, options?: {
        branch?: string | null;
        preserveBranch?: boolean;
    }): Promise<void>;
    /** Get the top-level git directory (the main repo root, even from a worktree). */
    getMainRepoRoot(cwd: string): Promise<string>;
    /** Check whether a named remote (e.g. "origin") exists. */
    hasRemote(cwd: string, name: string): Promise<boolean>;
    /** Get the remote URL for a named remote, or null if not set. */
    getRemoteUrl(cwd: string, name: string): Promise<string | null>;
    /** List configured remote names. */
    listRemotes(cwd: string): Promise<string[]>;
    /**
     * Resolve the best remote for push/PR operations.
     * Priority: branch-specific remote -> origin -> first configured remote.
     * Falls back to main repo root remotes for worktrees.
     */
    getPreferredRemote(cwd: string, branch?: string): Promise<string | null>;
    /**
     * Resolve the repository's default branch (e.g. "main" or "master").
     * Tries gh CLI first, then falls back to common defaults.
     */
    resolveDefaultBranch(cwd: string, remoteUrl?: string | null): Promise<string>;
    private createAzureDevopsPr;
    private buildAzurePrUrl;
    private createGithubPrViaApi;
    private fetchGithubPrByHead;
    /**
     * Generate a pull request body from the diff between base and head.
     * Collects commit log + diff stat and formats as markdown.
     */
    generatePrBody(cwd: string, baseBranch: string, headBranch: string, prTitle: string): Promise<string>;
    /**
     * Build a URL to create a new pull request on the hosting provider.
     * Supports GitHub, GitLab, Bitbucket, and Azure DevOps remote URLs.
     */
    buildCreatePrUrl(cwd: string, branch: string, remoteName?: string, baseBranch?: string): Promise<string | undefined>;
    /** Return the diff of uncommitted changes (staged + unstaged). */
    diff(cwd: string): Promise<GitDiffResult>;
    diffStats(cwd: string, baseBranch?: string, branch?: string): Promise<GitDiffStatsResult>;
    /**
     * Return per-file original and modified content so the frontend can
     * render a Monaco diff editor.
     *
     * @param baseBranch — when given, diff working tree against that branch
     *   (shows all thread changes: committed + uncommitted).
     * @param branch — when given with baseBranch, diff the thread branch
     *   against its merge-base with baseBranch (matches PR-style history).
     *   When omitted entirely, only uncommitted working-tree changes are
     *   returned (original = HEAD).
     */
    fileDiffs(cwd: string, baseBranch?: string, branch?: string): Promise<FileDiffEntry[]>;
    /**
     * Diff working tree against a base branch (shows all committed + uncommitted changes).
     */
    private fileDiffsBranch;
    /**
     * Diff a branch against its merge-base with the base branch, matching the
     * PR view even after the base branch has moved on.
     */
    private fileDiffsBetweenRefs;
    /**
     * Resolve a base branch to the commit where the working tree's HEAD
     * diverged from it. Diffing the working tree against this merge-base
     * (instead of the base branch tip) shows only the changes made on this
     * side, even when the base branch has advanced past where this branch was
     * created. Without this, a branch that is behind the base branch reports
     * the base's newer commits — inverted — as if this thread authored them.
     */
    private resolveWorkingTreeDiffBase;
    private resolveBranchDiffBase;
}
/**
 * Clean up a worktree, proxying to a remote FsNode when the path
 * doesn't exist locally (e.g. a Windows worktree on a Linux gateway).
 */
export declare function cleanupWorktreeRemoteAware(worktreePath: string, ws?: {
    proxyFsOp<T = unknown>(nodeId: string, op: string, params: Record<string, unknown>, timeout: number): Promise<T>;
    getFsNodes(): {
        id: string;
        isGateway?: boolean;
        platform?: string;
    }[];
}, branch?: string | null, options?: {
    preserveBranch?: boolean;
}): Promise<void>;
//# sourceMappingURL=git.d.ts.map