mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-19 00:00:13 +02:00
feat(memory): optional gbrain --semantic recall for decision search
Adds gstack-decision-search --semantic (with --query): appends a 'Related from memory' block from gbrain semantic search, scoped to the curated-memory source. Pure enhancement, reliability-first: a new lib/gstack-decision-semantic.ts is the ONLY decision module that touches gbrain and is imported lazily only on --semantic, so the reliable file path never loads gbrain code. Every path degrades to the reliable file results when gbrain is off, unconfigured, empty, or errors (never throws, 10s timeout). Built against the verified gbrain 0.42.x surface (text output [score] slug -- snippet, NOT JSON; curated-memory source resolved by worktree path, not a gstack-brain-<user> id). Deterministic-contract tests only: parser units, degrade-to-null when gbrain absent, and a fake-gbrain shim proving scope+search end-to-end. find-contradictions deferred (no verifiable CLI surface yet + curated memory not indexed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,11 +5,17 @@
|
||||
* Usage:
|
||||
* gstack-decision-search [--query KW] [--scope repo|branch|issue]
|
||||
* [--branch B] [--issue I] [--recent N] [--all] [--json]
|
||||
* [--semantic]
|
||||
*
|
||||
* Reads the BOUNDED active snapshot (decisions.active.json) — O(active), not a full
|
||||
* history scan — and rebuilds it from the event log if missing. Scope-filtered to the
|
||||
* current branch/issue context (recency != relevance). NON-INTERACTIVE. `--all` shows
|
||||
* superseded decisions too (from the full log). Exit 0 silently when there are none.
|
||||
*
|
||||
* `--semantic` (with `--query`) appends an OPTIONAL "related from memory" block from
|
||||
* gbrain semantic recall. It is a pure enhancement: when gbrain is off/unconfigured/
|
||||
* empty it degrades silently to the reliable file results above. The reliable path
|
||||
* never loads gbrain code (the semantic module is imported lazily only here).
|
||||
*/
|
||||
|
||||
import { existsSync } from "fs";
|
||||
@@ -43,13 +49,15 @@ function flagValue(name: string): string | undefined {
|
||||
|
||||
const slug = resolveSlug();
|
||||
const paths = decisionPaths(slug);
|
||||
const query = flagValue("--query")?.toLowerCase();
|
||||
const queryRaw = flagValue("--query");
|
||||
const query = queryRaw?.toLowerCase();
|
||||
const scope = flagValue("--scope");
|
||||
const branch = flagValue("--branch") ?? gitBranch();
|
||||
const issue = flagValue("--issue");
|
||||
const recent = flagValue("--recent") ? parseInt(flagValue("--recent") as string, 10) : undefined;
|
||||
const showAll = args.includes("--all");
|
||||
const asJson = args.includes("--json");
|
||||
const semantic = args.includes("--semantic");
|
||||
|
||||
let rows: ActiveDecision[];
|
||||
if (showAll) {
|
||||
@@ -75,13 +83,28 @@ rows.sort((a, b) => (a.date < b.date ? 1 : a.date > b.date ? -1 : 0)); // newest
|
||||
if (recent && recent > 0) rows = rows.slice(0, recent);
|
||||
|
||||
if (asJson) {
|
||||
// --json stays reliable-only (semantic recall is a human-facing supplement).
|
||||
console.log(JSON.stringify(rows));
|
||||
process.exit(0);
|
||||
}
|
||||
if (!rows.length) process.exit(0); // silent when nothing relevant
|
||||
|
||||
for (const d of rows) {
|
||||
const scopeTag = d.scope === "repo" ? "" : ` [${d.scope}${d.branch ? `:${d.branch}` : ""}${d.issue ? `:${d.issue}` : ""}]`;
|
||||
console.log(`- ${d.decision}${scopeTag} (${d.source}, ${d.date.slice(0, 10)})`);
|
||||
if (d.rationale) console.log(` why: ${d.rationale}`);
|
||||
}
|
||||
|
||||
// OPTIONAL gbrain enhancement. Lazy import so the reliable path above never loads
|
||||
// gbrain code. Degrades silently: null (gbrain off) or [] (nothing found) leaves the
|
||||
// reliable results above as the answer.
|
||||
if (semantic && queryRaw) {
|
||||
const { semanticRecall } = await import("../lib/gstack-decision-semantic");
|
||||
const hits = semanticRecall(queryRaw);
|
||||
if (hits && hits.length) {
|
||||
console.log("\nRelated from memory (gbrain semantic recall):");
|
||||
for (const h of hits) {
|
||||
const snip = h.snippet.length > 100 ? `${h.snippet.slice(0, 100)}…` : h.snippet;
|
||||
console.log(` [${h.score.toFixed(2)}] ${h.slug}: ${snip}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user