UNPKG

socraticode

Version:

SocratiCode — MCP server for local codebase indexing, semantic search, and code dependency graphs. All private, all local via Docker.

65 lines • 3.16 kB
// SPDX-License-Identifier: AGPL-3.0-only // Copyright (C) 2026 Giancarlo Erra - Altaire Limited import { execFile } from "node:child_process"; import { promisify } from "node:util"; import { projectIdFromPath } from "../config.js"; const exec = promisify(execFile); /** Resolve an observed branch through the existing project-identity rules. */ export function gitProjectId(projectPath, state) { return projectIdFromPath(projectPath, state.ref?.replace(/^refs\/heads\//, "") ?? null); } /** Capture storage identity and a guard that rejects subsequent checkout changes. */ export function gitRefreshTarget(projectPath, state, allowCreate) { const projectId = gitProjectId(projectPath, state); return { projectId, allowCreate, /** Reject a changed ref, commit, or configured identity before continuing. */ async assertCurrent() { const current = await readGitState(projectPath); if (!sameGitState(state, current) || gitProjectId(projectPath, current) !== projectId) { throw new Error("Git checkout changed during indexing; refresh remains pending for the current checkout."); } }, }; } /** Run a bounded Git query and preserve an actionable failure for the caller. */ async function git(projectPath, args) { try { const { stdout } = await exec("git", args, { cwd: projectPath, encoding: "utf8", timeout: 5_000, maxBuffer: 64 * 1024, windowsHide: true, }); return stdout.trim(); } catch (err) { const error = err; if (error.code === "ENOENT") throw new Error("Git refresh unavailable: Git or the checkout directory could not be found."); if (error.killed) throw new Error("Git refresh failed: Git did not respond within 5 seconds."); throw new Error(`Git refresh failed: ${error.stderr?.trim() || error.message}`); } } /** Read both ref and commit so a same-commit branch switch is still a change. */ export async function readGitState(projectPath) { if (await git(projectPath, ["rev-parse", "--is-inside-work-tree"]) !== "true") { throw new Error("Git refresh unavailable: this project is not a Git working tree."); } // Verify the ref did not move while the commit was being read. A checkout // changing repeatedly is pending, never an invented stable observation. const ref = await git(projectPath, ["rev-parse", "--symbolic-full-name", "HEAD"]); const head = await git(projectPath, ["rev-parse", "--verify", "HEAD"]); const refAfter = await git(projectPath, ["rev-parse", "--symbolic-full-name", "HEAD"]); if (ref !== refAfter) throw new Error("Git checkout changed while its state was being read; refresh will retry."); return { ref: ref === "HEAD" ? null : ref, head }; } /** Match both ref and commit; two absent observations are not synchronized. */ export function sameGitState(left, right) { return left !== undefined && right !== undefined && left.ref === right.ref && left.head === right.head; } //# sourceMappingURL=git-state.js.map