Files
L'électron rareandClaude Opus 4.6 619f9163c6 feat(web): add aperant renderer, cockpit components, EDA worker
- Aperant: Vite build, shims, deploy script, dist assets
- Cockpit components: Card, Badge, Modal, Input, DataTable, StatusDot
- EDA worker for browser-side PCB/schematic processing
- Next.js project config, CI pipeline, layout

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-24 00:22:28 +01:00

67 lines
1.6 KiB
TypeScript

import { execFile } from "node:child_process";
import { relative } from "node:path";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
export type GitProjectState = {
branch: string | null;
head: string | null;
author: string | null;
changedFiles: string[];
clean: boolean;
};
async function gitCapture(
repoRoot: string,
args: string[]
): Promise<string | null> {
try {
const { stdout } = await execFileAsync("git", ["-C", repoRoot, ...args]);
const value = stdout.trim();
return value || null;
} catch {
return null;
}
}
function normalizeChangedPath(line: string) {
const candidate = line.trim().slice(3).trim();
const normalized = candidate.includes("->")
? candidate.split("->").pop()?.trim() ?? candidate
: candidate;
return normalized || null;
}
export async function getGitProjectState(
repoRoot: string,
projectRoot: string
): Promise<GitProjectState> {
const scope = relative(repoRoot, projectRoot) || ".";
const [branch, head, author, statusOutput] = await Promise.all([
gitCapture(repoRoot, ["branch", "--show-current"]),
gitCapture(repoRoot, ["rev-parse", "--short", "HEAD"]),
gitCapture(repoRoot, ["config", "user.name"]),
gitCapture(repoRoot, ["status", "--short", "--", scope])
]);
const changedFiles = Array.from(
new Set(
(statusOutput ?? "")
.split("\n")
.map(normalizeChangedPath)
.filter((value): value is string => Boolean(value))
.sort((left, right) => left.localeCompare(right))
)
);
return {
branch,
head,
author,
changedFiles,
clean: changedFiles.length === 0
};
}