小橘 f7cf1a1cb2 refactor: single-package workspace with root esbuild build
Merge workflow and sense devDependencies into root, remove per-package package.json and workflow tsconfigs, add scripts/build.mjs for consistent outputs.

Fixes #22
2026-04-30 09:03:05 +00:00

86 lines
2.8 KiB
JavaScript

// senses/git-workspace-status/src/index.ts
import { execFileSync } from "node:child_process";
import { resolve } from "node:path";
// senses/git-workspace-status/src/schema.ts
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
var snapshots = sqliteTable("snapshots", {
ts: integer("ts").primaryKey(),
branch: text("branch").notNull(),
headShort: text("head_short").notNull(),
porcelainLines: integer("porcelain_lines").notNull(),
hasUpstream: integer("has_upstream").notNull(),
aheadCount: integer("ahead_count").notNull(),
behindCount: integer("behind_count").notNull(),
/** Empty string when the snapshot succeeded; otherwise a short error summary. */
gitError: text("git_error").notNull()
});
// senses/git-workspace-status/src/index.ts
var GIT_TIMEOUT_MS = 15e3;
function workspaceRoot() {
const raw = process.env.GIT_WORKSPACE_ROOT;
return raw ? resolve(raw) : resolve(process.cwd());
}
function gitErrorMessage(err) {
if (err instanceof Error) {
const m = err.message.trim();
return m.length > 200 ? `${m.slice(0, 197)}...` : m;
}
return String(err);
}
function runGit(cwd, args) {
return execFileSync("git", args, {
cwd,
encoding: "utf8",
timeout: GIT_TIMEOUT_MS,
maxBuffer: 2 * 1024 * 1024
}).trimEnd();
}
function countPorcelainLines(output) {
if (!output) return 0;
return output.split("\n").filter((line) => line.length > 0).length;
}
async function compute() {
const root = workspaceRoot();
const ts = Date.now();
let branch = "";
let headShort = "";
let porcelainLines = 0;
let hasUpstream = 0;
let aheadCount = 0;
let behindCount = 0;
let gitError = "";
try {
const inside = runGit(root, ["rev-parse", "--is-inside-work-tree"]).trim();
if (inside !== "true") {
gitError = "not a git work tree";
return { signal: { ts, branch, headShort, porcelainLines, hasUpstream, aheadCount, behindCount, gitError }, workflow: null };
}
branch = runGit(root, ["rev-parse", "--abbrev-ref", "HEAD"]);
headShort = runGit(root, ["rev-parse", "--short", "HEAD"]);
porcelainLines = countPorcelainLines(runGit(root, ["status", "--porcelain"]));
try {
runGit(root, ["rev-parse", "--abbrev-ref", "@{upstream}"]);
hasUpstream = 1;
const lb = runGit(root, ["rev-list", "--left-right", "--count", "HEAD...@{upstream}"]);
const parts = lb.split(/[\t\s]+/).filter(Boolean);
if (parts.length >= 2) {
aheadCount = Number.parseInt(parts[0], 10) || 0;
behindCount = Number.parseInt(parts[1], 10) || 0;
}
} catch {
hasUpstream = 0;
aheadCount = 0;
behindCount = 0;
}
} catch (e) {
gitError = gitErrorMessage(e);
}
return { signal: { ts, branch, headShort, porcelainLines, hasUpstream, aheadCount, behindCount, gitError }, workflow: null };
}
export {
compute,
snapshots as table
};