diff --git a/bin/gstack-code-intelligence b/bin/gstack-code-intelligence index 9bcca08ef..e9ea163f4 100755 --- a/bin/gstack-code-intelligence +++ b/bin/gstack-code-intelligence @@ -29,6 +29,7 @@ import { resolveSelectedProvider, setConsent, setProvider, + setRoot, type CodeProviderId, } from "../lib/code-intelligence"; @@ -98,11 +99,15 @@ async function cmdIndex(pathArg: string | undefined): Promise { if (!provider!.local && !consented) { fail(`${provider!.label} would send this repo's content off the machine. Run \`gstack-code-intelligence consent ${repoPath}\` first.`); } - const repo = { id: basename(repoPath), path: repoPath }; + // Graphify keys sources on the repo path; GBrain/Sourcebot on a short id. + const sourceId = provider!.id === "graphify" ? repoPath : basename(repoPath); + const repo = { id: sourceId, path: repoPath }; try { const registered = await provider!.registerSource(repo, { consented }); out(`registered ${repo.id} with ${provider!.label} (${registered.state})`); const refreshed = await provider!.refresh({ id: registered.id }, { consented }); + // Remember which repo this provider indexed so `search` reads the same graph. + setRoot(provider!.id, repoPath); out(`indexed: ${refreshed.state}${refreshed.itemCount != null ? ` (${refreshed.itemCount} items)` : ""}`); } catch (err) { handleProviderError(err, provider!.label); diff --git a/lib/code-intelligence/index.ts b/lib/code-intelligence/index.ts index 0c69719c8..6b387ff8c 100644 --- a/lib/code-intelligence/index.ts +++ b/lib/code-intelligence/index.ts @@ -12,6 +12,8 @@ export { setProvider, setConsent, hasConsent, + setRoot, + getRoot, type Selection, } from "./selection"; export { diff --git a/lib/code-intelligence/picker.ts b/lib/code-intelligence/picker.ts index 0d31d1c28..56573bc91 100644 --- a/lib/code-intelligence/picker.ts +++ b/lib/code-intelligence/picker.ts @@ -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; + /** Provider id → the absolute repo path it last indexed (so search finds it). */ + roots: Record; } -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]; +}