feat: initial project setup with gai tool
This commit is contained in:
+110
@@ -0,0 +1,110 @@
|
||||
import type { FileEntry } from "./types";
|
||||
|
||||
export async function isGitRepo(): Promise<boolean> {
|
||||
try {
|
||||
await Bun.$`git rev-parse --is-inside-work-tree`.quiet();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRepoRoot(): Promise<string> {
|
||||
const result = await Bun.$`git rev-parse --show-toplevel`.quiet().text();
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
function statusToLabel(status: string): string {
|
||||
switch (status[0]) {
|
||||
case "A":
|
||||
return "new";
|
||||
case "D":
|
||||
return "deleted";
|
||||
case "R":
|
||||
return "renamed";
|
||||
case "C":
|
||||
return "copied";
|
||||
case "M":
|
||||
return "modified";
|
||||
case "T":
|
||||
return "type change";
|
||||
default:
|
||||
return "modified";
|
||||
}
|
||||
}
|
||||
|
||||
function parseNameStatus(output: string): FileEntry[] {
|
||||
return output
|
||||
.trim()
|
||||
.split("\n")
|
||||
.filter(Boolean)
|
||||
.map((line) => {
|
||||
const [status, ...pathParts] = line.split("\t");
|
||||
const path = pathParts[pathParts.length - 1] ?? "";
|
||||
return { path, status: status!, label: statusToLabel(status!) };
|
||||
});
|
||||
}
|
||||
|
||||
export async function getStagedFiles(): Promise<FileEntry[]> {
|
||||
try {
|
||||
const result =
|
||||
await Bun.$`git diff --cached --name-status`.quiet().text();
|
||||
return parseNameStatus(result);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUnstagedFiles(): Promise<FileEntry[]> {
|
||||
const files: FileEntry[] = [];
|
||||
|
||||
try {
|
||||
const result = await Bun.$`git diff --name-status`.quiet().text();
|
||||
files.push(...parseNameStatus(result));
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
const result =
|
||||
await Bun.$`git ls-files --others --exclude-standard`.quiet().text();
|
||||
for (const path of result.trim().split("\n").filter(Boolean)) {
|
||||
files.push({ path, status: "??", label: "new" });
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
export async function getStagedDiff(): Promise<string> {
|
||||
try {
|
||||
const result = await Bun.$`git diff --staged`.quiet().text();
|
||||
return result.trim();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRecentCommits(count = 10): Promise<string[]> {
|
||||
try {
|
||||
const n = String(count);
|
||||
const result = await Bun.$`git log --oneline -n ${n}`.quiet().text();
|
||||
return result.trim().split("\n").filter(Boolean);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function stageFiles(paths: string[]): Promise<void> {
|
||||
if (paths.length === 0) return;
|
||||
await Bun.$`git add -- ${paths}`;
|
||||
}
|
||||
|
||||
export async function commit(message: string): Promise<void> {
|
||||
const proc = Bun.spawn(["git", "commit", "-m", message], {
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
});
|
||||
const exitCode = await proc.exited;
|
||||
if (exitCode !== 0) {
|
||||
throw new Error(`git commit failed (exit code ${exitCode})`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user