diff --git a/bin/gstack-memory-ingest.ts b/bin/gstack-memory-ingest.ts index a359d118..1913df32 100644 --- a/bin/gstack-memory-ingest.ts +++ b/bin/gstack-memory-ingest.ts @@ -34,8 +34,9 @@ * keep V1 ship-tight. See TODOS.md. * * V1.5 NOTE: When `gbrain put_file` ships in the gbrain CLI (cross-repo P0 TODO), - * transcripts will route to Supabase Storage instead of put_page. Until then, all - * content rides put_page; gbrain's native dedup keys on session_id. + * transcripts will route to Supabase Storage instead of the page-write path. + * Until then, all content rides `gbrain put ` (stdin, YAML frontmatter for + * title/type/tags); gbrain's native dedup keys on session_id. */ import { @@ -745,14 +746,25 @@ function buildArtifactPage(path: string, type: MemoryType): PageRecord { }; } -// ── Writer (calls gbrain put_page) ───────────────────────────────────────── +// ── Writer (calls `gbrain put`) ──────────────────────────────────────────── let _gbrainAvailability: boolean | null = null; function gbrainAvailable(): boolean { if (_gbrainAvailability !== null) return _gbrainAvailability; try { execSync("command -v gbrain", { stdio: "ignore" }); - _gbrainAvailability = true; + // gbrain v0.27 retired the legacy `put_page` flag-form for `put ` + // (content via stdin, metadata as YAML frontmatter). Probe `--help` for + // the `put` subcommand so we surface a single clean error here rather + // than failing every page with "Unknown command: put_page". The regex + // anchors on the indented subcommand format gbrain's help actually uses + // (" put ..."), not any whitespace-bordered "put" word in prose. + const help = execFileSync("gbrain", ["--help"], { + encoding: "utf-8", + timeout: 5000, + stdio: ["ignore", "pipe", "pipe"], + }); + _gbrainAvailability = /^\s+put\s/m.test(help); } catch { _gbrainAvailability = false; } @@ -761,12 +773,17 @@ function gbrainAvailable(): boolean { function gbrainPutPage(page: PageRecord): { ok: boolean; error?: string } { if (!gbrainAvailable()) { - return { ok: false, error: "gbrain CLI not in PATH" }; + return { ok: false, error: "gbrain CLI not in PATH or missing `put` subcommand" }; } - // gbrain CLI verb is `put `; it does NOT accept --title / --type / - // --tags flags. (`put_page` is the MCP tool name, not the CLI subcommand.) - // Inject title/type/tags into the YAML frontmatter that page.body already - // begins with so gbrain's frontmatter parser picks them up. + // gbrain v0.27+ uses `put ` (positional, content via stdin) instead + // of the legacy `put_page` flag form. Metadata rides as YAML frontmatter: + // - When the page body already starts with frontmatter (transcripts), inject + // title/type/tags into the existing block so gbrain's frontmatter parser + // picks them up. + // - When the page body has no frontmatter (raw artifacts: design-docs, + // learnings, builder-profile-entries), wrap with a fresh frontmatter + // carrying the same fields. Without this branch, artifact pages would + // land in gbrain with empty title/type/tags. let body = page.body; if (body.startsWith("---\n")) { const end = body.indexOf("\n---\n", 4); @@ -779,6 +796,16 @@ function gbrainPutPage(page: PageRecord): { ok: boolean; error?: string } { ].join("\n"); body = body.slice(0, end) + "\n" + inject + body.slice(end); } + } else { + body = [ + "---", + `title: ${JSON.stringify(page.title)}`, + `type: ${page.type}`, + `tags: [${page.tags.map((t) => JSON.stringify(t)).join(", ")}]`, + "---", + "", + body, + ].join("\n"); } try { execFileSync("gbrain", ["put", page.slug], {