fix(gbrain): probe CLI without command builtin

This commit is contained in:
Jayesh Betala
2026-05-16 21:00:32 +05:30
committed by Garry Tan
parent cfd2b5d792
commit f8e4291521
6 changed files with 29 additions and 18 deletions
-12
View File
@@ -429,15 +429,6 @@ function constrainSourceId(prefix: string, raw: string): string {
return tail ? `${prefix}-${tail}-${hash}` : `${prefix}-${hash}`;
}
function gbrainAvailable(): boolean {
try {
execSync("command -v gbrain", { stdio: "ignore" });
return true;
} catch {
return false;
}
}
// ── Lock file (D1) ─────────────────────────────────────────────────────────
interface LockInfo {
@@ -529,9 +520,6 @@ async function runCodeImport(args: CliArgs): Promise<StageResult> {
if (!root) {
return { name: "code", ran: false, ok: true, duration_ms: 0, summary: "skipped (not in git repo)" };
}
if (!gbrainAvailable()) {
return { name: "code", ran: false, ok: false, duration_ms: 0, summary: "skipped (gbrain CLI not in PATH)" };
}
const sourceId = deriveCodeSourceId(root);
+1 -2
View File
@@ -54,7 +54,7 @@ import {
rmSync,
} from "fs";
import { join, basename, dirname } from "path";
import { execSync, execFileSync, spawnSync, spawn, type ChildProcess } from "child_process";
import { execFileSync, spawnSync, spawn, type ChildProcess } from "child_process";
import { homedir } from "os";
import { createHash } from "crypto";
@@ -809,7 +809,6 @@ let _gbrainAvailability: boolean | null = null;
function gbrainAvailable(): boolean {
if (_gbrainAvailability !== null) return _gbrainAvailability;
try {
execSync("command -v gbrain", { stdio: "ignore" });
// Probe `--help` for the `import` subcommand. gbrain v0.20.0+ ships
// `import <dir>` (batch markdown import via path-authoritative slugs).
// If absent, we surface a single clean error here rather than failing
+3 -4
View File
@@ -101,13 +101,13 @@ export function resolveGbrainBin(env?: NodeJS.ProcessEnv): string | null {
if (_gbrainBinCache.has(key)) return _gbrainBinCache.get(key)!;
let result: string | null = null;
try {
const out = execFileSync("sh", ["-c", "command -v gbrain"], {
execFileSync("gbrain", ["--version"], {
encoding: "utf-8",
timeout: 2_000,
stdio: ["ignore", "pipe", "ignore"],
stdio: ["ignore", "ignore", "ignore"],
env: e,
});
result = out.trim() || null;
result = "gbrain";
} catch {
result = null;
}
@@ -266,4 +266,3 @@ export function localEngineStatus(opts: ClassifyOptions = {}): LocalEngineStatus
writeCache(fresh, key);
return fresh;
}
+11
View File
@@ -21,6 +21,7 @@ import { describe, it, expect, beforeEach, afterEach } from "bun:test";
import {
mkdtempSync,
writeFileSync,
readFileSync,
mkdirSync,
rmSync,
chmodSync,
@@ -160,6 +161,16 @@ describe("lib/gbrain-local-status — five status cases", () => {
restoreEnv = null;
});
it("probes the gbrain executable directly instead of shelling through command -v", () => {
const source = readFileSync(
join(import.meta.dir, "..", "lib", "gbrain-local-status.ts"),
"utf-8",
);
expect(source).not.toContain('command -v gbrain');
expect(source).toContain('execFileSync("gbrain", ["--version"]');
});
it("returns 'no-cli' when gbrain is not on PATH", () => {
env = makeEnv({ withGbrain: false });
restoreEnv = applyEnv(env);
+7
View File
@@ -55,6 +55,13 @@ describe("gstack-gbrain-sync CLI", () => {
expect(r.stderr).toContain("Unknown argument: --bogus");
});
it("uses the shared local gbrain status classifier instead of shelling through command -v", () => {
const source = readFileSync(SCRIPT, "utf-8");
expect(source).not.toContain('command -v gbrain');
expect(source).toContain("localEngineStatus");
});
it("--dry-run with --code-only reports the code import preview only", () => {
const home = makeTestHome();
const gstackHome = join(home, ".gstack");
+7
View File
@@ -421,6 +421,13 @@ esac
}
describe("gstack-memory-ingest writer (gbrain v0.20+ batch `import` interface)", () => {
it("probes the gbrain executable directly instead of shelling through command -v", () => {
const source = readFileSync(SCRIPT, "utf-8");
expect(source).not.toContain('command -v gbrain');
expect(source).toContain('execFileSync("gbrain", ["--help"]');
});
it("invokes `gbrain import <dir> --no-embed --json` exactly once with hierarchical staging", () => {
const home = makeTestHome();
const gstackHome = join(home, ".gstack");