fix: persist indexed repo per provider and detect installs honestly

search was running in cwd and missing the repo you indexed. Persist the indexed
path per provider in the selection store and resolve it back so `search` reads
the same graph `index` built. Graphify availability now checks `graphify --version`
(installed = selectable) instead of "a graph already exists here", and the CLI
keys Graphify sources on the repo path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sinabina
2026-07-21 17:40:50 -07:00
co-authored by Claude Opus 4.8
parent f0f13998d8
commit 6686202be9
4 changed files with 43 additions and 14 deletions
+2
View File
@@ -12,6 +12,8 @@ export {
setProvider,
setConsent,
hasConsent,
setRoot,
getRoot,
type Selection,
} from "./selection";
export {
+19 -12
View File
@@ -15,9 +15,9 @@
import { localEngineStatus } from "../gbrain-local-status";
import { GbrainProvider } from "./gbrain-adapter";
import { GraphifyProvider, type GraphifyOptions } from "./graphify-adapter";
import { GraphifyProvider, graphifyInstalled, type GraphifyOptions } from "./graphify-adapter";
import { SourcebotProvider, type SourcebotOptions } from "./sourcebot-adapter";
import { readSelection } from "./selection";
import { readSelection, getRoot } from "./selection";
import type { CodeProvider, CodeProviderId } from "./contract";
/** Recommendation order — GBrain first. */
@@ -34,8 +34,12 @@ export function providerById(id: CodeProviderId, opts: PickerOptions = {}): Code
switch (id) {
case "gbrain":
return new GbrainProvider();
case "graphify":
return new GraphifyProvider({ env: opts.env, ...opts.graphify });
case "graphify": {
// Default the graph root to the repo Graphify last indexed, so `search`
// reads the same graph `index` built (not whatever cwd happens to be).
const root = opts.graphify?.root ?? getRoot("graphify", opts.env);
return new GraphifyProvider({ env: opts.env, ...opts.graphify, ...(root ? { root } : {}) });
}
case "sourcebot":
return new SourcebotProvider({ env: opts.env, ...opts.sourcebot });
}
@@ -65,14 +69,17 @@ export async function detectAvailable(opts: PickerOptions = {}): Promise<Availab
const gbrainStatus = localEngineStatus({ env: opts.env });
const gbrainOk = gbrainStatus === "ok" || gbrainStatus === "timeout";
let graphifyOk = false;
let graphifyDetail = "graphify CLI not installed";
try {
const s = await new GraphifyProvider({ env: opts.env, ...opts.graphify }).status();
graphifyOk = s.state === "ready";
graphifyDetail = graphifyOk ? "graph indexed in this repo" : "installed; no graph in this repo yet";
} catch {
graphifyOk = false;
// Available = the CLI is installed and selectable (NOT "a graph already exists
// here"). A freshly installed Graphify with no graph yet is still available.
const graphifyOk = graphifyInstalled(opts.env);
let graphifyDetail = "graphify CLI not installed (pip install graphifyy, Python >= 3.10)";
if (graphifyOk) {
try {
const s = await new GraphifyProvider({ env: opts.env, ...opts.graphify }).status();
graphifyDetail = s.state === "ready" ? "installed; graph built in this repo" : "installed; run `index` to build a graph";
} catch {
graphifyDetail = "installed";
}
}
let sourcebotOk = false;
+16 -1
View File
@@ -18,9 +18,11 @@ export interface Selection {
provider: CodeProviderId | null;
/** Absolute repo path → consented. */
consents: Record<string, boolean>;
/** Provider id → the absolute repo path it last indexed (so search finds it). */
roots: Record<string, string>;
}
const EMPTY: Selection = { provider: null, consents: {} };
const EMPTY: Selection = { provider: null, consents: {}, roots: {} };
function storePath(env: NodeJS.ProcessEnv = process.env): string {
const home = env.GSTACK_HOME || join(env.HOME || homedir(), ".gstack");
@@ -35,6 +37,7 @@ export function readSelection(env: NodeJS.ProcessEnv = process.env): Selection {
return {
provider: raw.provider ?? null,
consents: raw.consents && typeof raw.consents === "object" ? raw.consents : {},
roots: raw.roots && typeof raw.roots === "object" ? raw.roots : {},
};
} catch {
return { ...EMPTY };
@@ -66,3 +69,15 @@ export function setConsent(repoPath: string, consented: boolean, env: NodeJS.Pro
export function hasConsent(repoPath: string, env: NodeJS.ProcessEnv = process.env): boolean {
return readSelection(env).consents[resolve(repoPath)] === true;
}
/** Record the repo path a provider last indexed, so search reads the same graph. */
export function setRoot(provider: CodeProviderId, repoPath: string, env: NodeJS.ProcessEnv = process.env): Selection {
const current = readSelection(env);
const next: Selection = { ...current, roots: { ...current.roots, [provider]: resolve(repoPath) } };
write(next, env);
return next;
}
export function getRoot(provider: CodeProviderId, env: NodeJS.ProcessEnv = process.env): string | undefined {
return readSelection(env).roots[provider];
}