diff --git a/src/cli/memory-cli.test.ts b/src/cli/memory-cli.test.ts index 4605a2a690..b5dc938ba9 100644 --- a/src/cli/memory-cli.test.ts +++ b/src/cli/memory-cli.test.ts @@ -1,4 +1,7 @@ import { Command } from "commander"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; const getMemorySearchManager = vi.fn(); @@ -273,6 +276,70 @@ describe("memory cli", () => { expect(log).toHaveBeenCalledWith("Memory index updated (main)."); }); + it("logs qmd index file path and size after index", async () => { + const { registerMemoryCli } = await import("./memory-cli.js"); + const { defaultRuntime } = await import("../runtime.js"); + const close = vi.fn(async () => {}); + const sync = vi.fn(async () => {}); + const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-cli-qmd-index-")); + const dbPath = path.join(tmpDir, "index.sqlite"); + await fs.writeFile(dbPath, "sqlite-bytes", "utf-8"); + getMemorySearchManager.mockResolvedValueOnce({ + manager: { + sync, + status: () => ({ backend: "qmd", dbPath }), + close, + }, + }); + + const log = vi.spyOn(defaultRuntime, "log").mockImplementation(() => {}); + const program = new Command(); + program.name("test"); + registerMemoryCli(program); + await program.parseAsync(["memory", "index"], { from: "user" }); + + expect(sync).toHaveBeenCalledWith( + expect.objectContaining({ reason: "cli", force: false, progress: expect.any(Function) }), + ); + expect(log).toHaveBeenCalledWith(expect.stringContaining("QMD index: ")); + expect(log).toHaveBeenCalledWith("Memory index updated (main)."); + expect(close).toHaveBeenCalled(); + await fs.rm(tmpDir, { recursive: true, force: true }); + }); + + it("fails index when qmd db file is empty", async () => { + const { registerMemoryCli } = await import("./memory-cli.js"); + const { defaultRuntime } = await import("../runtime.js"); + const close = vi.fn(async () => {}); + const sync = vi.fn(async () => {}); + const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-cli-qmd-index-")); + const dbPath = path.join(tmpDir, "index.sqlite"); + await fs.writeFile(dbPath, "", "utf-8"); + getMemorySearchManager.mockResolvedValueOnce({ + manager: { + sync, + status: () => ({ backend: "qmd", dbPath }), + close, + }, + }); + + const error = vi.spyOn(defaultRuntime, "error").mockImplementation(() => {}); + const program = new Command(); + program.name("test"); + registerMemoryCli(program); + await program.parseAsync(["memory", "index"], { from: "user" }); + + expect(sync).toHaveBeenCalledWith( + expect.objectContaining({ reason: "cli", force: false, progress: expect.any(Function) }), + ); + expect(error).toHaveBeenCalledWith( + expect.stringContaining("Memory index failed (main): QMD index file is empty"), + ); + expect(close).toHaveBeenCalled(); + expect(process.exitCode).toBe(1); + await fs.rm(tmpDir, { recursive: true, force: true }); + }); + it("logs close failures without failing the command", async () => { const { registerMemoryCli } = await import("./memory-cli.js"); const { defaultRuntime } = await import("../runtime.js"); diff --git a/src/cli/memory-cli.ts b/src/cli/memory-cli.ts index b447785a87..78d1615f74 100644 --- a/src/cli/memory-cli.ts +++ b/src/cli/memory-cli.ts @@ -215,6 +215,34 @@ async function scanMemoryFiles( return { source: "memory", totalFiles, issues }; } +async function summarizeQmdIndexArtifact(manager: MemoryManager): Promise { + const status = manager.status?.(); + if (!status || status.backend !== "qmd") { + return null; + } + const dbPath = status.dbPath?.trim(); + if (!dbPath) { + return null; + } + let stat: fsSync.Stats; + try { + stat = await fs.stat(dbPath); + } catch (err) { + const code = (err as NodeJS.ErrnoException).code; + if (code === "ENOENT") { + throw new Error(`QMD index file not found: ${shortenHomePath(dbPath)}`, { cause: err }); + } + throw new Error( + `QMD index file check failed: ${shortenHomePath(dbPath)} (${code ?? "error"})`, + { cause: err }, + ); + } + if (!stat.isFile() || stat.size <= 0) { + throw new Error(`QMD index file is empty: ${shortenHomePath(dbPath)}`); + } + return `QMD index: ${shortenHomePath(dbPath)} (${stat.size} bytes)`; +} + async function scanMemorySources(params: { workspaceDir: string; agentId: string; @@ -633,6 +661,10 @@ export function registerMemoryCli(program: Command) { } }, ); + const qmdIndexSummary = await summarizeQmdIndexArtifact(manager); + if (qmdIndexSummary) { + defaultRuntime.log(qmdIndexSummary); + } defaultRuntime.log(`Memory index updated (${agentId}).`); } catch (err) { const message = formatErrorMessage(err);