Introduce a split-planning path for staged changes that asks supported AI providers to group files into ordered Conventional Commit messages. The plan is validated before execution so every staged file is assigned once and unsafe states are rejected. A new dialog lets users review and adjust the proposed groups before creating the commits in sequence, with safeguards to preserve remaining changes if something fails.
517 lines
16 KiB
TypeScript
517 lines
16 KiB
TypeScript
import { tracedInvoke as invoke } from "./telemetry";
|
|
|
|
import type {
|
|
AiReviewResult,
|
|
AiCommitPlan,
|
|
CommitAiLocalProfile,
|
|
CommitAiProvider,
|
|
CommitAiStatus,
|
|
ConflictFile,
|
|
GitBlameResult,
|
|
GitBranch,
|
|
GitCommit,
|
|
GitCommitComparison,
|
|
GitRepositoryFile,
|
|
GitRemote,
|
|
MergeStrategy,
|
|
PullStrategy,
|
|
RebaseCommit,
|
|
RebasePlanItem,
|
|
ReflogEntry,
|
|
GitSearchHit,
|
|
GitStash,
|
|
GitStatus,
|
|
GitTag,
|
|
GitWorktree,
|
|
LocalModelOption,
|
|
PatchApplyAction,
|
|
RepositoryBundle,
|
|
StoredCredential,
|
|
} from "./types";
|
|
|
|
export function openRepository(path: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("open_repository", { path });
|
|
}
|
|
|
|
export function initRepository(path: string, initialBranch = "main"): Promise<GitStatus> {
|
|
return invoke<GitStatus>("init_repository", { path, initialBranch });
|
|
}
|
|
|
|
export function openRepoInExplorer(path: string): Promise<void> {
|
|
return invoke<void>("open_repo_in_explorer", { path });
|
|
}
|
|
|
|
export function openRepositoryFile(path: string, file: string): Promise<void> {
|
|
return invoke<void>("open_repository_file", { path, file });
|
|
}
|
|
|
|
export function openRepositoryBundle(path: string, commitLimit = 100): Promise<RepositoryBundle> {
|
|
return invoke<RepositoryBundle>("open_repository_bundle", { path, commitLimit });
|
|
}
|
|
|
|
export function cloneRepository(
|
|
remoteUrl: string,
|
|
parentPath: string,
|
|
directoryName?: string,
|
|
username?: string,
|
|
password?: string,
|
|
commitLimit = 100,
|
|
): Promise<RepositoryBundle> {
|
|
return invoke<RepositoryBundle>("clone_repository", {
|
|
remoteUrl,
|
|
parentPath,
|
|
directoryName: directoryName?.trim() ? directoryName.trim() : null,
|
|
username: username ?? null,
|
|
password: password ?? null,
|
|
commitLimit,
|
|
});
|
|
}
|
|
|
|
export function getStatus(path: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("get_status", { path });
|
|
}
|
|
|
|
// Sets the taskbar icon badge to ahead + behind + changed status files (0 clears it). Windows only — a no-op on
|
|
// other platforms, since Windows has no native numeric badge to fall back to.
|
|
export function setSyncBadge(ahead: number, behind: number, changes: number): Promise<void> {
|
|
return invoke<void>("set_sync_badge", { ahead, behind, changes });
|
|
}
|
|
|
|
export function listBranches(path: string): Promise<GitBranch[]> {
|
|
return invoke<GitBranch[]>("list_branches", { path });
|
|
}
|
|
|
|
export function listRemotes(path: string): Promise<GitRemote[]> { return invoke("list_remotes", { path }); }
|
|
export function addRemote(path: string, name: string, url: string): Promise<GitRemote[]> { return invoke("add_remote", { path, name, url }); }
|
|
export function updateRemote(path: string, name: string, url: string): Promise<GitRemote[]> { return invoke("update_remote", { path, name, url }); }
|
|
export function removeRemote(path: string, name: string): Promise<GitRemote[]> {
|
|
console.log("remove_remote")
|
|
return invoke("remove_remote", { path, name }); }
|
|
export function setBranchUpstream(path: string, branch: string, upstream?: string): Promise<GitStatus> { return invoke("set_branch_upstream", { path, branch, upstream: upstream || null }); }
|
|
export function deleteRemoteBranch(path: string, remote: string, branch: string): Promise<GitStatus> { return invoke("delete_remote_branch", { path, remote, branch }); }
|
|
|
|
export function listStashes(path: string): Promise<GitStash[]> {
|
|
return invoke<GitStash[]>("list_stashes", { path });
|
|
}
|
|
|
|
export function checkoutBranch(path: string, branch: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("checkout_branch", { path, branch });
|
|
}
|
|
|
|
export function createBranch(
|
|
path: string,
|
|
branch: string,
|
|
startPoint?: string,
|
|
): Promise<GitStatus> {
|
|
return invoke<GitStatus>("create_branch", { path, branch, startPoint: startPoint ?? null });
|
|
}
|
|
|
|
export function renameBranch(
|
|
path: string,
|
|
oldBranch: string,
|
|
newBranch: string,
|
|
): Promise<GitStatus> {
|
|
return invoke<GitStatus>("rename_branch", { path, oldBranch, newBranch });
|
|
}
|
|
|
|
export function deleteBranch(path: string, branch: string, force = false): Promise<GitStatus> {
|
|
return invoke<GitStatus>("delete_branch", { path, branch, force });
|
|
}
|
|
|
|
export function listWorktrees(path: string): Promise<GitWorktree[]> {
|
|
return invoke<GitWorktree[]>("list_worktrees", { path });
|
|
}
|
|
|
|
export function addWorktree(
|
|
path: string,
|
|
worktreePath: string,
|
|
options: {
|
|
branch?: string;
|
|
newBranch?: string;
|
|
startPoint?: string;
|
|
detached?: boolean;
|
|
lock?: boolean;
|
|
} = {},
|
|
): Promise<GitWorktree[]> {
|
|
return invoke<GitWorktree[]>("add_worktree", {
|
|
path,
|
|
worktreePath,
|
|
branch: options.branch ?? null,
|
|
newBranch: options.newBranch ?? null,
|
|
startPoint: options.startPoint ?? null,
|
|
detached: options.detached ?? false,
|
|
lock: options.lock ?? false,
|
|
});
|
|
}
|
|
|
|
export function removeWorktree(path: string, worktreePath: string, force = false): Promise<GitWorktree[]> {
|
|
return invoke<GitWorktree[]>("remove_worktree", { path, worktreePath, force });
|
|
}
|
|
|
|
export function moveWorktree(path: string, worktreePath: string, destination: string): Promise<GitWorktree[]> {
|
|
return invoke<GitWorktree[]>("move_worktree", { path, worktreePath, destination });
|
|
}
|
|
|
|
export function lockWorktree(path: string, worktreePath: string, reason?: string): Promise<GitWorktree[]> {
|
|
return invoke<GitWorktree[]>("lock_worktree", { path, worktreePath, reason: reason?.trim() || null });
|
|
}
|
|
|
|
export function unlockWorktree(path: string, worktreePath: string): Promise<GitWorktree[]> {
|
|
return invoke<GitWorktree[]>("unlock_worktree", { path, worktreePath });
|
|
}
|
|
|
|
export function pruneWorktrees(path: string): Promise<GitWorktree[]> {
|
|
return invoke<GitWorktree[]>("prune_worktrees", { path });
|
|
}
|
|
|
|
export function repairWorktree(path: string, worktreePath: string): Promise<GitWorktree[]> {
|
|
return invoke<GitWorktree[]>("repair_worktree", { path, worktreePath });
|
|
}
|
|
|
|
export function listTags(path: string): Promise<GitTag[]> {
|
|
return invoke<GitTag[]>("list_tags", { path });
|
|
}
|
|
|
|
export function createTag(
|
|
path: string,
|
|
name: string,
|
|
target?: string,
|
|
message?: string,
|
|
): Promise<GitTag[]> {
|
|
return invoke<GitTag[]>("create_tag", {
|
|
path,
|
|
name,
|
|
target: target?.trim() ? target.trim() : null,
|
|
message: message?.trim() ? message.trim() : null,
|
|
});
|
|
}
|
|
|
|
export function deleteTag(path: string, name: string): Promise<GitTag[]> {
|
|
return invoke<GitTag[]>("delete_tag", { path, name });
|
|
}
|
|
|
|
export function pushTag(
|
|
path: string,
|
|
name: string,
|
|
username?: string,
|
|
password?: string,
|
|
): Promise<void> {
|
|
return invoke<void>("push_tag", { path, name, username: username ?? null, password: password ?? null });
|
|
}
|
|
|
|
export function cherryPickCommit(path: string, commit: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("cherry_pick_commit", { path, commit });
|
|
}
|
|
|
|
export function cherryPickContinue(path: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("cherry_pick_continue", { path });
|
|
}
|
|
|
|
export function cherryPickAbort(path: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("cherry_pick_abort", { path });
|
|
}
|
|
|
|
export function stageFiles(path: string, files: string[]): Promise<GitStatus> {
|
|
return invoke<GitStatus>("stage_files", { path, files });
|
|
}
|
|
|
|
export function unstageFiles(path: string, files: string[]): Promise<GitStatus> {
|
|
return invoke<GitStatus>("unstage_files", { path, files });
|
|
}
|
|
|
|
export function restoreFiles(
|
|
path: string,
|
|
files: string[],
|
|
staged: boolean,
|
|
): Promise<GitStatus> {
|
|
return invoke<GitStatus>("restore_files", { path, files, staged });
|
|
}
|
|
|
|
export function getFilePatch(path: string, file: string, staged: boolean): Promise<string> {
|
|
return invoke<string>("get_file_patch", { path, file, staged });
|
|
}
|
|
|
|
export function applyFilePatch(
|
|
path: string,
|
|
file: string,
|
|
patch: string,
|
|
action: PatchApplyAction,
|
|
): Promise<GitStatus> {
|
|
return invoke<GitStatus>("apply_file_patch", { path, file, patch, action });
|
|
}
|
|
|
|
export function commit(path: string, message: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("commit", { path, message });
|
|
}
|
|
|
|
export function amendCommit(path: string, message?: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("amend_commit", { path, message: message?.trim() ? message.trim() : null });
|
|
}
|
|
|
|
export function lastCommitMessage(path: string): Promise<string | null> {
|
|
return invoke<string | null>("last_commit_message", { path });
|
|
}
|
|
|
|
export function undoLastCommit(path: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("undo_last_commit", { path });
|
|
}
|
|
|
|
export function stashPush(
|
|
path: string,
|
|
message?: string,
|
|
includeUntracked = true,
|
|
): Promise<GitStatus> {
|
|
return invoke<GitStatus>("stash_push", {
|
|
path,
|
|
message: message?.trim() ? message.trim() : null,
|
|
includeUntracked,
|
|
});
|
|
}
|
|
|
|
export function stashApply(path: string, selector: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("stash_apply", { path, selector });
|
|
}
|
|
|
|
export function stashPop(path: string, selector: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("stash_pop", { path, selector });
|
|
}
|
|
|
|
export function stashDrop(path: string, selector: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("stash_drop", { path, selector });
|
|
}
|
|
|
|
export function commitAiStatus(): Promise<CommitAiStatus> {
|
|
return invoke<CommitAiStatus>("commit_ai_status");
|
|
}
|
|
|
|
export function commitAiLoad(modelId: string): Promise<void> {
|
|
return invoke<void>("commit_ai_load", { modelId });
|
|
}
|
|
|
|
export function commitAiLocalModels(): Promise<LocalModelOption[]> {
|
|
return invoke<LocalModelOption[]>("commit_ai_local_models");
|
|
}
|
|
|
|
export interface CommitAiGenerateOptions {
|
|
notes?: string;
|
|
provider: CommitAiProvider;
|
|
localProfile?: CommitAiLocalProfile;
|
|
model?: string;
|
|
apiKey?: string;
|
|
baseUrl?: string;
|
|
}
|
|
|
|
export function commitAiGenerate(path: string, options: CommitAiGenerateOptions): Promise<string> {
|
|
return invoke<string>("commit_ai_generate", {
|
|
path,
|
|
notes: options.notes,
|
|
provider: options.provider,
|
|
localProfile: options.localProfile,
|
|
model: options.model,
|
|
apiKey: options.apiKey,
|
|
baseUrl: options.baseUrl,
|
|
});
|
|
}
|
|
|
|
export function commitAiReview(path: string, options: CommitAiGenerateOptions): Promise<AiReviewResult> {
|
|
return invoke<AiReviewResult>("commit_ai_review", {
|
|
path,
|
|
provider: options.provider,
|
|
model: options.model,
|
|
apiKey: options.apiKey,
|
|
baseUrl: options.baseUrl,
|
|
});
|
|
}
|
|
|
|
export function commitAiSplit(path: string, options: CommitAiGenerateOptions): Promise<AiCommitPlan> {
|
|
return invoke<AiCommitPlan>("commit_ai_split", {
|
|
path,
|
|
provider: options.provider,
|
|
model: options.model,
|
|
apiKey: options.apiKey,
|
|
baseUrl: options.baseUrl,
|
|
});
|
|
}
|
|
|
|
export function pull(path: string, username?: string, password?: string, strategy: PullStrategy = "merge", remote?: string, branch?: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("pull", { path, username: username ?? null, password: password ?? null, strategy, remote: remote || null, branch: branch || null });
|
|
}
|
|
|
|
export function fetchRemote(path: string, username?: string, password?: string, prune = false, remote?: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("fetch", { path, username: username ?? null, password: password ?? null, prune, remote: remote || null });
|
|
}
|
|
|
|
export function push(path: string, username?: string, password?: string, forceWithLease = false, remote?: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("push", { path, username: username ?? null, password: password ?? null, forceWithLease, remote: remote || null });
|
|
}
|
|
|
|
export function getRemoteUrl(path: string): Promise<string | null> {
|
|
return invoke<string | null>("get_remote_url", { path });
|
|
}
|
|
|
|
export function credLoad(key: string): Promise<StoredCredential | null> {
|
|
return invoke<StoredCredential | null>("cred_load", { key });
|
|
}
|
|
|
|
export function credSave(
|
|
key: string,
|
|
username: string,
|
|
password: string,
|
|
expiresAt: string | null,
|
|
): Promise<void> {
|
|
return invoke<void>("cred_save", { key, username, password, expiresAt });
|
|
}
|
|
|
|
export function credDelete(key: string): Promise<void> {
|
|
return invoke<void>("cred_delete", { key });
|
|
}
|
|
|
|
export function listCommits(path: string, limit = 100): Promise<GitCommit[]> {
|
|
return invoke<GitCommit[]>("list_commits", { path, limit });
|
|
}
|
|
|
|
export function restoreToCommit(path: string, commit: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("restore_to_commit", { path, commit });
|
|
}
|
|
|
|
export function restoreFileFromCommit(
|
|
path: string,
|
|
commit: string,
|
|
file: string,
|
|
): Promise<GitStatus> {
|
|
return invoke<GitStatus>("restore_file_from_commit", { path, commit, file });
|
|
}
|
|
|
|
export function mergeBranch(path: string, branch: string, strategy: MergeStrategy = "default"): Promise<GitStatus> {
|
|
return invoke<GitStatus>("merge_branch", { path, branch, strategy });
|
|
}
|
|
export function mergeContinue(path: string): Promise<GitStatus> { return invoke("merge_continue", { path }); }
|
|
export function mergeAbort(path: string): Promise<GitStatus> { return invoke("merge_abort", { path }); }
|
|
export function revertCommit(path: string, commit: string): Promise<GitStatus> { return invoke("revert_commit", { path, commit }); }
|
|
|
|
export function rebaseBranch(path: string, branch: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("rebase_branch", { path, branch });
|
|
}
|
|
|
|
export function rebaseContinue(path: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("rebase_continue", { path });
|
|
}
|
|
|
|
export function rebaseAbort(path: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("rebase_abort", { path });
|
|
}
|
|
|
|
export function listInteractiveRebaseCommits(path: string, base: string): Promise<RebaseCommit[]> {
|
|
return invoke<RebaseCommit[]>("list_interactive_rebase_commits", { path, base });
|
|
}
|
|
|
|
export function startInteractiveRebase(
|
|
path: string,
|
|
base: string,
|
|
plan: RebasePlanItem[],
|
|
): Promise<GitStatus> {
|
|
return invoke<GitStatus>("start_interactive_rebase", { path, base, plan });
|
|
}
|
|
|
|
export function listReflog(path: string, limit = 250): Promise<ReflogEntry[]> {
|
|
return invoke<ReflogEntry[]>("list_reflog", { path, limit });
|
|
}
|
|
|
|
export function restoreReflogEntry(path: string, commit: string, branch: string): Promise<GitStatus> {
|
|
return invoke<GitStatus>("restore_reflog_entry", { path, commit, branch });
|
|
}
|
|
|
|
export function listRepositoryFiles(path: string): Promise<GitRepositoryFile[]> {
|
|
return invoke<GitRepositoryFile[]>("list_repository_files", { path });
|
|
}
|
|
|
|
export function listFileHistory(
|
|
path: string,
|
|
file: string,
|
|
limit = 100,
|
|
requestId?: string,
|
|
): Promise<GitCommit[]> {
|
|
return invoke<GitCommit[]>("list_file_history", { path, file, limit, requestId: requestId ?? null });
|
|
}
|
|
|
|
export function cancelFileHistory(requestId: string): Promise<void> {
|
|
return invoke<void>("cancel_file_history", { requestId });
|
|
}
|
|
|
|
export function getFileBlame(path: string, file: string): Promise<GitBlameResult> {
|
|
return invoke<GitBlameResult>("get_file_blame", { path, file });
|
|
}
|
|
|
|
export function compareCommits(
|
|
path: string,
|
|
from: string,
|
|
to: string,
|
|
): Promise<GitCommitComparison> {
|
|
return invoke<GitCommitComparison>("compare_commits", { path, from, to });
|
|
}
|
|
|
|
export function diffFileAgainstWorkingTree(
|
|
path: string,
|
|
commit: string,
|
|
file: string,
|
|
): Promise<GitCommitComparison> {
|
|
return invoke<GitCommitComparison>("diff_file_against_working_tree", { path, commit, file });
|
|
}
|
|
|
|
export function compareFileToHead(
|
|
path: string,
|
|
commit: string,
|
|
file: string,
|
|
): Promise<GitCommitComparison> {
|
|
return invoke<GitCommitComparison>("compare_file_to_head", { path, commit, file });
|
|
}
|
|
|
|
export function compareFileToParent(
|
|
path: string,
|
|
commit: string,
|
|
file: string,
|
|
oldFile: string | null = null,
|
|
): Promise<GitCommitComparison> {
|
|
return invoke<GitCommitComparison>("compare_file_to_parent", { path, commit, file, oldFile });
|
|
}
|
|
|
|
export function searchCodeIntroductions(
|
|
path: string,
|
|
query: string,
|
|
caseSensitive = false,
|
|
limit = 250,
|
|
searchId?: string,
|
|
): Promise<GitSearchHit[]> {
|
|
return invoke<GitSearchHit[]>("search_code_introductions", {
|
|
path,
|
|
query,
|
|
caseSensitive,
|
|
limit,
|
|
searchId: searchId ?? null,
|
|
});
|
|
}
|
|
|
|
export function cancelCodeSearch(searchId: string): Promise<void> {
|
|
return invoke<void>("cancel_code_search", { searchId });
|
|
}
|
|
|
|
export function readConflict(path: string, file: string): Promise<ConflictFile> {
|
|
return invoke<ConflictFile>("read_conflict", { path, file });
|
|
}
|
|
|
|
export function resolveConflict(
|
|
path: string,
|
|
file: string,
|
|
content: string,
|
|
): Promise<GitStatus> {
|
|
return invoke<GitStatus>("resolve_conflict", { path, file, content });
|
|
}
|
|
|
|
export function resolveConflictSide(
|
|
path: string,
|
|
file: string,
|
|
side: "ours" | "theirs",
|
|
): Promise<GitStatus> {
|
|
return invoke<GitStatus>("resolve_conflict_side", { path, file, side });
|
|
}
|