/*
RFC-0563: gitmesh.status command handler. A local-only query that reports
the current sync state (local SHA, remote SHA, behind/ahead counts, last sync
time) without performing any network I/O. Safe to run frequently.
- Do not perform network I/O — this is a local-only query based on remote-tracking branches.
- Do not fetch from remotes — use gitmesh.sync for that.
- Status is a local-only query — it never touches remotes.
- RFC-0563 fix: add diagnostics array on error return (RFC-0086).
- RFC-1097: step 6 — compass.migrate codemod run
Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
- RFC-1149: migrate ambient filesystem and process IO to the WorkspaceIO port across engine and shared
- RFC-1254: drop unused io-migration leftovers
RFC-0563
*/
import { join } from "node:path";
import type {
KernelCommandInput,
KernelCommandResult,
KernelRuntimeContext,
} from "@warpgogol/werkstatt-shared/kernel";
import type { GitMeshStatus } from "./types.ts";
import { loadGitMeshConfig } from "./config.ts";
import {
gitCommitTimestamp,
gitRevListCount,
gitRevParseHead,
gitRevParseRemote,
} from "./git-ops.ts";
const LAST_SYNC_FILE = ".git/gitmesh.last-sync";
export async function runGitMeshStatus(
_input: KernelCommandInput,
context: KernelRuntimeContext,
): Promise> {
const { workspaceRoot } = context;
let config;
try {
config = await loadGitMeshConfig(workspaceRoot);
} catch {
return {
data: {
localSha: "",
remoteSha: "",
behind: 0,
ahead: 0,
lastSync: "",
remotes: [],
diagnostics: ["gitmesh.status: no werkstatt.gitmesh.json found — run gitmesh.sync first"],
},
exitCode: 1,
summary: "[gitmesh.status] no werkstatt.gitmesh.json found — run gitmesh.sync first",
nextSteps: [
{
action: "Run gitmesh.sync first to configure gitmesh, then re-run gitmesh.status",
kind: "required",
},
],
};
}
const localSha = await gitRevParseHead(workspaceRoot, context.io).catch(() => "");
// Find the latest remote-tracking branch by committer timestamp
let remoteSha = "";
let latestTimestamp = 0;
for (const remote of config.remotes) {
try {
const sha = await gitRevParseRemote(
remote.name,
config.trackedBranch,
workspaceRoot,
context.io,
);
const timestamp = await gitCommitTimestamp(sha, workspaceRoot, context.io);
if (timestamp > latestTimestamp) {
latestTimestamp = timestamp;
remoteSha = sha;
}
} catch {
// remote-tracking branch doesn't exist — skip
}
}
let behind = 0;
let ahead = 0;
if (remoteSha && localSha) {
behind = await gitRevListCount(localSha, remoteSha, workspaceRoot, context.io).catch(() => 0);
ahead = await gitRevListCount(remoteSha, localSha, workspaceRoot, context.io).catch(() => 0);
}
// Read last sync time
let lastSync = "";
try {
lastSync = await context.io.readFile(join(workspaceRoot, LAST_SYNC_FILE));
lastSync = lastSync.trim();
} catch {
// No last sync file — never synced
}
return {
data: {
localSha,
remoteSha,
behind,
ahead,
lastSync,
remotes: config.remotes,
},
exitCode: 0,
summary:
`[gitmesh.status] ${behind} behind, ${ahead} ahead` +
(lastSync ? `, last sync: ${lastSync}` : ", never synced"),
};
}