claude-launchpad 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/chunk-CSLWJEGD.js +0 -0
  2. package/dist/{chunk-JE3BZ5S4.js → chunk-EU2OXNTI.js} +2 -2
  3. package/dist/{chunk-6ZVXZ4EF.js → chunk-FL3JGYDM.js} +14 -1
  4. package/dist/{chunk-6ZVXZ4EF.js.map → chunk-FL3JGYDM.js.map} +1 -1
  5. package/dist/chunk-FQN5PHEU.js +41 -0
  6. package/dist/chunk-FQN5PHEU.js.map +1 -0
  7. package/dist/chunk-NAW47BYA.js +0 -0
  8. package/dist/{chunk-EBM7RBPB.js → chunk-P77WV5BF.js} +2 -2
  9. package/dist/{chunk-IILH26C7.js → chunk-RKN3XZO4.js} +7 -4
  10. package/dist/chunk-RKN3XZO4.js.map +1 -0
  11. package/dist/chunk-TALTTAMW.js +0 -0
  12. package/dist/cli.js +11 -16
  13. package/dist/cli.js.map +1 -1
  14. package/dist/commands/memory/server.js +4 -3
  15. package/dist/commands/memory/server.js.map +1 -1
  16. package/dist/{context-LNUZ4GCF.js → context-ANYE6ORS.js} +6 -5
  17. package/dist/{context-LNUZ4GCF.js.map → context-ANYE6ORS.js.map} +1 -1
  18. package/dist/{extract-NVAXO5CK.js → extract-MVN35LES.js} +5 -4
  19. package/dist/{extract-NVAXO5CK.js.map → extract-MVN35LES.js.map} +1 -1
  20. package/dist/{install-65P6LMUN.js → install-2HLCAZYU.js} +8 -6
  21. package/dist/install-2HLCAZYU.js.map +1 -0
  22. package/dist/require-deps-GVKDXPPE.js +11 -0
  23. package/dist/{stats-FYAK7KZW.js → stats-OZ6S6GVG.js} +9 -7
  24. package/dist/stats-OZ6S6GVG.js.map +1 -0
  25. package/dist/{tui-R25NTQ4K.js → tui-YC74JC6L.js} +4 -3
  26. package/dist/{tui-R25NTQ4K.js.map → tui-YC74JC6L.js.map} +1 -1
  27. package/package.json +19 -26
  28. package/dist/chunk-2H7UOFLK.js +0 -11
  29. package/dist/chunk-IILH26C7.js.map +0 -1
  30. package/dist/install-65P6LMUN.js.map +0 -1
  31. package/dist/stats-FYAK7KZW.js.map +0 -1
  32. /package/dist/{chunk-JE3BZ5S4.js.map → chunk-EU2OXNTI.js.map} +0 -0
  33. /package/dist/{chunk-EBM7RBPB.js.map → chunk-P77WV5BF.js.map} +0 -0
  34. /package/dist/{chunk-2H7UOFLK.js.map → require-deps-GVKDXPPE.js.map} +0 -0
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- log
4
- } from "./chunk-6ZVXZ4EF.js";
5
2
  import {
6
3
  initStorage
7
- } from "./chunk-EBM7RBPB.js";
4
+ } from "./chunk-P77WV5BF.js";
8
5
  import "./chunk-TALTTAMW.js";
9
- import "./chunk-IILH26C7.js";
10
- import "./chunk-2H7UOFLK.js";
6
+ import "./chunk-RKN3XZO4.js";
7
+ import "./chunk-FQN5PHEU.js";
8
+ import {
9
+ log
10
+ } from "./chunk-FL3JGYDM.js";
11
11
 
12
12
  // src/commands/memory/subcommands/stats.ts
13
13
  import { existsSync, statSync } from "fs";
@@ -18,6 +18,8 @@ function formatBytes(bytes) {
18
18
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
19
19
  }
20
20
  async function runStats(opts) {
21
+ const { requireMemoryDeps } = await import("./require-deps-GVKDXPPE.js");
22
+ await requireMemoryDeps();
21
23
  const ctx = initStorage(opts.dbPath);
22
24
  try {
23
25
  const total = ctx.memoryRepo.count();
@@ -70,4 +72,4 @@ async function runStats(opts) {
70
72
  export {
71
73
  runStats
72
74
  };
73
- //# sourceMappingURL=stats-FYAK7KZW.js.map
75
+ //# sourceMappingURL=stats-OZ6S6GVG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/memory/subcommands/stats.ts"],"sourcesContent":["import { existsSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { log } from '../../../lib/output.js';\nimport { initStorage } from './init-storage.js';\n\ninterface StatsOpts {\n readonly json?: boolean;\n readonly dbPath?: string;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n\nexport async function runStats(opts: StatsOpts): Promise<void> {\n const { requireMemoryDeps } = await import('../utils/require-deps.js');\n await requireMemoryDeps();\n const ctx = initStorage(opts.dbPath);\n\n try {\n const total = ctx.memoryRepo.count();\n const byType = ctx.memoryRepo.countByType();\n const relations = ctx.relationRepo.count();\n const dateRange = ctx.memoryRepo.dateRange();\n const topInjected = ctx.memoryRepo.topInjected(5);\n\n let dbSize = 0;\n try {\n const dbPath = join(ctx.dataDir, 'memory.db');\n if (existsSync(dbPath)) {\n dbSize = statSync(dbPath).size;\n }\n } catch { /* ignore */ }\n\n if (opts.json) {\n process.stdout.write(JSON.stringify({\n totalMemories: total,\n byType,\n totalRelations: relations,\n dbSizeBytes: dbSize,\n oldestMemory: dateRange.oldest,\n newestMemory: dateRange.newest,\n topInjected,\n }, null, 2) + '\\n');\n } else {\n log.blank();\n log.step('Memory stats');\n log.info(`Memories: ${total}`);\n for (const [type, count] of Object.entries(byType)) {\n log.info(` ${type}: ${count}`);\n }\n log.info(`Relations: ${relations}`);\n log.info(`DB size: ${formatBytes(dbSize)}`);\n log.info(`Oldest: ${dateRange.oldest ?? 'none'}`);\n log.info(`Newest: ${dateRange.newest ?? 'none'}`);\n if (topInjected.length > 0) {\n log.blank();\n log.info('Top injected:');\n for (const m of topInjected) {\n log.info(` ${m.title ?? m.id} (${m.injectionCount}x)`);\n }\n }\n log.blank();\n }\n } finally {\n ctx.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,YAAY,gBAAgB;AACrC,SAAS,YAAY;AASrB,SAAS,YAAY,OAAuB;AAC1C,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,MAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAC5D,SAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AAC9C;AAEA,eAAsB,SAAS,MAAgC;AAC7D,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,4BAA0B;AACrE,QAAM,kBAAkB;AACxB,QAAM,MAAM,YAAY,KAAK,MAAM;AAEnC,MAAI;AACF,UAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,UAAM,SAAS,IAAI,WAAW,YAAY;AAC1C,UAAM,YAAY,IAAI,aAAa,MAAM;AACzC,UAAM,YAAY,IAAI,WAAW,UAAU;AAC3C,UAAM,cAAc,IAAI,WAAW,YAAY,CAAC;AAEhD,QAAI,SAAS;AACb,QAAI;AACF,YAAM,SAAS,KAAK,IAAI,SAAS,WAAW;AAC5C,UAAI,WAAW,MAAM,GAAG;AACtB,iBAAS,SAAS,MAAM,EAAE;AAAA,MAC5B;AAAA,IACF,QAAQ;AAAA,IAAe;AAEvB,QAAI,KAAK,MAAM;AACb,cAAQ,OAAO,MAAM,KAAK,UAAU;AAAA,QAClC,eAAe;AAAA,QACf;AAAA,QACA,gBAAgB;AAAA,QAChB,aAAa;AAAA,QACb,cAAc,UAAU;AAAA,QACxB,cAAc,UAAU;AAAA,QACxB;AAAA,MACF,GAAG,MAAM,CAAC,IAAI,IAAI;AAAA,IACpB,OAAO;AACL,UAAI,MAAM;AACV,UAAI,KAAK,cAAc;AACvB,UAAI,KAAK,eAAe,KAAK,EAAE;AAC/B,iBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAClD,YAAI,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE;AAAA,MAChC;AACA,UAAI,KAAK,eAAe,SAAS,EAAE;AACnC,UAAI,KAAK,eAAe,YAAY,MAAM,CAAC,EAAE;AAC7C,UAAI,KAAK,eAAe,UAAU,UAAU,MAAM,EAAE;AACpD,UAAI,KAAK,eAAe,UAAU,UAAU,MAAM,EAAE;AACpD,UAAI,YAAY,SAAS,GAAG;AAC1B,YAAI,MAAM;AACV,YAAI,KAAK,eAAe;AACxB,mBAAW,KAAK,aAAa;AAC3B,cAAI,KAAK,KAAK,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,cAAc,IAAI;AAAA,QACxD;AAAA,MACF;AACA,UAAI,MAAM;AAAA,IACZ;AAAA,EACF,UAAE;AACA,QAAI,MAAM;AAAA,EACZ;AACF;","names":[]}
@@ -11,8 +11,9 @@ import {
11
11
  loadConfig,
12
12
  migrate,
13
13
  resolveDataDir
14
- } from "./chunk-IILH26C7.js";
15
- import "./chunk-2H7UOFLK.js";
14
+ } from "./chunk-RKN3XZO4.js";
15
+ import "./chunk-FQN5PHEU.js";
16
+ import "./chunk-FL3JGYDM.js";
16
17
 
17
18
  // src/commands/memory/dashboard/tui.ts
18
19
  import blessed9 from "blessed";
@@ -1097,4 +1098,4 @@ async function startTui(options) {
1097
1098
  export {
1098
1099
  startTui
1099
1100
  };
1100
- //# sourceMappingURL=tui-R25NTQ4K.js.map
1101
+ //# sourceMappingURL=tui-YC74JC6L.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/memory/dashboard/tui.ts","../src/commands/memory/dashboard/data/data-source.ts","../src/commands/memory/dashboard/layout.ts","../src/commands/memory/dashboard/widgets/header.ts","../src/commands/memory/dashboard/widgets/memory-list.ts","../src/commands/memory/dashboard/data/formatters.ts","../src/commands/memory/dashboard/colors.ts","../src/commands/memory/dashboard/widgets/memory-detail.ts","../src/commands/memory/dashboard/widgets/stats-bar.ts","../src/commands/memory/dashboard/widgets/search-modal.ts","../src/commands/memory/dashboard/widgets/project-list.ts","../src/commands/memory/dashboard/widgets/project-picker-modal.ts","../src/commands/memory/dashboard/keybindings.ts"],"sourcesContent":["import blessed from \"blessed\";\nimport { createDatabase, closeDatabase } from \"../storage/database.js\";\nimport { migrate } from \"../storage/migrator.js\";\nimport { MemoryRepo } from \"../storage/memory-repo.js\";\nimport { RelationRepo } from \"../storage/relation-repo.js\";\nimport { SearchRepo } from \"../storage/search-repo.js\";\nimport { loadConfig, resolveDataDir } from \"../config.js\";\nimport { DashboardDataSource } from \"./data/data-source.js\";\nimport { createDashboard } from \"./layout.js\";\n\n// -- TUI Entry Point ----------------------------------------------------------\n\nexport interface TuiOptions {\n readonly dbPath?: string;\n}\n\nexport async function startTui(options?: TuiOptions): Promise<void> {\n // Work around blessed bug with xterm-256color Setulc parsing\n if (process.env[\"TERM\"]?.includes(\"256color\")) {\n process.env[\"TERM\"] = \"xterm\";\n }\n\n // -- Initialize storage -----------------------------------------------------\n const config = loadConfig(\n options?.dbPath ? { dataDir: options.dbPath } : undefined,\n );\n const dataDir = resolveDataDir(config.dataDir);\n const db = createDatabase({ dataDir });\n migrate(db);\n\n const memoryRepo = new MemoryRepo(db);\n const relationRepo = new RelationRepo(db);\n const searchRepo = new SearchRepo(db);\n\n // -- Create blessed screen --------------------------------------------------\n const screen = blessed.screen({\n smartCSR: true,\n title: \"agentic-memory\",\n });\n\n // -- Create data source and dashboard ---------------------------------------\n const dataSource = new DashboardDataSource(\n memoryRepo,\n relationRepo,\n searchRepo,\n dataDir,\n );\n\n // -- Unified shutdown -- all exit paths go through here ---------------------\n let shuttingDown = false;\n function shutdown(): void {\n if (shuttingDown) return;\n shuttingDown = true;\n dataSource.stopWatching();\n screen.destroy();\n closeDatabase(db);\n }\n\n const dashboard = createDashboard(screen, dataSource, { onQuit: shutdown });\n\n // -- Initial render ---------------------------------------------------------\n dashboard.refresh();\n\n // -- Start file watching ----------------------------------------------------\n dataSource.startWatching(() => {\n dashboard.refresh();\n });\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // -- Crash guard -- always clean up the terminal ----------------------------\n process.on(\"uncaughtException\", (err) => {\n dataSource.stopWatching();\n screen.destroy();\n closeDatabase(db);\n process.stderr.write(\n `[agentic-memory] dashboard crashed: ${err.message}\\n`,\n );\n process.exit(1);\n });\n}\n","import { statSync, watchFile, unwatchFile } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { MemoryRepo } from \"../../storage/memory-repo.js\";\nimport type { RelationRepo } from \"../../storage/relation-repo.js\";\nimport type { SearchRepo } from \"../../storage/search-repo.js\";\nimport type { Memory, Relation } from \"../../types.js\";\n\n// -- Types --------------------------------------------------------------------\n\nexport interface MemoryFilter {\n readonly type?: string;\n readonly project?: string;\n readonly query?: string;\n}\n\nexport interface DashboardStats {\n readonly total: number;\n readonly byType: Record<string, number>;\n readonly byProject: Record<string, number>;\n readonly relations: number;\n readonly dbSizeBytes: number;\n readonly oldest: string | null;\n readonly newest: string | null;\n}\n\n// -- Data Source --------------------------------------------------------------\n\nexport class DashboardDataSource {\n readonly #memoryRepo: MemoryRepo;\n readonly #relationRepo: RelationRepo;\n readonly #searchRepo: SearchRepo;\n readonly #dbPath: string;\n\n #cachedMemories: readonly Memory[] = [];\n\n constructor(\n memoryRepo: MemoryRepo,\n relationRepo: RelationRepo,\n searchRepo: SearchRepo,\n dataDir: string,\n ) {\n this.#memoryRepo = memoryRepo;\n this.#relationRepo = relationRepo;\n this.#searchRepo = searchRepo;\n this.#dbPath = join(dataDir, \"memory.db\");\n }\n\n /** Re-query all memories from DB and cache them. Excludes soft-deleted (importance=0). */\n refresh(): void {\n this.#cachedMemories = this.#memoryRepo\n .getAll()\n .filter((m) => m.importance > 0);\n }\n\n /** Return cached memories, optionally filtered by type, project, or FTS query. */\n getMemories(filter?: MemoryFilter): readonly Memory[] {\n if (!filter) return this.#cachedMemories;\n\n let results: readonly Memory[] = this.#cachedMemories;\n\n if (filter.type) {\n const t = filter.type;\n results = results.filter((m) => m.type === t);\n }\n\n if (filter.project) {\n const p = filter.project;\n results = results.filter((m) => m.project === p);\n }\n\n if (filter.query) {\n const matches = this.#searchRepo.searchFts({\n query: filter.query,\n limit: 100,\n });\n const matchedIds = new Set(matches.map((m) => m.memoryId));\n results = results.filter((m) => matchedIds.has(m.id));\n }\n\n return results;\n }\n\n /** Get all relations for a specific memory. */\n getRelationsForMemory(id: string): readonly Relation[] {\n return this.#relationRepo.getByMemory(id);\n }\n\n /** Compute aggregate stats from cached data + DB queries. */\n getStats(): DashboardStats {\n const total = this.#memoryRepo.count();\n const byType = this.#memoryRepo.countByType();\n const relations = this.#relationRepo.count();\n const { oldest, newest } = this.#memoryRepo.dateRange();\n const dbSizeBytes = this.#getDbSize();\n const byProject = this.#computeByProject();\n\n return { total, byType, byProject, relations, dbSizeBytes, oldest, newest };\n }\n\n /** Derive unique project names from cached memories. */\n getProjects(): readonly string[] {\n const projects = new Set<string>();\n for (const m of this.#cachedMemories) {\n if (m.project) {\n projects.add(m.project);\n }\n }\n return [...projects].sort();\n }\n\n /** Watch the DB file for changes (2s polling interval). Only fires on mtime change. */\n startWatching(onChange: () => void): void {\n let lastMtime = 0;\n watchFile(this.#dbPath, { interval: 2000 }, (curr) => {\n const mtime = curr.mtimeMs;\n if (mtime === lastMtime) return;\n lastMtime = mtime;\n this.refresh();\n onChange();\n });\n }\n\n /** Stop watching the DB file. */\n stopWatching(): void {\n unwatchFile(this.#dbPath);\n }\n\n // -- Private helpers --------------------------------------------------------\n\n #getDbSize(): number {\n try {\n return statSync(this.#dbPath).size;\n } catch {\n return 0;\n }\n }\n\n #computeByProject(): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const m of this.#cachedMemories) {\n const key = m.project ?? \"(none)\";\n counts[key] = (counts[key] ?? 0) + 1;\n }\n return counts;\n }\n}\n","import blessed from \"blessed\";\nimport type { DashboardDataSource } from \"./data/data-source.js\";\nimport type { Memory, MemoryType } from \"../types.js\";\nimport { createHeader } from \"./widgets/header.js\";\nimport { createMemoryList } from \"./widgets/memory-list.js\";\nimport { createMemoryDetail } from \"./widgets/memory-detail.js\";\nimport { createStatsBar } from \"./widgets/stats-bar.js\";\nimport { createSearchModal } from \"./widgets/search-modal.js\";\nimport { createProjectList } from \"./widgets/project-list.js\";\nimport { createProjectPickerModal } from \"./widgets/project-picker-modal.js\";\nimport {\n computeLifespan,\n type LifespanStatus,\n} from \"./data/formatters.js\";\nimport { registerKeybindings } from \"./keybindings.js\";\n\n// -- Types --------------------------------------------------------------------\n\nexport interface DashboardOptions {\n readonly onQuit: () => void;\n}\n\nexport interface Dashboard {\n refresh(): void;\n destroy(): void;\n}\n\ntype SortMode = \"importance\" | \"age\" | \"access\" | \"lifespan\";\n\nconst SORT_MODES: readonly SortMode[] = [\n \"importance\",\n \"age\",\n \"access\",\n \"lifespan\",\n] as const;\nconst LIFESPAN_FILTERS: readonly (LifespanStatus | undefined)[] = [\n undefined,\n \"healthy\",\n \"fading\",\n \"stale\",\n \"session\",\n] as const;\n\n// -- Layout Assembly ----------------------------------------------------------\n\nexport function createDashboard(\n screen: blessed.Widgets.Screen,\n dataSource: DashboardDataSource,\n options: DashboardOptions,\n): Dashboard {\n // -- State ------------------------------------------------------------------\n let currentTypeFilter: MemoryType | undefined;\n let currentProject: string | undefined;\n let currentProjectIndex = -1;\n let currentLifespanFilter: LifespanStatus | undefined;\n let currentSearchQuery = \"\";\n let sortMode: SortMode = \"importance\";\n let currentMemories: readonly Memory[] = [];\n\n // -- Create widgets ---------------------------------------------------------\n const header = createHeader(screen);\n const memoryList = createMemoryList(screen);\n const projectList = createProjectList(screen);\n const memoryDetail = createMemoryDetail(screen);\n const statsBar = createStatsBar(screen);\n const searchModal = createSearchModal(screen);\n const projectPickerModal = createProjectPickerModal(screen);\n\n // -- Position widgets -------------------------------------------------------\n header.widget.top = 0;\n header.widget.left = 0;\n header.widget.width = \"100%\";\n header.widget.height = 1;\n\n memoryList.widget.top = 1;\n memoryList.widget.left = 0;\n memoryList.widget.width = \"60%\";\n memoryList.widget.height = screen.rows - 4;\n\n projectList.widget.top = 1;\n projectList.widget.left = \"60%\";\n projectList.widget.width = \"40%\";\n projectList.widget.height = Math.max(\n 8,\n Math.floor((screen.rows - 4) * 0.35),\n );\n\n memoryDetail.widget.top = projectList.widget.height + 1;\n memoryDetail.widget.left = \"60%\";\n memoryDetail.widget.width = \"40%\";\n memoryDetail.widget.height =\n screen.rows - 4 - projectList.widget.height;\n\n statsBar.widget.bottom = 0;\n statsBar.widget.left = 0;\n statsBar.widget.width = \"100%\";\n statsBar.widget.height = 3;\n\n // -- Wire events ------------------------------------------------------------\n memoryList.onSelect((memory: Memory) => {\n const relations = dataSource.getRelationsForMemory(memory.id);\n memoryDetail.showMemory(memory, relations);\n screen.render();\n });\n\n projectList.onSelect((project: string | undefined) => {\n if (project === currentProject) return;\n currentProject = project;\n currentProjectIndex = dataSource\n .getProjects()\n .findIndex((p) => p === project);\n refresh();\n });\n\n projectPickerModal.onSubmit((project: string | undefined) => {\n if (project === currentProject) return;\n currentProject = project;\n currentProjectIndex = dataSource\n .getProjects()\n .findIndex((p) => p === project);\n refresh();\n });\n\n searchModal.onSubmit((query: string) => {\n currentSearchQuery = query.trim();\n refresh();\n });\n\n searchModal.onCancel(() => {\n screen.render();\n });\n\n // -- Resize handler ---------------------------------------------------------\n screen.on(\"resize\", () => {\n const listHeight = screen.rows - 4;\n memoryList.widget.height = listHeight;\n projectList.widget.height = Math.max(\n 8,\n Math.floor(listHeight * 0.35),\n );\n memoryDetail.widget.top = projectList.widget.height + 1;\n memoryDetail.widget.height = listHeight - projectList.widget.height;\n screen.render();\n });\n\n // -- Sorting ----------------------------------------------------------------\n function sortMemories(\n memories: readonly Memory[],\n ): readonly Memory[] {\n const sorted = [...memories];\n switch (sortMode) {\n case \"importance\":\n sorted.sort((a, b) => b.importance - a.importance);\n break;\n case \"age\":\n sorted.sort((a, b) => b.createdAt.localeCompare(a.createdAt));\n break;\n case \"access\":\n sorted.sort((a, b) => b.accessCount - a.accessCount);\n break;\n case \"lifespan\":\n sorted.sort(\n (a, b) =>\n computeLifespan(a).remaining - computeLifespan(b).remaining,\n );\n break;\n }\n return sorted;\n }\n\n // -- Filtering --------------------------------------------------------------\n function getCurrentProject(): string | undefined {\n return currentProject;\n }\n\n function applyTextFilter(\n memories: readonly Memory[],\n query?: string,\n ): readonly Memory[] {\n if (!query || query.trim().length === 0) return memories;\n const lower = query.toLowerCase();\n return memories.filter(\n (m) =>\n (m.title?.toLowerCase().includes(lower) ?? false) ||\n m.content.toLowerCase().includes(lower) ||\n m.tags.some((t) => t.toLowerCase().includes(lower)),\n );\n }\n\n function applyLifespanFilter(\n memories: readonly Memory[],\n ): readonly Memory[] {\n if (!currentLifespanFilter) return memories;\n return memories.filter(\n (m) => computeLifespan(m).status === currentLifespanFilter,\n );\n }\n\n // -- Refresh ----------------------------------------------------------------\n function refresh(): void {\n const previousFocus = screen.focused;\n dataSource.refresh();\n const raw = dataSource.getMemories({\n type: currentTypeFilter,\n project: getCurrentProject(),\n });\n const withLife = applyLifespanFilter(raw);\n const withSearch = applyTextFilter(withLife, currentSearchQuery);\n currentMemories = sortMemories(withSearch);\n memoryList.setData(currentMemories);\n projectList.setData(dataSource.getMemories(), currentProject);\n if (currentMemories.length === 0) {\n memoryDetail.clear();\n }\n // Always keep focus on memory list unless a specific widget has it\n const focusableWidgets: unknown[] = [memoryList.widget, projectList.widget, memoryDetail.widget];\n if (previousFocus && focusableWidgets.includes(previousFocus)) {\n previousFocus.focus();\n } else {\n memoryList.focus();\n }\n statsBar.setStats(dataSource.getStats(), currentMemories);\n\n const project = getCurrentProject();\n const label = [\n project ? `project:${project}` : \"all projects\",\n currentTypeFilter ? `type:${currentTypeFilter}` : \"all types\",\n currentLifespanFilter\n ? `life:${currentLifespanFilter}`\n : \"all life\",\n currentSearchQuery\n ? `search:${currentSearchQuery}`\n : \"search:off\",\n `sort:${sortMode}`,\n ].join(\" | \");\n header.setLabel(`agentic-memory cockpit [${label}]`);\n\n screen.render();\n }\n\n // -- Help overlay -----------------------------------------------------------\n function showHelp(): void {\n const helpBox = blessed.box({\n parent: screen,\n top: \"center\",\n left: \"center\",\n width: 50,\n height: 18,\n border: { type: \"line\" },\n style: { border: { fg: \"yellow\" }, bg: \"black\" },\n tags: true,\n content: [\n \"{bold}{yellow-fg}Keybindings{/yellow-fg}{/bold}\",\n \"\",\n \" j / k Navigate list\",\n \" Enter Select memory\",\n \" / Search\",\n \" Esc Close search / Clear search\",\n \" r Refresh\",\n \" 1-5 Filter by type (0 = all)\",\n \" l Cycle lifespan filter\",\n \" p Open project picker\",\n \" [ / ] Previous / next project\",\n \" s Cycle sort mode (incl lifespan)\",\n \" Tab Focus next pane (list/projects/detail)\",\n \" ? Show this help\",\n \" q Quit\",\n \"\",\n \"{center}Press any key to close{/center}\",\n ].join(\"\\n\"),\n });\n\n screen.render();\n helpBox.once(\"keypress\", () => {\n helpBox.destroy();\n screen.render();\n });\n helpBox.focus();\n }\n\n // -- Register keybindings ---------------------------------------------------\n registerKeybindings(screen, {\n refresh,\n openSearch: () => searchModal.open(),\n closeSearch: () => {\n if (projectPickerModal.isOpen()) {\n projectPickerModal.close();\n return;\n }\n if (searchModal.isOpen()) {\n searchModal.close();\n return;\n }\n if (currentSearchQuery.length > 0) {\n currentSearchQuery = \"\";\n refresh();\n }\n },\n openProjectPicker: () => {\n projectPickerModal.open(\n dataSource.getProjects(),\n currentProject,\n );\n },\n filterByType: (type: string | null) => {\n currentTypeFilter =\n type === null ? undefined : (type as MemoryType);\n refresh();\n },\n cycleLifespan: () => {\n const idx = LIFESPAN_FILTERS.findIndex(\n (x) => x === currentLifespanFilter,\n );\n currentLifespanFilter =\n LIFESPAN_FILTERS[(idx + 1) % LIFESPAN_FILTERS.length];\n refresh();\n },\n cycleProjectNext: () => {\n const projects = dataSource.getProjects();\n if (projects.length === 0) return;\n currentProjectIndex =\n currentProjectIndex >= projects.length - 1\n ? -1\n : currentProjectIndex + 1;\n currentProject =\n currentProjectIndex < 0\n ? undefined\n : projects[currentProjectIndex];\n refresh();\n memoryList.focus();\n },\n cycleProjectPrev: () => {\n const projects = dataSource.getProjects();\n if (projects.length === 0) return;\n currentProjectIndex =\n currentProjectIndex <= -1\n ? projects.length - 1\n : currentProjectIndex - 1;\n currentProject =\n currentProjectIndex < 0\n ? undefined\n : projects[currentProjectIndex];\n refresh();\n memoryList.focus();\n },\n cycleSort: () => {\n const idx = SORT_MODES.indexOf(sortMode);\n sortMode = SORT_MODES[(idx + 1) % SORT_MODES.length]!;\n refresh();\n },\n quit: () => options.onQuit(),\n focusNext: () => {\n if (screen.focused === memoryList.widget) {\n projectList.widget.focus();\n } else if (screen.focused === projectList.widget) {\n memoryDetail.widget.focus();\n } else {\n memoryList.widget.focus();\n }\n screen.render();\n },\n showHelp,\n });\n\n // -- Destroy ----------------------------------------------------------------\n function destroy(): void {\n header.widget.destroy();\n memoryList.widget.destroy();\n projectList.widget.destroy();\n memoryDetail.widget.destroy();\n statsBar.widget.destroy();\n projectPickerModal.close();\n screen.render();\n }\n\n return { refresh, destroy };\n}\n","import blessed from \"blessed\";\n\nexport interface HeaderWidget {\n readonly widget: blessed.Widgets.BoxElement;\n setLabel(text: string): void;\n}\n\nexport function createHeader(screen: blessed.Widgets.Screen): HeaderWidget {\n const box = blessed.box({\n parent: screen,\n top: 0,\n left: 0,\n width: \"100%\",\n height: 1,\n tags: true,\n style: { fg: \"black\", bg: \"green\", bold: true },\n content:\n \"{bold} agentic-memory cockpit {/bold} {|} [/]=search [p]=project picker [[/]]=prev/next [1-5]=type [l]=life [s]=sort [tab]=focus [?]=help [q]=quit\",\n });\n\n return {\n widget: box,\n setLabel(text: string) {\n box.setContent(\n `{bold} ${text} {/bold} {|} [/]=search [p]=project picker [[/]]=prev/next [1-5]=type [l]=life [s]=sort [tab]=focus [?]=help [q]=quit`,\n );\n },\n };\n}\n","import blessed from \"blessed\";\nimport type { Memory } from \"../../types.js\";\nimport {\n truncate,\n formatRelativeTime,\n computeLifespan,\n formatLifespanLabel,\n escapeBlessedTags,\n} from \"../data/formatters.js\";\nimport { TYPE_ABBREV } from \"../colors.js\";\n\nexport interface MemoryListWidget {\n readonly widget: blessed.Widgets.ListElement;\n setData(memories: readonly Memory[]): void;\n onSelect(callback: (memory: Memory) => void): void;\n focus(): void;\n}\n\nexport function createMemoryList(\n screen: blessed.Widgets.Screen,\n): MemoryListWidget {\n const list = blessed.list({\n parent: screen,\n top: 1,\n left: 0,\n width: \"60%\",\n height: \"100%-4\",\n keys: true,\n vi: true,\n mouse: true,\n tags: true,\n interactive: true,\n scrollable: true,\n alwaysScroll: true,\n border: { type: \"line\" },\n label: \" Memories \",\n style: {\n border: { fg: \"cyan\" },\n item: { fg: \"white\" },\n selected: { fg: \"black\", bg: \"green\" },\n },\n scrollbar: {\n style: { bg: \"cyan\" },\n },\n });\n\n let currentMemories: readonly Memory[] = [];\n let selectCallback: ((memory: Memory) => void) | null = null;\n\n function colorForLife(\n status: ReturnType<typeof computeLifespan>[\"status\"],\n ): string {\n switch (status) {\n case \"healthy\":\n return \"green\";\n case \"fading\":\n return \"yellow\";\n case \"stale\":\n return \"red\";\n case \"session\":\n return \"magenta\";\n }\n }\n\n function buildRows(memories: readonly Memory[]): string[] {\n return memories.map((m) => {\n const life = computeLifespan(m);\n const title = escapeBlessedTags(\n truncate(m.title ?? m.content.replace(/\\n/g, \" \"), 36),\n );\n const project = escapeBlessedTags(truncate(m.project ?? \"(none)\", 14));\n const type = TYPE_ABBREV[m.type] ?? m.type;\n const lifeColor = colorForLife(life.status);\n const lifeLabel = formatLifespanLabel(life.status).trim();\n const imp = `${Math.round(m.importance * 100)}%`;\n const updated = formatRelativeTime(m.updatedAt);\n return [\n `{bold}${title}{/bold}`,\n `{gray-fg}${project}{/gray-fg}`,\n `{cyan-fg}${type}{/cyan-fg}`,\n `{${lifeColor}-fg}${lifeLabel}{/${lifeColor}-fg}`,\n `{white-fg}${imp}{/white-fg}`,\n `{gray-fg}${updated}{/gray-fg}`,\n `{blue-fg}acc:${m.accessCount}{/blue-fg}`,\n ].join(\" \");\n });\n }\n\n function emitCurrentSelection(): void {\n const idx = (list as unknown as { selected: number }).selected;\n const memory = currentMemories[idx];\n if (memory && selectCallback) selectCallback(memory);\n }\n\n list.on(\"select\", () => emitCurrentSelection());\n list.on(\"select item\", () => emitCurrentSelection());\n\n return {\n widget: list,\n setData(memories: readonly Memory[]) {\n currentMemories = memories;\n const rows = buildRows(memories);\n list.setItems(rows);\n },\n onSelect(callback: (memory: Memory) => void) {\n selectCallback = callback;\n },\n focus() {\n list.focus();\n if (currentMemories.length > 0) {\n list.select(0);\n emitCurrentSelection();\n }\n },\n };\n}\n","import type { Memory, MemoryType } from \"../../types.js\";\nimport { DEFAULT_DECAY_PARAMS } from \"../../config.js\";\n\n// -- Blessed Tag Escaping -----------------------------------------------------\n\n/** Escape curly braces so Blessed doesn't interpret them as markup tags.\n * Uses fullwidth brackets (U+FF5B / U+FF5D) - visually identical in terminal fonts. */\nexport function escapeBlessedTags(text: string): string {\n return text.replace(/\\{/g, \"\\uFF5B\").replace(/\\}/g, \"\\uFF5D\");\n}\n\n// -- Relative Time ------------------------------------------------------------\n\nconst MINUTE = 60_000;\nconst HOUR = 3_600_000;\nconst DAY = 86_400_000;\nconst MONTH = 30 * DAY;\nconst YEAR = 365 * DAY;\n\nexport function formatRelativeTime(isoString: string): string {\n const diff = Date.now() - new Date(isoString).getTime();\n\n if (diff < 0) return \"just now\";\n if (diff < MINUTE) return \"just now\";\n if (diff < HOUR) return `${Math.floor(diff / MINUTE)}m ago`;\n if (diff < DAY) return `${Math.floor(diff / HOUR)}h ago`;\n if (diff < MONTH) return `${Math.floor(diff / DAY)}d ago`;\n if (diff < YEAR) return `${Math.floor(diff / MONTH)}mo ago`;\n return `${Math.floor(diff / YEAR)}y ago`;\n}\n\n// -- Importance Bar -----------------------------------------------------------\n\nconst FILLED = \"\\u2588\"; // full block\nconst EMPTY = \"\\u2591\"; // light shade\n\nexport function formatImportanceBar(\n value: number,\n width: number = 8,\n): string {\n const clamped = Math.max(0, Math.min(1, value));\n const filled = Math.round(clamped * width);\n return FILLED.repeat(filled) + EMPTY.repeat(width - filled);\n}\n\n// -- Truncation ---------------------------------------------------------------\n\nexport function truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n if (maxLen <= 1) return \"\\u2026\";\n return text.slice(0, maxLen - 1) + \"\\u2026\";\n}\n\n// -- Byte Formatting ----------------------------------------------------------\n\nconst UNITS = [\"B\", \"KB\", \"MB\", \"GB\"] as const;\n\nexport function formatBytes(bytes: number): string {\n if (bytes < 0) return \"0B\";\n let value = bytes;\n let unitIndex = 0;\n while (value >= 1024 && unitIndex < UNITS.length - 1) {\n value /= 1024;\n unitIndex++;\n }\n if (unitIndex === 0) return `${value}B`;\n return `${value.toFixed(1)}${UNITS[unitIndex]}`;\n}\n\n// -- Lifespan Helpers ---------------------------------------------------------\n\nexport type LifespanStatus = \"healthy\" | \"fading\" | \"stale\" | \"session\";\n\nexport interface LifespanInfo {\n readonly status: LifespanStatus;\n readonly tauDays: number;\n readonly ageDays: number;\n readonly remaining: number;\n}\n\nexport function tauDaysForType(type: MemoryType): number {\n return DEFAULT_DECAY_PARAMS.tauByType[type];\n}\n\nexport function computeLifespan(memory: Memory): LifespanInfo {\n const tauDays = tauDaysForType(memory.type);\n if (tauDays === 0) {\n return {\n status: \"session\",\n tauDays,\n ageDays: 0,\n remaining: 0,\n };\n }\n\n const ageDays = Math.max(\n 0,\n (Date.now() - new Date(memory.updatedAt).getTime()) / DAY,\n );\n const remaining = Math.max(0, Math.min(1, 1 - ageDays / (tauDays * 2)));\n const status: LifespanStatus =\n remaining > 0.62 ? \"healthy\" : remaining > 0.32 ? \"fading\" : \"stale\";\n return { status, tauDays, ageDays, remaining };\n}\n\nexport function formatLifespanLabel(status: LifespanStatus): string {\n switch (status) {\n case \"healthy\":\n return \"HEALTHY\";\n case \"fading\":\n return \"FADING \";\n case \"stale\":\n return \"STALE \";\n case \"session\":\n return \"SESSION\";\n }\n}\n","import type { MemoryType, RelationType } from \"../types.js\";\n\n// -- Type Colors (blessed-compatible color names) -----------------------------\n\nexport const TYPE_COLORS: Record<MemoryType, string> = {\n working: \"red\",\n episodic: \"yellow\",\n semantic: \"cyan\",\n procedural: \"green\",\n pattern: \"magenta\",\n};\n\n// -- Type Abbreviations (4-char max) ------------------------------------------\n\nexport const TYPE_ABBREV: Record<MemoryType, string> = {\n working: \"WORK\",\n episodic: \"EPIS\",\n semantic: \"SEMA\",\n procedural: \"PROC\",\n pattern: \"PTRN\",\n};\n\n// -- Relation Colors ----------------------------------------------------------\n\nexport const RELATION_COLORS: Record<RelationType, string> = {\n relates_to: \"white\",\n depends_on: \"blue\",\n contradicts: \"red\",\n extends: \"green\",\n implements: \"cyan\",\n derived_from: \"yellow\",\n};\n","import blessed from \"blessed\";\nimport type { Memory, Relation } from \"../../types.js\";\nimport {\n formatImportanceBar,\n formatRelativeTime,\n computeLifespan,\n formatLifespanLabel,\n escapeBlessedTags,\n} from \"../data/formatters.js\";\nimport { TYPE_COLORS, RELATION_COLORS } from \"../colors.js\";\n\nexport interface MemoryDetailWidget {\n readonly widget: blessed.Widgets.BoxElement;\n showMemory(memory: Memory, relations: readonly Relation[]): void;\n clear(): void;\n}\n\nexport function createMemoryDetail(\n screen: blessed.Widgets.Screen,\n): MemoryDetailWidget {\n const box = blessed.box({\n parent: screen,\n top: 1,\n left: \"45%\",\n width: \"55%\",\n height: \"100%-4\",\n keys: true,\n vi: true,\n mouse: true,\n scrollable: true,\n alwaysScroll: true,\n tags: true,\n border: { type: \"line\" },\n label: \" Detail \",\n style: {\n border: { fg: \"blue\" },\n fg: \"white\",\n },\n scrollbar: {\n style: { bg: \"blue\" },\n },\n });\n\n return {\n widget: box,\n showMemory(memory: Memory, relations: readonly Relation[]) {\n const typeColor = TYPE_COLORS[memory.type] ?? \"white\";\n const life = computeLifespan(memory);\n const lines: string[] = [\n `{bold}${escapeBlessedTags(memory.title ?? \"(untitled)\")}{/bold}`,\n \"\",\n `Type: {${typeColor}-fg}${memory.type}{/${typeColor}-fg}`,\n `Lifespan: ${formatLifespanLabel(life.status)} | age ${Math.round(life.ageDays)}d | tau ${life.tauDays}d`,\n `Health: ${formatImportanceBar(life.remaining)} ${(life.remaining * 100).toFixed(0)}% remaining`,\n `Importance: ${formatImportanceBar(memory.importance)} ${memory.importance.toFixed(2)}`,\n `Project: ${escapeBlessedTags(memory.project ?? \"(none)\")}`,\n `Tags: ${memory.tags.length > 0 ? memory.tags.map((t) => `[${escapeBlessedTags(t)}]`).join(\" \") : \"(none)\"}`,\n `Source: ${memory.source ?? \"unknown\"}`,\n \"\",\n `Created: ${formatRelativeTime(memory.createdAt)}`,\n `Updated: ${formatRelativeTime(memory.updatedAt)}`,\n `Accessed: ${memory.accessCount}x${memory.lastAccessed ? ` (last: ${formatRelativeTime(memory.lastAccessed)})` : \"\"}`,\n `Injected: ${memory.injectionCount}x`,\n \"\",\n \"{bold}Content{/bold}\",\n \"{gray-fg}\" + \"\\u2500\".repeat(40) + \"{/gray-fg}\",\n escapeBlessedTags(memory.content),\n ];\n\n if (relations.length > 0) {\n lines.push(\n \"\",\n \"{bold}Relations{/bold}\",\n \"{gray-fg}\" + \"\\u2500\".repeat(40) + \"{/gray-fg}\",\n );\n for (const r of relations) {\n const relColor = RELATION_COLORS[r.relationType] ?? \"white\";\n const direction = r.sourceId === memory.id ? \"\\u2192\" : \"\\u2190\";\n const otherId =\n r.sourceId === memory.id ? r.targetId : r.sourceId;\n lines.push(\n ` ${direction} {${relColor}-fg}${r.relationType}{/${relColor}-fg} ${otherId}`,\n );\n }\n }\n\n box.setContent(lines.join(\"\\n\"));\n box.scrollTo(0);\n },\n clear() {\n box.setContent(\"{gray-fg}Select a memory to view details{/gray-fg}\");\n },\n };\n}\n","import blessed from \"blessed\";\nimport type { DashboardStats } from \"../data/data-source.js\";\nimport { formatBytes, computeLifespan } from \"../data/formatters.js\";\nimport { TYPE_COLORS } from \"../colors.js\";\nimport type { Memory } from \"../../types.js\";\n\nexport interface StatsBarWidget {\n readonly widget: blessed.Widgets.BoxElement;\n setStats(stats: DashboardStats, visibleMemories: readonly Memory[]): void;\n}\n\nexport function createStatsBar(\n screen: blessed.Widgets.Screen,\n): StatsBarWidget {\n const box = blessed.box({\n parent: screen,\n bottom: 0,\n left: 0,\n width: \"100%\",\n height: 3,\n tags: true,\n border: { type: \"line\" },\n style: {\n border: { fg: \"blue\" },\n fg: \"white\",\n },\n });\n\n return {\n widget: box,\n setStats(stats: DashboardStats, visibleMemories: readonly Memory[]) {\n const typeBreakdown = Object.entries(stats.byType)\n .filter(([, count]) => count > 0)\n .map(([type, count]) => {\n const color =\n TYPE_COLORS[type as keyof typeof TYPE_COLORS] ?? \"white\";\n return `{${color}-fg}${type}:${count}{/${color}-fg}`;\n })\n .join(\" \");\n\n const projects = Object.entries(stats.byProject)\n .map(([name, count]) => `${name}(${count})`)\n .join(\" \");\n\n const lifeCounts = { healthy: 0, fading: 0, stale: 0, session: 0 };\n for (const memory of visibleMemories) {\n lifeCounts[computeLifespan(memory).status]++;\n }\n const lifeSummary = `Life H:${lifeCounts.healthy} F:${lifeCounts.fading} S:${lifeCounts.stale} Sess:${lifeCounts.session}`;\n\n box.setContent(\n `{bold}Total:{/bold} ${stats.total} {bold}Relations:{/bold} ${stats.relations} {bold}Visible:{/bold} ${visibleMemories.length} {bold}DB:{/bold} ${formatBytes(stats.dbSizeBytes)}\\n{bold}${lifeSummary}{/bold} ${typeBreakdown} ${projects}`,\n );\n },\n };\n}\n","import blessed from \"blessed\";\n\nexport interface SearchModalWidget {\n readonly widget: blessed.Widgets.BoxElement;\n open(): void;\n close(): void;\n isOpen(): boolean;\n onSubmit(callback: (query: string) => void): void;\n onCancel(callback: () => void): void;\n}\n\nexport function createSearchModal(\n screen: blessed.Widgets.Screen,\n): SearchModalWidget {\n const box = blessed.box({\n top: \"center\",\n left: \"center\",\n width: 60,\n height: 5,\n border: { type: \"line\" },\n label: \" Search \",\n tags: true,\n hidden: true,\n style: {\n border: { fg: \"yellow\" },\n bg: \"black\",\n },\n });\n\n const input = blessed.textbox({\n parent: box,\n top: 1,\n left: 1,\n right: 1,\n height: 1,\n inputOnFocus: true,\n style: {\n fg: \"white\",\n bg: \"black\",\n },\n });\n\n screen.append(box);\n\n let submitCallback: ((query: string) => void) | null = null;\n let cancelCallback: (() => void) | null = null;\n let isOpenState = false;\n let previousFocus: blessed.Widgets.BlessedElement | null = null;\n\n function restoreFocus(): void {\n if (previousFocus) {\n previousFocus.focus();\n previousFocus = null;\n }\n }\n\n input.on(\"submit\", (value: string) => {\n isOpenState = false;\n box.hide();\n restoreFocus();\n screen.render();\n if (value.trim()) submitCallback?.(value.trim());\n });\n\n input.on(\"cancel\", () => {\n isOpenState = false;\n box.hide();\n restoreFocus();\n screen.render();\n cancelCallback?.();\n });\n\n return {\n widget: box,\n open() {\n previousFocus = screen.focused as blessed.Widgets.BlessedElement | null;\n input.clearValue();\n isOpenState = true;\n box.show();\n input.focus();\n screen.render();\n },\n close() {\n isOpenState = false;\n box.hide();\n restoreFocus();\n screen.render();\n },\n isOpen() {\n return isOpenState;\n },\n onSubmit(callback: (query: string) => void) {\n submitCallback = callback;\n },\n onCancel(callback: () => void) {\n cancelCallback = callback;\n },\n };\n}\n","import blessed from \"blessed\";\nimport type { Memory } from \"../../types.js\";\nimport { computeLifespan } from \"../data/formatters.js\";\n\nexport interface ProjectListWidget {\n readonly widget: blessed.Widgets.ListElement;\n setData(memories: readonly Memory[], activeProject?: string): void;\n onSelect(callback: (project: string | undefined) => void): void;\n}\n\ninterface ProjectRow {\n readonly project: string | undefined;\n readonly total: number;\n readonly healthPct: number;\n}\n\nexport function createProjectList(\n screen: blessed.Widgets.Screen,\n): ProjectListWidget {\n const list = blessed.list({\n parent: screen,\n top: 1,\n left: \"60%\",\n width: \"40%\",\n height: \"35%-1\",\n keys: true,\n vi: true,\n mouse: true,\n tags: true,\n border: { type: \"line\" },\n label: \" Projects \",\n scrollable: true,\n style: {\n border: { fg: \"magenta\" },\n item: { fg: \"white\" },\n selected: { fg: \"black\", bg: \"yellow\" },\n },\n });\n\n let rows: readonly ProjectRow[] = [];\n let selectCallback: ((project: string | undefined) => void) | null = null;\n let suppressSelect = false;\n\n function buildRows(memories: readonly Memory[]): readonly ProjectRow[] {\n const byProject = new Map<string, Memory[]>();\n for (const memory of memories) {\n const project = memory.project ?? \"(none)\";\n const bucket = byProject.get(project);\n if (bucket) {\n bucket.push(memory);\n } else {\n byProject.set(project, [memory]);\n }\n }\n\n const items: ProjectRow[] = [];\n for (const [project, projectMemories] of byProject.entries()) {\n const avgRemaining =\n projectMemories.reduce(\n (sum, m) => sum + computeLifespan(m).remaining,\n 0,\n ) / projectMemories.length;\n items.push({\n project,\n total: projectMemories.length,\n healthPct: Math.round(avgRemaining * 100),\n });\n }\n\n items.sort((a, b) => b.total - a.total);\n const allHealth =\n memories.length > 0\n ? Math.round(\n (memories.reduce(\n (sum, m) => sum + computeLifespan(m).remaining,\n 0,\n ) /\n memories.length) *\n 100,\n )\n : 0;\n return [\n { project: undefined, total: memories.length, healthPct: allHealth },\n ...items,\n ];\n }\n\n function render(activeProject?: string): void {\n const body = rows.map((row) => {\n const isActive =\n row.project === activeProject ||\n (row.project === undefined && !activeProject);\n const projectName = row.project ?? \"All projects\";\n const healthColor =\n row.healthPct > 62 ? \"green\" : row.healthPct > 32 ? \"yellow\" : \"red\";\n const nameCol = projectName.padEnd(20, \" \").slice(0, 20);\n const countCol = String(row.total).padStart(3, \" \");\n const healthCol = `${String(row.healthPct).padStart(3, \" \")}%`;\n return [\n isActive ? \"{bold}> {/bold}\" : \" \",\n `{bold}${nameCol}{/bold}`,\n ` {cyan-fg}${countCol} mem{/cyan-fg}`,\n ` {${healthColor}-fg}${healthCol} health{/${healthColor}-fg}`,\n ].join(\"\");\n });\n list.setItems(body);\n }\n\n function emitSelection(): void {\n if (suppressSelect) return;\n const selectedIndex = (list as unknown as { selected: number }).selected;\n const row = rows[selectedIndex];\n if (!row || !selectCallback) return;\n selectCallback(row.project);\n }\n\n list.on(\"select\", () => emitSelection());\n\n return {\n widget: list,\n setData(memories: readonly Memory[], activeProject?: string) {\n rows = buildRows(memories);\n render(activeProject);\n if (rows.length > 0) {\n suppressSelect = true;\n const idx = rows.findIndex(\n (r) =>\n r.project === activeProject ||\n (r.project === undefined && !activeProject),\n );\n list.select(Math.max(0, idx));\n setImmediate(() => {\n suppressSelect = false;\n });\n }\n },\n onSelect(callback: (project: string | undefined) => void) {\n selectCallback = callback;\n },\n };\n}\n","import blessed from \"blessed\";\n\nexport interface ProjectPickerModalWidget {\n open(projects: readonly string[], activeProject?: string): void;\n close(): void;\n isOpen(): boolean;\n onSubmit(callback: (project: string | undefined) => void): void;\n}\n\ninterface ProjectOption {\n readonly label: string;\n readonly project: string | undefined;\n}\n\nexport function createProjectPickerModal(\n screen: blessed.Widgets.Screen,\n): ProjectPickerModalWidget {\n const box = blessed.box({\n parent: screen,\n top: \"center\",\n left: \"center\",\n width: 52,\n height: 14,\n border: { type: \"line\" },\n label: \" Project Picker \",\n tags: true,\n hidden: true,\n style: {\n border: { fg: \"yellow\" },\n bg: \"black\",\n },\n });\n\n const hint = blessed.box({\n parent: box,\n top: 0,\n left: 1,\n right: 1,\n height: 1,\n tags: true,\n content: \"{gray-fg}Enter=select Esc=close{/gray-fg}\",\n });\n\n const list = blessed.list({\n parent: box,\n top: 1,\n left: 1,\n right: 1,\n bottom: 1,\n keys: true,\n vi: true,\n mouse: true,\n tags: true,\n style: {\n item: { fg: \"white\" },\n selected: { fg: \"black\", bg: \"yellow\" },\n },\n scrollbar: {\n style: { bg: \"yellow\" },\n },\n });\n\n let isOpenState = false;\n let options: readonly ProjectOption[] = [];\n let submitCallback: ((project: string | undefined) => void) | null = null;\n let previousFocus: blessed.Widgets.BlessedElement | null = null;\n\n function restoreFocus(): void {\n if (previousFocus) {\n previousFocus.focus();\n previousFocus = null;\n }\n }\n\n function emitSelection(): void {\n const selectedIndex = (list as unknown as { selected: number }).selected;\n const selected = options[selectedIndex];\n if (!selected || !submitCallback) return;\n submitCallback(selected.project);\n box.hide();\n isOpenState = false;\n restoreFocus();\n screen.render();\n }\n\n list.key([\"escape\"], () => {\n box.hide();\n isOpenState = false;\n restoreFocus();\n screen.render();\n });\n\n list.on(\"select\", () => emitSelection());\n\n return {\n open(projects: readonly string[], activeProject?: string) {\n options = [\n { label: \"All projects\", project: undefined },\n ...projects.map((project) => ({ label: project, project })),\n ];\n\n const items = options.map((option) => {\n const active =\n option.project === activeProject ||\n (option.project === undefined && !activeProject);\n return `${active ? \"> \" : \" \"}${option.label}`;\n });\n\n list.setItems(items);\n const activeIndex = options.findIndex(\n (o) =>\n o.project === activeProject ||\n (o.project === undefined && !activeProject),\n );\n list.select(Math.max(0, activeIndex));\n previousFocus = screen.focused as blessed.Widgets.BlessedElement | null;\n box.show();\n isOpenState = true;\n list.focus();\n hint.setFront();\n screen.render();\n },\n close() {\n box.hide();\n isOpenState = false;\n restoreFocus();\n screen.render();\n },\n isOpen() {\n return isOpenState;\n },\n onSubmit(callback: (project: string | undefined) => void) {\n submitCallback = callback;\n },\n };\n}\n","// -- Keybinding registry for TUI dashboard ------------------------------------\n\nimport type { Widgets } from \"blessed\";\n\nexport interface DashboardActions {\n readonly refresh: () => void;\n readonly openSearch: () => void;\n readonly closeSearch: () => void;\n readonly openProjectPicker: () => void;\n readonly filterByType: (type: string | null) => void;\n readonly cycleLifespan: () => void;\n readonly cycleProjectNext: () => void;\n readonly cycleProjectPrev: () => void;\n readonly cycleSort: () => void;\n readonly quit: () => void;\n readonly focusNext: () => void;\n readonly showHelp: () => void;\n}\n\nconst TYPE_KEYS: Record<string, string> = {\n \"1\": \"working\",\n \"2\": \"episodic\",\n \"3\": \"semantic\",\n \"4\": \"procedural\",\n \"5\": \"pattern\",\n};\n\nexport function registerKeybindings(\n screen: Widgets.Screen,\n actions: DashboardActions,\n): void {\n screen.key([\"q\"], () => actions.quit());\n screen.key([\"C-c\"], () => actions.quit());\n screen.key([\"r\"], () => actions.refresh());\n screen.key([\"/\"], () => actions.openSearch());\n screen.key([\"escape\"], () => actions.closeSearch());\n screen.key([\"tab\"], () => actions.focusNext());\n screen.key([\"p\"], () => actions.openProjectPicker());\n screen.key([\"]\", \"right\"], () => actions.cycleProjectNext());\n screen.key([\"[\", \"left\"], () => actions.cycleProjectPrev());\n screen.key([\"s\"], () => actions.cycleSort());\n screen.key([\"l\"], () => actions.cycleLifespan());\n screen.key([\"?\"], () => actions.showHelp());\n screen.key([\"0\"], () => actions.filterByType(null));\n\n for (const [key, type] of Object.entries(TYPE_KEYS)) {\n screen.key([key], () => actions.filterByType(type));\n }\n}\n"],"mappings":"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","names":["blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed"]}
1
+ {"version":3,"sources":["../src/commands/memory/dashboard/tui.ts","../src/commands/memory/dashboard/data/data-source.ts","../src/commands/memory/dashboard/layout.ts","../src/commands/memory/dashboard/widgets/header.ts","../src/commands/memory/dashboard/widgets/memory-list.ts","../src/commands/memory/dashboard/data/formatters.ts","../src/commands/memory/dashboard/colors.ts","../src/commands/memory/dashboard/widgets/memory-detail.ts","../src/commands/memory/dashboard/widgets/stats-bar.ts","../src/commands/memory/dashboard/widgets/search-modal.ts","../src/commands/memory/dashboard/widgets/project-list.ts","../src/commands/memory/dashboard/widgets/project-picker-modal.ts","../src/commands/memory/dashboard/keybindings.ts"],"sourcesContent":["import blessed from \"blessed\";\nimport { createDatabase, closeDatabase } from \"../storage/database.js\";\nimport { migrate } from \"../storage/migrator.js\";\nimport { MemoryRepo } from \"../storage/memory-repo.js\";\nimport { RelationRepo } from \"../storage/relation-repo.js\";\nimport { SearchRepo } from \"../storage/search-repo.js\";\nimport { loadConfig, resolveDataDir } from \"../config.js\";\nimport { DashboardDataSource } from \"./data/data-source.js\";\nimport { createDashboard } from \"./layout.js\";\n\n// -- TUI Entry Point ----------------------------------------------------------\n\nexport interface TuiOptions {\n readonly dbPath?: string;\n}\n\nexport async function startTui(options?: TuiOptions): Promise<void> {\n // Work around blessed bug with xterm-256color Setulc parsing\n if (process.env[\"TERM\"]?.includes(\"256color\")) {\n process.env[\"TERM\"] = \"xterm\";\n }\n\n // -- Initialize storage -----------------------------------------------------\n const config = loadConfig(\n options?.dbPath ? { dataDir: options.dbPath } : undefined,\n );\n const dataDir = resolveDataDir(config.dataDir);\n const db = createDatabase({ dataDir });\n migrate(db);\n\n const memoryRepo = new MemoryRepo(db);\n const relationRepo = new RelationRepo(db);\n const searchRepo = new SearchRepo(db);\n\n // -- Create blessed screen --------------------------------------------------\n const screen = blessed.screen({\n smartCSR: true,\n title: \"agentic-memory\",\n });\n\n // -- Create data source and dashboard ---------------------------------------\n const dataSource = new DashboardDataSource(\n memoryRepo,\n relationRepo,\n searchRepo,\n dataDir,\n );\n\n // -- Unified shutdown -- all exit paths go through here ---------------------\n let shuttingDown = false;\n function shutdown(): void {\n if (shuttingDown) return;\n shuttingDown = true;\n dataSource.stopWatching();\n screen.destroy();\n closeDatabase(db);\n }\n\n const dashboard = createDashboard(screen, dataSource, { onQuit: shutdown });\n\n // -- Initial render ---------------------------------------------------------\n dashboard.refresh();\n\n // -- Start file watching ----------------------------------------------------\n dataSource.startWatching(() => {\n dashboard.refresh();\n });\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // -- Crash guard -- always clean up the terminal ----------------------------\n process.on(\"uncaughtException\", (err) => {\n dataSource.stopWatching();\n screen.destroy();\n closeDatabase(db);\n process.stderr.write(\n `[agentic-memory] dashboard crashed: ${err.message}\\n`,\n );\n process.exit(1);\n });\n}\n","import { statSync, watchFile, unwatchFile } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { MemoryRepo } from \"../../storage/memory-repo.js\";\nimport type { RelationRepo } from \"../../storage/relation-repo.js\";\nimport type { SearchRepo } from \"../../storage/search-repo.js\";\nimport type { Memory, Relation } from \"../../types.js\";\n\n// -- Types --------------------------------------------------------------------\n\nexport interface MemoryFilter {\n readonly type?: string;\n readonly project?: string;\n readonly query?: string;\n}\n\nexport interface DashboardStats {\n readonly total: number;\n readonly byType: Record<string, number>;\n readonly byProject: Record<string, number>;\n readonly relations: number;\n readonly dbSizeBytes: number;\n readonly oldest: string | null;\n readonly newest: string | null;\n}\n\n// -- Data Source --------------------------------------------------------------\n\nexport class DashboardDataSource {\n readonly #memoryRepo: MemoryRepo;\n readonly #relationRepo: RelationRepo;\n readonly #searchRepo: SearchRepo;\n readonly #dbPath: string;\n\n #cachedMemories: readonly Memory[] = [];\n\n constructor(\n memoryRepo: MemoryRepo,\n relationRepo: RelationRepo,\n searchRepo: SearchRepo,\n dataDir: string,\n ) {\n this.#memoryRepo = memoryRepo;\n this.#relationRepo = relationRepo;\n this.#searchRepo = searchRepo;\n this.#dbPath = join(dataDir, \"memory.db\");\n }\n\n /** Re-query all memories from DB and cache them. Excludes soft-deleted (importance=0). */\n refresh(): void {\n this.#cachedMemories = this.#memoryRepo\n .getAll()\n .filter((m) => m.importance > 0);\n }\n\n /** Return cached memories, optionally filtered by type, project, or FTS query. */\n getMemories(filter?: MemoryFilter): readonly Memory[] {\n if (!filter) return this.#cachedMemories;\n\n let results: readonly Memory[] = this.#cachedMemories;\n\n if (filter.type) {\n const t = filter.type;\n results = results.filter((m) => m.type === t);\n }\n\n if (filter.project) {\n const p = filter.project;\n results = results.filter((m) => m.project === p);\n }\n\n if (filter.query) {\n const matches = this.#searchRepo.searchFts({\n query: filter.query,\n limit: 100,\n });\n const matchedIds = new Set(matches.map((m) => m.memoryId));\n results = results.filter((m) => matchedIds.has(m.id));\n }\n\n return results;\n }\n\n /** Get all relations for a specific memory. */\n getRelationsForMemory(id: string): readonly Relation[] {\n return this.#relationRepo.getByMemory(id);\n }\n\n /** Compute aggregate stats from cached data + DB queries. */\n getStats(): DashboardStats {\n const total = this.#memoryRepo.count();\n const byType = this.#memoryRepo.countByType();\n const relations = this.#relationRepo.count();\n const { oldest, newest } = this.#memoryRepo.dateRange();\n const dbSizeBytes = this.#getDbSize();\n const byProject = this.#computeByProject();\n\n return { total, byType, byProject, relations, dbSizeBytes, oldest, newest };\n }\n\n /** Derive unique project names from cached memories. */\n getProjects(): readonly string[] {\n const projects = new Set<string>();\n for (const m of this.#cachedMemories) {\n if (m.project) {\n projects.add(m.project);\n }\n }\n return [...projects].sort();\n }\n\n /** Watch the DB file for changes (2s polling interval). Only fires on mtime change. */\n startWatching(onChange: () => void): void {\n let lastMtime = 0;\n watchFile(this.#dbPath, { interval: 2000 }, (curr) => {\n const mtime = curr.mtimeMs;\n if (mtime === lastMtime) return;\n lastMtime = mtime;\n this.refresh();\n onChange();\n });\n }\n\n /** Stop watching the DB file. */\n stopWatching(): void {\n unwatchFile(this.#dbPath);\n }\n\n // -- Private helpers --------------------------------------------------------\n\n #getDbSize(): number {\n try {\n return statSync(this.#dbPath).size;\n } catch {\n return 0;\n }\n }\n\n #computeByProject(): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const m of this.#cachedMemories) {\n const key = m.project ?? \"(none)\";\n counts[key] = (counts[key] ?? 0) + 1;\n }\n return counts;\n }\n}\n","import blessed from \"blessed\";\nimport type { DashboardDataSource } from \"./data/data-source.js\";\nimport type { Memory, MemoryType } from \"../types.js\";\nimport { createHeader } from \"./widgets/header.js\";\nimport { createMemoryList } from \"./widgets/memory-list.js\";\nimport { createMemoryDetail } from \"./widgets/memory-detail.js\";\nimport { createStatsBar } from \"./widgets/stats-bar.js\";\nimport { createSearchModal } from \"./widgets/search-modal.js\";\nimport { createProjectList } from \"./widgets/project-list.js\";\nimport { createProjectPickerModal } from \"./widgets/project-picker-modal.js\";\nimport {\n computeLifespan,\n type LifespanStatus,\n} from \"./data/formatters.js\";\nimport { registerKeybindings } from \"./keybindings.js\";\n\n// -- Types --------------------------------------------------------------------\n\nexport interface DashboardOptions {\n readonly onQuit: () => void;\n}\n\nexport interface Dashboard {\n refresh(): void;\n destroy(): void;\n}\n\ntype SortMode = \"importance\" | \"age\" | \"access\" | \"lifespan\";\n\nconst SORT_MODES: readonly SortMode[] = [\n \"importance\",\n \"age\",\n \"access\",\n \"lifespan\",\n] as const;\nconst LIFESPAN_FILTERS: readonly (LifespanStatus | undefined)[] = [\n undefined,\n \"healthy\",\n \"fading\",\n \"stale\",\n \"session\",\n] as const;\n\n// -- Layout Assembly ----------------------------------------------------------\n\nexport function createDashboard(\n screen: blessed.Widgets.Screen,\n dataSource: DashboardDataSource,\n options: DashboardOptions,\n): Dashboard {\n // -- State ------------------------------------------------------------------\n let currentTypeFilter: MemoryType | undefined;\n let currentProject: string | undefined;\n let currentProjectIndex = -1;\n let currentLifespanFilter: LifespanStatus | undefined;\n let currentSearchQuery = \"\";\n let sortMode: SortMode = \"importance\";\n let currentMemories: readonly Memory[] = [];\n\n // -- Create widgets ---------------------------------------------------------\n const header = createHeader(screen);\n const memoryList = createMemoryList(screen);\n const projectList = createProjectList(screen);\n const memoryDetail = createMemoryDetail(screen);\n const statsBar = createStatsBar(screen);\n const searchModal = createSearchModal(screen);\n const projectPickerModal = createProjectPickerModal(screen);\n\n // -- Position widgets -------------------------------------------------------\n header.widget.top = 0;\n header.widget.left = 0;\n header.widget.width = \"100%\";\n header.widget.height = 1;\n\n memoryList.widget.top = 1;\n memoryList.widget.left = 0;\n memoryList.widget.width = \"60%\";\n memoryList.widget.height = screen.rows - 4;\n\n projectList.widget.top = 1;\n projectList.widget.left = \"60%\";\n projectList.widget.width = \"40%\";\n projectList.widget.height = Math.max(\n 8,\n Math.floor((screen.rows - 4) * 0.35),\n );\n\n memoryDetail.widget.top = projectList.widget.height + 1;\n memoryDetail.widget.left = \"60%\";\n memoryDetail.widget.width = \"40%\";\n memoryDetail.widget.height =\n screen.rows - 4 - projectList.widget.height;\n\n statsBar.widget.bottom = 0;\n statsBar.widget.left = 0;\n statsBar.widget.width = \"100%\";\n statsBar.widget.height = 3;\n\n // -- Wire events ------------------------------------------------------------\n memoryList.onSelect((memory: Memory) => {\n const relations = dataSource.getRelationsForMemory(memory.id);\n memoryDetail.showMemory(memory, relations);\n screen.render();\n });\n\n projectList.onSelect((project: string | undefined) => {\n if (project === currentProject) return;\n currentProject = project;\n currentProjectIndex = dataSource\n .getProjects()\n .findIndex((p) => p === project);\n refresh();\n });\n\n projectPickerModal.onSubmit((project: string | undefined) => {\n if (project === currentProject) return;\n currentProject = project;\n currentProjectIndex = dataSource\n .getProjects()\n .findIndex((p) => p === project);\n refresh();\n });\n\n searchModal.onSubmit((query: string) => {\n currentSearchQuery = query.trim();\n refresh();\n });\n\n searchModal.onCancel(() => {\n screen.render();\n });\n\n // -- Resize handler ---------------------------------------------------------\n screen.on(\"resize\", () => {\n const listHeight = screen.rows - 4;\n memoryList.widget.height = listHeight;\n projectList.widget.height = Math.max(\n 8,\n Math.floor(listHeight * 0.35),\n );\n memoryDetail.widget.top = projectList.widget.height + 1;\n memoryDetail.widget.height = listHeight - projectList.widget.height;\n screen.render();\n });\n\n // -- Sorting ----------------------------------------------------------------\n function sortMemories(\n memories: readonly Memory[],\n ): readonly Memory[] {\n const sorted = [...memories];\n switch (sortMode) {\n case \"importance\":\n sorted.sort((a, b) => b.importance - a.importance);\n break;\n case \"age\":\n sorted.sort((a, b) => b.createdAt.localeCompare(a.createdAt));\n break;\n case \"access\":\n sorted.sort((a, b) => b.accessCount - a.accessCount);\n break;\n case \"lifespan\":\n sorted.sort(\n (a, b) =>\n computeLifespan(a).remaining - computeLifespan(b).remaining,\n );\n break;\n }\n return sorted;\n }\n\n // -- Filtering --------------------------------------------------------------\n function getCurrentProject(): string | undefined {\n return currentProject;\n }\n\n function applyTextFilter(\n memories: readonly Memory[],\n query?: string,\n ): readonly Memory[] {\n if (!query || query.trim().length === 0) return memories;\n const lower = query.toLowerCase();\n return memories.filter(\n (m) =>\n (m.title?.toLowerCase().includes(lower) ?? false) ||\n m.content.toLowerCase().includes(lower) ||\n m.tags.some((t) => t.toLowerCase().includes(lower)),\n );\n }\n\n function applyLifespanFilter(\n memories: readonly Memory[],\n ): readonly Memory[] {\n if (!currentLifespanFilter) return memories;\n return memories.filter(\n (m) => computeLifespan(m).status === currentLifespanFilter,\n );\n }\n\n // -- Refresh ----------------------------------------------------------------\n function refresh(): void {\n const previousFocus = screen.focused;\n dataSource.refresh();\n const raw = dataSource.getMemories({\n type: currentTypeFilter,\n project: getCurrentProject(),\n });\n const withLife = applyLifespanFilter(raw);\n const withSearch = applyTextFilter(withLife, currentSearchQuery);\n currentMemories = sortMemories(withSearch);\n memoryList.setData(currentMemories);\n projectList.setData(dataSource.getMemories(), currentProject);\n if (currentMemories.length === 0) {\n memoryDetail.clear();\n }\n // Always keep focus on memory list unless a specific widget has it\n const focusableWidgets: unknown[] = [memoryList.widget, projectList.widget, memoryDetail.widget];\n if (previousFocus && focusableWidgets.includes(previousFocus)) {\n previousFocus.focus();\n } else {\n memoryList.focus();\n }\n statsBar.setStats(dataSource.getStats(), currentMemories);\n\n const project = getCurrentProject();\n const label = [\n project ? `project:${project}` : \"all projects\",\n currentTypeFilter ? `type:${currentTypeFilter}` : \"all types\",\n currentLifespanFilter\n ? `life:${currentLifespanFilter}`\n : \"all life\",\n currentSearchQuery\n ? `search:${currentSearchQuery}`\n : \"search:off\",\n `sort:${sortMode}`,\n ].join(\" | \");\n header.setLabel(`agentic-memory cockpit [${label}]`);\n\n screen.render();\n }\n\n // -- Help overlay -----------------------------------------------------------\n function showHelp(): void {\n const helpBox = blessed.box({\n parent: screen,\n top: \"center\",\n left: \"center\",\n width: 50,\n height: 18,\n border: { type: \"line\" },\n style: { border: { fg: \"yellow\" }, bg: \"black\" },\n tags: true,\n content: [\n \"{bold}{yellow-fg}Keybindings{/yellow-fg}{/bold}\",\n \"\",\n \" j / k Navigate list\",\n \" Enter Select memory\",\n \" / Search\",\n \" Esc Close search / Clear search\",\n \" r Refresh\",\n \" 1-5 Filter by type (0 = all)\",\n \" l Cycle lifespan filter\",\n \" p Open project picker\",\n \" [ / ] Previous / next project\",\n \" s Cycle sort mode (incl lifespan)\",\n \" Tab Focus next pane (list/projects/detail)\",\n \" ? Show this help\",\n \" q Quit\",\n \"\",\n \"{center}Press any key to close{/center}\",\n ].join(\"\\n\"),\n });\n\n screen.render();\n helpBox.once(\"keypress\", () => {\n helpBox.destroy();\n screen.render();\n });\n helpBox.focus();\n }\n\n // -- Register keybindings ---------------------------------------------------\n registerKeybindings(screen, {\n refresh,\n openSearch: () => searchModal.open(),\n closeSearch: () => {\n if (projectPickerModal.isOpen()) {\n projectPickerModal.close();\n return;\n }\n if (searchModal.isOpen()) {\n searchModal.close();\n return;\n }\n if (currentSearchQuery.length > 0) {\n currentSearchQuery = \"\";\n refresh();\n }\n },\n openProjectPicker: () => {\n projectPickerModal.open(\n dataSource.getProjects(),\n currentProject,\n );\n },\n filterByType: (type: string | null) => {\n currentTypeFilter =\n type === null ? undefined : (type as MemoryType);\n refresh();\n },\n cycleLifespan: () => {\n const idx = LIFESPAN_FILTERS.findIndex(\n (x) => x === currentLifespanFilter,\n );\n currentLifespanFilter =\n LIFESPAN_FILTERS[(idx + 1) % LIFESPAN_FILTERS.length];\n refresh();\n },\n cycleProjectNext: () => {\n const projects = dataSource.getProjects();\n if (projects.length === 0) return;\n currentProjectIndex =\n currentProjectIndex >= projects.length - 1\n ? -1\n : currentProjectIndex + 1;\n currentProject =\n currentProjectIndex < 0\n ? undefined\n : projects[currentProjectIndex];\n refresh();\n memoryList.focus();\n },\n cycleProjectPrev: () => {\n const projects = dataSource.getProjects();\n if (projects.length === 0) return;\n currentProjectIndex =\n currentProjectIndex <= -1\n ? projects.length - 1\n : currentProjectIndex - 1;\n currentProject =\n currentProjectIndex < 0\n ? undefined\n : projects[currentProjectIndex];\n refresh();\n memoryList.focus();\n },\n cycleSort: () => {\n const idx = SORT_MODES.indexOf(sortMode);\n sortMode = SORT_MODES[(idx + 1) % SORT_MODES.length]!;\n refresh();\n },\n quit: () => options.onQuit(),\n focusNext: () => {\n if (screen.focused === memoryList.widget) {\n projectList.widget.focus();\n } else if (screen.focused === projectList.widget) {\n memoryDetail.widget.focus();\n } else {\n memoryList.widget.focus();\n }\n screen.render();\n },\n showHelp,\n });\n\n // -- Destroy ----------------------------------------------------------------\n function destroy(): void {\n header.widget.destroy();\n memoryList.widget.destroy();\n projectList.widget.destroy();\n memoryDetail.widget.destroy();\n statsBar.widget.destroy();\n projectPickerModal.close();\n screen.render();\n }\n\n return { refresh, destroy };\n}\n","import blessed from \"blessed\";\n\nexport interface HeaderWidget {\n readonly widget: blessed.Widgets.BoxElement;\n setLabel(text: string): void;\n}\n\nexport function createHeader(screen: blessed.Widgets.Screen): HeaderWidget {\n const box = blessed.box({\n parent: screen,\n top: 0,\n left: 0,\n width: \"100%\",\n height: 1,\n tags: true,\n style: { fg: \"black\", bg: \"green\", bold: true },\n content:\n \"{bold} agentic-memory cockpit {/bold} {|} [/]=search [p]=project picker [[/]]=prev/next [1-5]=type [l]=life [s]=sort [tab]=focus [?]=help [q]=quit\",\n });\n\n return {\n widget: box,\n setLabel(text: string) {\n box.setContent(\n `{bold} ${text} {/bold} {|} [/]=search [p]=project picker [[/]]=prev/next [1-5]=type [l]=life [s]=sort [tab]=focus [?]=help [q]=quit`,\n );\n },\n };\n}\n","import blessed from \"blessed\";\nimport type { Memory } from \"../../types.js\";\nimport {\n truncate,\n formatRelativeTime,\n computeLifespan,\n formatLifespanLabel,\n escapeBlessedTags,\n} from \"../data/formatters.js\";\nimport { TYPE_ABBREV } from \"../colors.js\";\n\nexport interface MemoryListWidget {\n readonly widget: blessed.Widgets.ListElement;\n setData(memories: readonly Memory[]): void;\n onSelect(callback: (memory: Memory) => void): void;\n focus(): void;\n}\n\nexport function createMemoryList(\n screen: blessed.Widgets.Screen,\n): MemoryListWidget {\n const list = blessed.list({\n parent: screen,\n top: 1,\n left: 0,\n width: \"60%\",\n height: \"100%-4\",\n keys: true,\n vi: true,\n mouse: true,\n tags: true,\n interactive: true,\n scrollable: true,\n alwaysScroll: true,\n border: { type: \"line\" },\n label: \" Memories \",\n style: {\n border: { fg: \"cyan\" },\n item: { fg: \"white\" },\n selected: { fg: \"black\", bg: \"green\" },\n },\n scrollbar: {\n style: { bg: \"cyan\" },\n },\n });\n\n let currentMemories: readonly Memory[] = [];\n let selectCallback: ((memory: Memory) => void) | null = null;\n\n function colorForLife(\n status: ReturnType<typeof computeLifespan>[\"status\"],\n ): string {\n switch (status) {\n case \"healthy\":\n return \"green\";\n case \"fading\":\n return \"yellow\";\n case \"stale\":\n return \"red\";\n case \"session\":\n return \"magenta\";\n }\n }\n\n function buildRows(memories: readonly Memory[]): string[] {\n return memories.map((m) => {\n const life = computeLifespan(m);\n const title = escapeBlessedTags(\n truncate(m.title ?? m.content.replace(/\\n/g, \" \"), 36),\n );\n const project = escapeBlessedTags(truncate(m.project ?? \"(none)\", 14));\n const type = TYPE_ABBREV[m.type] ?? m.type;\n const lifeColor = colorForLife(life.status);\n const lifeLabel = formatLifespanLabel(life.status).trim();\n const imp = `${Math.round(m.importance * 100)}%`;\n const updated = formatRelativeTime(m.updatedAt);\n return [\n `{bold}${title}{/bold}`,\n `{gray-fg}${project}{/gray-fg}`,\n `{cyan-fg}${type}{/cyan-fg}`,\n `{${lifeColor}-fg}${lifeLabel}{/${lifeColor}-fg}`,\n `{white-fg}${imp}{/white-fg}`,\n `{gray-fg}${updated}{/gray-fg}`,\n `{blue-fg}acc:${m.accessCount}{/blue-fg}`,\n ].join(\" \");\n });\n }\n\n function emitCurrentSelection(): void {\n const idx = (list as unknown as { selected: number }).selected;\n const memory = currentMemories[idx];\n if (memory && selectCallback) selectCallback(memory);\n }\n\n list.on(\"select\", () => emitCurrentSelection());\n list.on(\"select item\", () => emitCurrentSelection());\n\n return {\n widget: list,\n setData(memories: readonly Memory[]) {\n currentMemories = memories;\n const rows = buildRows(memories);\n list.setItems(rows);\n },\n onSelect(callback: (memory: Memory) => void) {\n selectCallback = callback;\n },\n focus() {\n list.focus();\n if (currentMemories.length > 0) {\n list.select(0);\n emitCurrentSelection();\n }\n },\n };\n}\n","import type { Memory, MemoryType } from \"../../types.js\";\nimport { DEFAULT_DECAY_PARAMS } from \"../../config.js\";\n\n// -- Blessed Tag Escaping -----------------------------------------------------\n\n/** Escape curly braces so Blessed doesn't interpret them as markup tags.\n * Uses fullwidth brackets (U+FF5B / U+FF5D) - visually identical in terminal fonts. */\nexport function escapeBlessedTags(text: string): string {\n return text.replace(/\\{/g, \"\\uFF5B\").replace(/\\}/g, \"\\uFF5D\");\n}\n\n// -- Relative Time ------------------------------------------------------------\n\nconst MINUTE = 60_000;\nconst HOUR = 3_600_000;\nconst DAY = 86_400_000;\nconst MONTH = 30 * DAY;\nconst YEAR = 365 * DAY;\n\nexport function formatRelativeTime(isoString: string): string {\n const diff = Date.now() - new Date(isoString).getTime();\n\n if (diff < 0) return \"just now\";\n if (diff < MINUTE) return \"just now\";\n if (diff < HOUR) return `${Math.floor(diff / MINUTE)}m ago`;\n if (diff < DAY) return `${Math.floor(diff / HOUR)}h ago`;\n if (diff < MONTH) return `${Math.floor(diff / DAY)}d ago`;\n if (diff < YEAR) return `${Math.floor(diff / MONTH)}mo ago`;\n return `${Math.floor(diff / YEAR)}y ago`;\n}\n\n// -- Importance Bar -----------------------------------------------------------\n\nconst FILLED = \"\\u2588\"; // full block\nconst EMPTY = \"\\u2591\"; // light shade\n\nexport function formatImportanceBar(\n value: number,\n width: number = 8,\n): string {\n const clamped = Math.max(0, Math.min(1, value));\n const filled = Math.round(clamped * width);\n return FILLED.repeat(filled) + EMPTY.repeat(width - filled);\n}\n\n// -- Truncation ---------------------------------------------------------------\n\nexport function truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n if (maxLen <= 1) return \"\\u2026\";\n return text.slice(0, maxLen - 1) + \"\\u2026\";\n}\n\n// -- Byte Formatting ----------------------------------------------------------\n\nconst UNITS = [\"B\", \"KB\", \"MB\", \"GB\"] as const;\n\nexport function formatBytes(bytes: number): string {\n if (bytes < 0) return \"0B\";\n let value = bytes;\n let unitIndex = 0;\n while (value >= 1024 && unitIndex < UNITS.length - 1) {\n value /= 1024;\n unitIndex++;\n }\n if (unitIndex === 0) return `${value}B`;\n return `${value.toFixed(1)}${UNITS[unitIndex]}`;\n}\n\n// -- Lifespan Helpers ---------------------------------------------------------\n\nexport type LifespanStatus = \"healthy\" | \"fading\" | \"stale\" | \"session\";\n\nexport interface LifespanInfo {\n readonly status: LifespanStatus;\n readonly tauDays: number;\n readonly ageDays: number;\n readonly remaining: number;\n}\n\nexport function tauDaysForType(type: MemoryType): number {\n return DEFAULT_DECAY_PARAMS.tauByType[type];\n}\n\nexport function computeLifespan(memory: Memory): LifespanInfo {\n const tauDays = tauDaysForType(memory.type);\n if (tauDays === 0) {\n return {\n status: \"session\",\n tauDays,\n ageDays: 0,\n remaining: 0,\n };\n }\n\n const ageDays = Math.max(\n 0,\n (Date.now() - new Date(memory.updatedAt).getTime()) / DAY,\n );\n const remaining = Math.max(0, Math.min(1, 1 - ageDays / (tauDays * 2)));\n const status: LifespanStatus =\n remaining > 0.62 ? \"healthy\" : remaining > 0.32 ? \"fading\" : \"stale\";\n return { status, tauDays, ageDays, remaining };\n}\n\nexport function formatLifespanLabel(status: LifespanStatus): string {\n switch (status) {\n case \"healthy\":\n return \"HEALTHY\";\n case \"fading\":\n return \"FADING \";\n case \"stale\":\n return \"STALE \";\n case \"session\":\n return \"SESSION\";\n }\n}\n","import type { MemoryType, RelationType } from \"../types.js\";\n\n// -- Type Colors (blessed-compatible color names) -----------------------------\n\nexport const TYPE_COLORS: Record<MemoryType, string> = {\n working: \"red\",\n episodic: \"yellow\",\n semantic: \"cyan\",\n procedural: \"green\",\n pattern: \"magenta\",\n};\n\n// -- Type Abbreviations (4-char max) ------------------------------------------\n\nexport const TYPE_ABBREV: Record<MemoryType, string> = {\n working: \"WORK\",\n episodic: \"EPIS\",\n semantic: \"SEMA\",\n procedural: \"PROC\",\n pattern: \"PTRN\",\n};\n\n// -- Relation Colors ----------------------------------------------------------\n\nexport const RELATION_COLORS: Record<RelationType, string> = {\n relates_to: \"white\",\n depends_on: \"blue\",\n contradicts: \"red\",\n extends: \"green\",\n implements: \"cyan\",\n derived_from: \"yellow\",\n};\n","import blessed from \"blessed\";\nimport type { Memory, Relation } from \"../../types.js\";\nimport {\n formatImportanceBar,\n formatRelativeTime,\n computeLifespan,\n formatLifespanLabel,\n escapeBlessedTags,\n} from \"../data/formatters.js\";\nimport { TYPE_COLORS, RELATION_COLORS } from \"../colors.js\";\n\nexport interface MemoryDetailWidget {\n readonly widget: blessed.Widgets.BoxElement;\n showMemory(memory: Memory, relations: readonly Relation[]): void;\n clear(): void;\n}\n\nexport function createMemoryDetail(\n screen: blessed.Widgets.Screen,\n): MemoryDetailWidget {\n const box = blessed.box({\n parent: screen,\n top: 1,\n left: \"45%\",\n width: \"55%\",\n height: \"100%-4\",\n keys: true,\n vi: true,\n mouse: true,\n scrollable: true,\n alwaysScroll: true,\n tags: true,\n border: { type: \"line\" },\n label: \" Detail \",\n style: {\n border: { fg: \"blue\" },\n fg: \"white\",\n },\n scrollbar: {\n style: { bg: \"blue\" },\n },\n });\n\n return {\n widget: box,\n showMemory(memory: Memory, relations: readonly Relation[]) {\n const typeColor = TYPE_COLORS[memory.type] ?? \"white\";\n const life = computeLifespan(memory);\n const lines: string[] = [\n `{bold}${escapeBlessedTags(memory.title ?? \"(untitled)\")}{/bold}`,\n \"\",\n `Type: {${typeColor}-fg}${memory.type}{/${typeColor}-fg}`,\n `Lifespan: ${formatLifespanLabel(life.status)} | age ${Math.round(life.ageDays)}d | tau ${life.tauDays}d`,\n `Health: ${formatImportanceBar(life.remaining)} ${(life.remaining * 100).toFixed(0)}% remaining`,\n `Importance: ${formatImportanceBar(memory.importance)} ${memory.importance.toFixed(2)}`,\n `Project: ${escapeBlessedTags(memory.project ?? \"(none)\")}`,\n `Tags: ${memory.tags.length > 0 ? memory.tags.map((t) => `[${escapeBlessedTags(t)}]`).join(\" \") : \"(none)\"}`,\n `Source: ${memory.source ?? \"unknown\"}`,\n \"\",\n `Created: ${formatRelativeTime(memory.createdAt)}`,\n `Updated: ${formatRelativeTime(memory.updatedAt)}`,\n `Accessed: ${memory.accessCount}x${memory.lastAccessed ? ` (last: ${formatRelativeTime(memory.lastAccessed)})` : \"\"}`,\n `Injected: ${memory.injectionCount}x`,\n \"\",\n \"{bold}Content{/bold}\",\n \"{gray-fg}\" + \"\\u2500\".repeat(40) + \"{/gray-fg}\",\n escapeBlessedTags(memory.content),\n ];\n\n if (relations.length > 0) {\n lines.push(\n \"\",\n \"{bold}Relations{/bold}\",\n \"{gray-fg}\" + \"\\u2500\".repeat(40) + \"{/gray-fg}\",\n );\n for (const r of relations) {\n const relColor = RELATION_COLORS[r.relationType] ?? \"white\";\n const direction = r.sourceId === memory.id ? \"\\u2192\" : \"\\u2190\";\n const otherId =\n r.sourceId === memory.id ? r.targetId : r.sourceId;\n lines.push(\n ` ${direction} {${relColor}-fg}${r.relationType}{/${relColor}-fg} ${otherId}`,\n );\n }\n }\n\n box.setContent(lines.join(\"\\n\"));\n box.scrollTo(0);\n },\n clear() {\n box.setContent(\"{gray-fg}Select a memory to view details{/gray-fg}\");\n },\n };\n}\n","import blessed from \"blessed\";\nimport type { DashboardStats } from \"../data/data-source.js\";\nimport { formatBytes, computeLifespan } from \"../data/formatters.js\";\nimport { TYPE_COLORS } from \"../colors.js\";\nimport type { Memory } from \"../../types.js\";\n\nexport interface StatsBarWidget {\n readonly widget: blessed.Widgets.BoxElement;\n setStats(stats: DashboardStats, visibleMemories: readonly Memory[]): void;\n}\n\nexport function createStatsBar(\n screen: blessed.Widgets.Screen,\n): StatsBarWidget {\n const box = blessed.box({\n parent: screen,\n bottom: 0,\n left: 0,\n width: \"100%\",\n height: 3,\n tags: true,\n border: { type: \"line\" },\n style: {\n border: { fg: \"blue\" },\n fg: \"white\",\n },\n });\n\n return {\n widget: box,\n setStats(stats: DashboardStats, visibleMemories: readonly Memory[]) {\n const typeBreakdown = Object.entries(stats.byType)\n .filter(([, count]) => count > 0)\n .map(([type, count]) => {\n const color =\n TYPE_COLORS[type as keyof typeof TYPE_COLORS] ?? \"white\";\n return `{${color}-fg}${type}:${count}{/${color}-fg}`;\n })\n .join(\" \");\n\n const projects = Object.entries(stats.byProject)\n .map(([name, count]) => `${name}(${count})`)\n .join(\" \");\n\n const lifeCounts = { healthy: 0, fading: 0, stale: 0, session: 0 };\n for (const memory of visibleMemories) {\n lifeCounts[computeLifespan(memory).status]++;\n }\n const lifeSummary = `Life H:${lifeCounts.healthy} F:${lifeCounts.fading} S:${lifeCounts.stale} Sess:${lifeCounts.session}`;\n\n box.setContent(\n `{bold}Total:{/bold} ${stats.total} {bold}Relations:{/bold} ${stats.relations} {bold}Visible:{/bold} ${visibleMemories.length} {bold}DB:{/bold} ${formatBytes(stats.dbSizeBytes)}\\n{bold}${lifeSummary}{/bold} ${typeBreakdown} ${projects}`,\n );\n },\n };\n}\n","import blessed from \"blessed\";\n\nexport interface SearchModalWidget {\n readonly widget: blessed.Widgets.BoxElement;\n open(): void;\n close(): void;\n isOpen(): boolean;\n onSubmit(callback: (query: string) => void): void;\n onCancel(callback: () => void): void;\n}\n\nexport function createSearchModal(\n screen: blessed.Widgets.Screen,\n): SearchModalWidget {\n const box = blessed.box({\n top: \"center\",\n left: \"center\",\n width: 60,\n height: 5,\n border: { type: \"line\" },\n label: \" Search \",\n tags: true,\n hidden: true,\n style: {\n border: { fg: \"yellow\" },\n bg: \"black\",\n },\n });\n\n const input = blessed.textbox({\n parent: box,\n top: 1,\n left: 1,\n right: 1,\n height: 1,\n inputOnFocus: true,\n style: {\n fg: \"white\",\n bg: \"black\",\n },\n });\n\n screen.append(box);\n\n let submitCallback: ((query: string) => void) | null = null;\n let cancelCallback: (() => void) | null = null;\n let isOpenState = false;\n let previousFocus: blessed.Widgets.BlessedElement | null = null;\n\n function restoreFocus(): void {\n if (previousFocus) {\n previousFocus.focus();\n previousFocus = null;\n }\n }\n\n input.on(\"submit\", (value: string) => {\n isOpenState = false;\n box.hide();\n restoreFocus();\n screen.render();\n if (value.trim()) submitCallback?.(value.trim());\n });\n\n input.on(\"cancel\", () => {\n isOpenState = false;\n box.hide();\n restoreFocus();\n screen.render();\n cancelCallback?.();\n });\n\n return {\n widget: box,\n open() {\n previousFocus = screen.focused as blessed.Widgets.BlessedElement | null;\n input.clearValue();\n isOpenState = true;\n box.show();\n input.focus();\n screen.render();\n },\n close() {\n isOpenState = false;\n box.hide();\n restoreFocus();\n screen.render();\n },\n isOpen() {\n return isOpenState;\n },\n onSubmit(callback: (query: string) => void) {\n submitCallback = callback;\n },\n onCancel(callback: () => void) {\n cancelCallback = callback;\n },\n };\n}\n","import blessed from \"blessed\";\nimport type { Memory } from \"../../types.js\";\nimport { computeLifespan } from \"../data/formatters.js\";\n\nexport interface ProjectListWidget {\n readonly widget: blessed.Widgets.ListElement;\n setData(memories: readonly Memory[], activeProject?: string): void;\n onSelect(callback: (project: string | undefined) => void): void;\n}\n\ninterface ProjectRow {\n readonly project: string | undefined;\n readonly total: number;\n readonly healthPct: number;\n}\n\nexport function createProjectList(\n screen: blessed.Widgets.Screen,\n): ProjectListWidget {\n const list = blessed.list({\n parent: screen,\n top: 1,\n left: \"60%\",\n width: \"40%\",\n height: \"35%-1\",\n keys: true,\n vi: true,\n mouse: true,\n tags: true,\n border: { type: \"line\" },\n label: \" Projects \",\n scrollable: true,\n style: {\n border: { fg: \"magenta\" },\n item: { fg: \"white\" },\n selected: { fg: \"black\", bg: \"yellow\" },\n },\n });\n\n let rows: readonly ProjectRow[] = [];\n let selectCallback: ((project: string | undefined) => void) | null = null;\n let suppressSelect = false;\n\n function buildRows(memories: readonly Memory[]): readonly ProjectRow[] {\n const byProject = new Map<string, Memory[]>();\n for (const memory of memories) {\n const project = memory.project ?? \"(none)\";\n const bucket = byProject.get(project);\n if (bucket) {\n bucket.push(memory);\n } else {\n byProject.set(project, [memory]);\n }\n }\n\n const items: ProjectRow[] = [];\n for (const [project, projectMemories] of byProject.entries()) {\n const avgRemaining =\n projectMemories.reduce(\n (sum, m) => sum + computeLifespan(m).remaining,\n 0,\n ) / projectMemories.length;\n items.push({\n project,\n total: projectMemories.length,\n healthPct: Math.round(avgRemaining * 100),\n });\n }\n\n items.sort((a, b) => b.total - a.total);\n const allHealth =\n memories.length > 0\n ? Math.round(\n (memories.reduce(\n (sum, m) => sum + computeLifespan(m).remaining,\n 0,\n ) /\n memories.length) *\n 100,\n )\n : 0;\n return [\n { project: undefined, total: memories.length, healthPct: allHealth },\n ...items,\n ];\n }\n\n function render(activeProject?: string): void {\n const body = rows.map((row) => {\n const isActive =\n row.project === activeProject ||\n (row.project === undefined && !activeProject);\n const projectName = row.project ?? \"All projects\";\n const healthColor =\n row.healthPct > 62 ? \"green\" : row.healthPct > 32 ? \"yellow\" : \"red\";\n const nameCol = projectName.padEnd(20, \" \").slice(0, 20);\n const countCol = String(row.total).padStart(3, \" \");\n const healthCol = `${String(row.healthPct).padStart(3, \" \")}%`;\n return [\n isActive ? \"{bold}> {/bold}\" : \" \",\n `{bold}${nameCol}{/bold}`,\n ` {cyan-fg}${countCol} mem{/cyan-fg}`,\n ` {${healthColor}-fg}${healthCol} health{/${healthColor}-fg}`,\n ].join(\"\");\n });\n list.setItems(body);\n }\n\n function emitSelection(): void {\n if (suppressSelect) return;\n const selectedIndex = (list as unknown as { selected: number }).selected;\n const row = rows[selectedIndex];\n if (!row || !selectCallback) return;\n selectCallback(row.project);\n }\n\n list.on(\"select\", () => emitSelection());\n\n return {\n widget: list,\n setData(memories: readonly Memory[], activeProject?: string) {\n rows = buildRows(memories);\n render(activeProject);\n if (rows.length > 0) {\n suppressSelect = true;\n const idx = rows.findIndex(\n (r) =>\n r.project === activeProject ||\n (r.project === undefined && !activeProject),\n );\n list.select(Math.max(0, idx));\n setImmediate(() => {\n suppressSelect = false;\n });\n }\n },\n onSelect(callback: (project: string | undefined) => void) {\n selectCallback = callback;\n },\n };\n}\n","import blessed from \"blessed\";\n\nexport interface ProjectPickerModalWidget {\n open(projects: readonly string[], activeProject?: string): void;\n close(): void;\n isOpen(): boolean;\n onSubmit(callback: (project: string | undefined) => void): void;\n}\n\ninterface ProjectOption {\n readonly label: string;\n readonly project: string | undefined;\n}\n\nexport function createProjectPickerModal(\n screen: blessed.Widgets.Screen,\n): ProjectPickerModalWidget {\n const box = blessed.box({\n parent: screen,\n top: \"center\",\n left: \"center\",\n width: 52,\n height: 14,\n border: { type: \"line\" },\n label: \" Project Picker \",\n tags: true,\n hidden: true,\n style: {\n border: { fg: \"yellow\" },\n bg: \"black\",\n },\n });\n\n const hint = blessed.box({\n parent: box,\n top: 0,\n left: 1,\n right: 1,\n height: 1,\n tags: true,\n content: \"{gray-fg}Enter=select Esc=close{/gray-fg}\",\n });\n\n const list = blessed.list({\n parent: box,\n top: 1,\n left: 1,\n right: 1,\n bottom: 1,\n keys: true,\n vi: true,\n mouse: true,\n tags: true,\n style: {\n item: { fg: \"white\" },\n selected: { fg: \"black\", bg: \"yellow\" },\n },\n scrollbar: {\n style: { bg: \"yellow\" },\n },\n });\n\n let isOpenState = false;\n let options: readonly ProjectOption[] = [];\n let submitCallback: ((project: string | undefined) => void) | null = null;\n let previousFocus: blessed.Widgets.BlessedElement | null = null;\n\n function restoreFocus(): void {\n if (previousFocus) {\n previousFocus.focus();\n previousFocus = null;\n }\n }\n\n function emitSelection(): void {\n const selectedIndex = (list as unknown as { selected: number }).selected;\n const selected = options[selectedIndex];\n if (!selected || !submitCallback) return;\n submitCallback(selected.project);\n box.hide();\n isOpenState = false;\n restoreFocus();\n screen.render();\n }\n\n list.key([\"escape\"], () => {\n box.hide();\n isOpenState = false;\n restoreFocus();\n screen.render();\n });\n\n list.on(\"select\", () => emitSelection());\n\n return {\n open(projects: readonly string[], activeProject?: string) {\n options = [\n { label: \"All projects\", project: undefined },\n ...projects.map((project) => ({ label: project, project })),\n ];\n\n const items = options.map((option) => {\n const active =\n option.project === activeProject ||\n (option.project === undefined && !activeProject);\n return `${active ? \"> \" : \" \"}${option.label}`;\n });\n\n list.setItems(items);\n const activeIndex = options.findIndex(\n (o) =>\n o.project === activeProject ||\n (o.project === undefined && !activeProject),\n );\n list.select(Math.max(0, activeIndex));\n previousFocus = screen.focused as blessed.Widgets.BlessedElement | null;\n box.show();\n isOpenState = true;\n list.focus();\n hint.setFront();\n screen.render();\n },\n close() {\n box.hide();\n isOpenState = false;\n restoreFocus();\n screen.render();\n },\n isOpen() {\n return isOpenState;\n },\n onSubmit(callback: (project: string | undefined) => void) {\n submitCallback = callback;\n },\n };\n}\n","// -- Keybinding registry for TUI dashboard ------------------------------------\n\nimport type { Widgets } from \"blessed\";\n\nexport interface DashboardActions {\n readonly refresh: () => void;\n readonly openSearch: () => void;\n readonly closeSearch: () => void;\n readonly openProjectPicker: () => void;\n readonly filterByType: (type: string | null) => void;\n readonly cycleLifespan: () => void;\n readonly cycleProjectNext: () => void;\n readonly cycleProjectPrev: () => void;\n readonly cycleSort: () => void;\n readonly quit: () => void;\n readonly focusNext: () => void;\n readonly showHelp: () => void;\n}\n\nconst TYPE_KEYS: Record<string, string> = {\n \"1\": \"working\",\n \"2\": \"episodic\",\n \"3\": \"semantic\",\n \"4\": \"procedural\",\n \"5\": \"pattern\",\n};\n\nexport function registerKeybindings(\n screen: Widgets.Screen,\n actions: DashboardActions,\n): void {\n screen.key([\"q\"], () => actions.quit());\n screen.key([\"C-c\"], () => actions.quit());\n screen.key([\"r\"], () => actions.refresh());\n screen.key([\"/\"], () => actions.openSearch());\n screen.key([\"escape\"], () => actions.closeSearch());\n screen.key([\"tab\"], () => actions.focusNext());\n screen.key([\"p\"], () => actions.openProjectPicker());\n screen.key([\"]\", \"right\"], () => actions.cycleProjectNext());\n screen.key([\"[\", \"left\"], () => actions.cycleProjectPrev());\n screen.key([\"s\"], () => actions.cycleSort());\n screen.key([\"l\"], () => actions.cycleLifespan());\n screen.key([\"?\"], () => actions.showHelp());\n screen.key([\"0\"], () => actions.filterByType(null));\n\n for (const [key, type] of Object.entries(TYPE_KEYS)) {\n screen.key([key], () => actions.filterByType(type));\n }\n}\n"],"mappings":"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","names":["blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed","blessed"]}
package/package.json CHANGED
@@ -1,25 +1,11 @@
1
1
  {
2
2
  "name": "claude-launchpad",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "CLI toolkit that makes Claude Code setups measurably good - scaffold, diagnose, evaluate, remember",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "claude-launchpad": "./dist/cli.js"
8
8
  },
9
- "scripts": {
10
- "dev": "tsx watch src/cli.ts",
11
- "build": "tsup",
12
- "start": "tsx src/cli.ts",
13
- "test": "vitest",
14
- "test:run": "vitest run",
15
- "lint": "eslint src/ --ext .ts",
16
- "typecheck": "tsc --noEmit",
17
- "prepublishOnly": "pnpm build",
18
- "publish:dev": "npm version prerelease --preid=dev && npm publish --tag dev",
19
- "publish:release": "npm publish",
20
- "docs:dev": "cd docs && pnpm dev",
21
- "docs:build": "cd docs && pnpm build"
22
- },
23
9
  "keywords": [
24
10
  "claude",
25
11
  "claude-code",
@@ -46,7 +32,6 @@
46
32
  "engines": {
47
33
  "node": ">=22"
48
34
  },
49
- "packageManager": "pnpm@10.26.1",
50
35
  "dependencies": {
51
36
  "@inquirer/prompts": "^8.3.2",
52
37
  "chalk": "^5.6.2",
@@ -56,24 +41,32 @@
56
41
  },
57
42
  "optionalDependencies": {
58
43
  "@anthropic-ai/claude-agent-sdk": "^0.2.86",
59
- "better-sqlite3": "^11.8.0",
60
- "blessed": "^0.1.81",
61
- "sqlite-vec": "^0.1.6",
62
44
  "@modelcontextprotocol/sdk": "^1.12.0",
63
- "zod": "^3.24.0"
45
+ "zod": "^4.0.0"
64
46
  },
65
47
  "devDependencies": {
66
- "@types/blessed": "^0.1.25",
67
48
  "@types/better-sqlite3": "^7.6.12",
49
+ "@types/blessed": "^0.1.25",
68
50
  "@types/node": "^25.5.0",
51
+ "better-sqlite3": "^11.10.0",
52
+ "blessed": "^0.1.81",
53
+ "sqlite-vec": "^0.1.9",
69
54
  "tsup": "^8.5.1",
70
55
  "tsx": "^4.21.0",
71
56
  "typescript": "^6.0.2",
72
57
  "vitest": "^4.1.2"
73
58
  },
74
- "pnpm": {
75
- "onlyBuiltDependencies": [
76
- "better-sqlite3"
77
- ]
59
+ "scripts": {
60
+ "dev": "tsx watch src/cli.ts",
61
+ "build": "tsup",
62
+ "start": "tsx src/cli.ts",
63
+ "test": "vitest",
64
+ "test:run": "vitest run",
65
+ "lint": "eslint src/ --ext .ts",
66
+ "typecheck": "tsc --noEmit",
67
+ "publish:dev": "npm version prerelease --preid=dev && npm publish --tag dev",
68
+ "publish:release": "npm publish",
69
+ "docs:dev": "cd docs && pnpm dev",
70
+ "docs:build": "cd docs && pnpm build"
78
71
  }
79
- }
72
+ }
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __export = (target, all) => {
4
- for (var name in all)
5
- __defProp(target, name, { get: all[name], enumerable: true });
6
- };
7
-
8
- export {
9
- __export
10
- };
11
- //# sourceMappingURL=chunk-2H7UOFLK.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/memory/config.ts","../src/commands/memory/storage/database.ts","../src/commands/memory/storage/migrations/001-initial.ts","../src/commands/memory/storage/migrations/002-add-project.ts","../src/commands/memory/storage/migrator.ts"],"sourcesContent":["import { z } from 'zod';\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { DecayParams } from './types.js';\n\n// ── Config Schema ─────────────────────────────────────────────\n\nconst ConfigSchema = z.object({\n dataDir: z.string().default('~/.agentic-memory'),\n injectionBudget: z.number().int().min(100).max(20000).default(2000),\n consolidationInterval: z.number().int().min(1).default(10),\n enableReranker: z.boolean().default(true),\n logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('warn'),\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\n// ── Defaults ──────────────────────────────────────────────────\n\nexport const DEFAULT_CONFIG: Config = {\n dataDir: '~/.agentic-memory',\n injectionBudget: 2000,\n consolidationInterval: 10,\n enableReranker: true,\n logLevel: 'warn',\n};\n\nexport const DEFAULT_DECAY_PARAMS: DecayParams = {\n tauByType: {\n working: 0, // cleared each session, tau irrelevant\n episodic: 60, // fast decay\n semantic: 365, // slow decay\n procedural: 730, // near-permanent\n pattern: 180, // medium decay\n },\n accessModifiers: [\n { maxCount: 3, multiplier: 1.0 },\n { maxCount: 10, multiplier: 2.0 },\n { maxCount: Infinity, multiplier: 4.0 },\n ],\n relationModifier: {\n connectedThreshold: 3,\n connectedMultiplier: 0.7,\n isolatedMultiplier: 1.3,\n },\n importanceFloor: 0.05,\n pruneThreshold: 0.1,\n pruneMinAgeDays: 90,\n};\n\nexport const SCORING_WEIGHTS = {\n text: 0.35,\n importance: 0.20,\n recency: 0.20,\n access: 0.10,\n context: 0.15,\n} as const;\n\n// ── Config Loader ─────────────────────────────────────────────\n\nexport function resolveDataDir(dataDir: string): string {\n if (dataDir.startsWith('~')) {\n return join(homedir(), dataDir.slice(1));\n }\n return dataDir;\n}\n\nexport function loadConfig(overrides?: Partial<Config>): Config {\n const envOverrides: Record<string, unknown> = {};\n\n const envBudget = process.env['AGENTIC_MEMORY_INJECTION_BUDGET'];\n if (envBudget !== undefined) {\n envOverrides['injectionBudget'] = parseInt(envBudget, 10);\n }\n\n const envLogLevel = process.env['AGENTIC_MEMORY_LOG_LEVEL'];\n if (envLogLevel !== undefined) {\n envOverrides['logLevel'] = envLogLevel;\n }\n\n const envDataDir = process.env['AGENTIC_MEMORY_DATA_DIR'];\n if (envDataDir !== undefined) {\n envOverrides['dataDir'] = envDataDir;\n }\n\n // Try loading config.json from data dir\n let fileConfig: Record<string, unknown> = {};\n const baseDir = resolveDataDir(overrides?.dataDir ?? envOverrides['dataDir'] as string ?? DEFAULT_CONFIG.dataDir);\n try {\n const raw = readFileSync(join(baseDir, 'config.json'), 'utf-8');\n fileConfig = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n const isNotFound = err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'ENOENT';\n if (!isNotFound) {\n // Malformed JSON or permissions error - warn, don't silently ignore\n console.error('[agentic-memory] Failed to load config.json:', err instanceof Error ? err.message : err);\n }\n }\n\n const merged = { ...DEFAULT_CONFIG, ...fileConfig, ...envOverrides, ...overrides };\n return ConfigSchema.parse(merged);\n}\n\n// ── Token Estimation ──────────────────────────────────────────\n\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n","import Database from 'better-sqlite3';\nimport * as sqliteVec from 'sqlite-vec';\nimport { resolveDataDir } from '../config.js';\nimport { mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nexport interface DatabaseOptions {\n readonly dbPath?: string; // full path override (e.g. ':memory:' for tests)\n readonly dataDir?: string; // resolved data dir (default ~/.agentic-memory)\n}\n\nexport function createDatabase(options: DatabaseOptions = {}): Database.Database {\n const dbPath = options.dbPath ?? resolveDbPath(options.dataDir);\n\n if (dbPath !== ':memory:') {\n mkdirSync(dirname(dbPath), { recursive: true });\n }\n\n const db = new Database(dbPath);\n\n // Load sqlite-vec extension\n sqliteVec.load(db);\n\n // Configure PRAGMAs (order matters: foreign_keys before any ops, journal_mode is persistent)\n db.pragma('journal_mode = WAL');\n db.pragma('busy_timeout = 5000');\n db.pragma('foreign_keys = ON');\n db.pragma('cache_size = -64000');\n db.pragma('mmap_size = 268435456');\n db.pragma('synchronous = NORMAL');\n db.pragma('temp_store = MEMORY');\n db.pragma('journal_size_limit = 33554432');\n\n return db;\n}\n\nexport function closeDatabase(db: Database.Database): void {\n try {\n db.pragma('wal_checkpoint(TRUNCATE)');\n } catch {\n // Checkpoint may fail on :memory: - that's fine\n }\n db.close();\n}\n\nfunction resolveDbPath(dataDir?: string): string {\n const dir = resolveDataDir(dataDir ?? '~/.agentic-memory');\n return join(dir, 'memory.db');\n}\n","import type Database from 'better-sqlite3';\n\nexport const version = 1;\n\nexport function up(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT\n );\n\n CREATE TABLE IF NOT EXISTS memories (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL CHECK(type IN ('episodic','semantic','procedural','working','pattern')),\n title TEXT,\n content TEXT NOT NULL,\n context TEXT,\n source TEXT CHECK(source IN ('manual','session_end','consolidation','hook','import')),\n tags TEXT NOT NULL DEFAULT '[]',\n importance REAL NOT NULL DEFAULT 0.5 CHECK(importance >= 0.0 AND importance <= 1.0),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n access_count INTEGER NOT NULL DEFAULT 0 CHECK(access_count >= 0),\n last_accessed TEXT,\n injection_count INTEGER NOT NULL DEFAULT 0 CHECK(injection_count >= 0),\n embedding BLOB\n );\n\n CREATE TABLE IF NOT EXISTS relations (\n source_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n target_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n relation_type TEXT NOT NULL CHECK(relation_type IN (\n 'relates_to','depends_on','contradicts','extends','implements','derived_from'\n )),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (source_id, target_id, relation_type)\n );\n\n -- FTS5 external content (no data duplication)\n CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(\n title, content, tags,\n content=memories,\n content_rowid=rowid,\n tokenize='porter unicode61'\n );\n\n -- FTS5 sync triggers\n CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN\n INSERT INTO memories_fts(rowid, title, content, tags)\n VALUES (new.rowid, new.title, new.content, new.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, title, content, tags)\n VALUES ('delete', old.rowid, old.title, old.content, old.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, title, content, tags)\n VALUES ('delete', old.rowid, old.title, old.content, old.tags);\n INSERT INTO memories_fts(rowid, title, content, tags)\n VALUES (new.rowid, new.title, new.content, new.tags);\n END;\n\n -- Vector search (synced manually in application code)\n CREATE VIRTUAL TABLE IF NOT EXISTS memories_vec USING vec0(\n memory_id TEXT PRIMARY KEY,\n embedding float[384] distance_metric=cosine\n );\n\n -- Indexes\n CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);\n CREATE INDEX IF NOT EXISTS idx_memories_importance ON memories(importance);\n CREATE INDEX IF NOT EXISTS idx_memories_created_at ON memories(created_at);\n CREATE INDEX IF NOT EXISTS idx_relations_target ON relations(target_id);\n `);\n}\n","import type Database from 'better-sqlite3';\n\nexport const version = 2;\n\nexport function up(db: Database.Database): void {\n db.exec(`\n ALTER TABLE memories ADD COLUMN project TEXT;\n CREATE INDEX IF NOT EXISTS idx_memories_project ON memories(project);\n `);\n}\n","import type Database from 'better-sqlite3';\nimport * as migration001 from './migrations/001-initial.js';\nimport * as migration002 from './migrations/002-add-project.js';\n\ninterface Migration {\n readonly version: number;\n readonly up: (db: Database.Database) => void;\n}\n\nconst migrations: readonly Migration[] = [\n migration001,\n migration002,\n];\n\nexport function getSchemaVersion(db: Database.Database): number {\n try {\n const row = db.prepare(\"SELECT value FROM meta WHERE key = 'schema_version'\").get() as\n { value: string } | undefined;\n return row ? parseInt(row.value, 10) : 0;\n } catch {\n return 0;\n }\n}\n\nexport function migrate(db: Database.Database): void {\n const current = getSchemaVersion(db);\n const pending = migrations.filter(m => m.version > current);\n\n if (pending.length === 0) return;\n\n const runMigrations = db.transaction(() => {\n for (const m of pending) {\n m.up(db);\n db.prepare(\"INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)\")\n .run(String(m.version));\n }\n });\n\n runMigrations();\n}\n"],"mappings":"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","names":["join","up","version"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/memory/subcommands/install.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { createDatabase, closeDatabase } from '../storage/database.js';\nimport { migrate } from '../storage/migrator.js';\nimport { loadConfig, resolveDataDir } from '../config.js';\nimport { readSettingsJson, writeSettingsJson } from '../../../lib/settings.js';\nimport { log } from '../../../lib/output.js';\n\ninterface InstallOpts {\n readonly dbPath?: string;\n}\n\nexport async function runInstall(opts: InstallOpts): Promise<void> {\n log.blank();\n log.step('Memory system - install');\n log.blank();\n\n const config = loadConfig(opts.dbPath ? { dataDir: opts.dbPath } : undefined);\n const dataDir = resolveDataDir(config.dataDir);\n\n // Step 1: Database\n log.step('[1/4] Setting up database...');\n if (!existsSync(dataDir)) {\n mkdirSync(dataDir, { recursive: true });\n }\n const db = createDatabase({ dataDir });\n migrate(db);\n closeDatabase(db);\n log.success(`${dataDir}/memory.db ready`);\n\n // Step 2: Configure Claude Code settings\n log.step('[2/4] Configuring Claude Code...');\n await configureSettings(process.cwd());\n\n // Step 3: Register MCP server\n log.step('[3/4] Registering MCP server...');\n const registered = registerMcpServer();\n if (registered) {\n log.success('MCP server registered via `claude mcp add`');\n } else {\n log.warn('Could not register MCP server automatically.');\n log.info('Run: claude mcp add agentic-memory -- npx claude-launchpad memory serve');\n }\n\n // Step 4: CLAUDE.md + skills\n log.step('[4/4] Injecting guidance...');\n const guidanceAdded = injectClaudeMdGuidance(process.cwd());\n if (guidanceAdded) {\n log.success('Memory guidance added to CLAUDE.md');\n }\n const skillsInstalled = installSkills(process.cwd());\n if (skillsInstalled > 0) {\n log.success(`Installed ${skillsInstalled} skill(s) to .claude/skills/`);\n }\n\n log.blank();\n log.success('Memory system installed.');\n log.info('Restart your Claude Code session for the MCP server to connect.');\n log.blank();\n}\n\nasync function configureSettings(projectDir: string): Promise<void> {\n const settings = await readSettingsJson(projectDir);\n\n // Disable built-in auto-memory\n settings['autoMemoryEnabled'] = false;\n log.info('Built-in auto-memory disabled');\n\n // SessionStart hook\n const hooks = (settings['hooks'] ?? {}) as Record<string, unknown[]>;\n addSessionStartHook(hooks);\n addStopHook(hooks);\n settings['hooks'] = hooks;\n\n // Auto-allow MCP tools\n addToolPermissions(settings);\n\n await writeSettingsJson(projectDir, settings);\n log.success('settings.json updated');\n}\n\nfunction addSessionStartHook(hooks: Record<string, unknown[]>): void {\n const sessionStartHooks = (hooks['SessionStart'] ?? []) as Record<string, unknown>[];\n const hookCommand = 'npx claude-launchpad memory context --json 2>/dev/null; exit 0';\n\n const alreadyHooked = sessionStartHooks.some((h) => {\n const innerHooks = h['hooks'] as Record<string, unknown>[] | undefined;\n return innerHooks?.some(\n ih => typeof ih['command'] === 'string' && (ih['command'] as string).includes('claude-launchpad memory context'),\n );\n });\n\n if (!alreadyHooked) {\n sessionStartHooks.push({\n matcher: 'startup|resume',\n hooks: [{ type: 'command', command: hookCommand }],\n });\n hooks['SessionStart'] = sessionStartHooks;\n log.info('SessionStart hook added (injects memory context)');\n }\n}\n\nfunction addStopHook(hooks: Record<string, unknown[]>): void {\n const stopHooks = (hooks['Stop'] ?? []) as Record<string, unknown>[];\n const extractCommand = 'npx claude-launchpad memory extract 2>/dev/null; exit 0';\n\n const alreadyHasExtract = stopHooks.some((h) => {\n const innerHooks = h['hooks'] as Record<string, unknown>[] | undefined;\n return innerHooks?.some(\n ih => typeof ih['command'] === 'string' && (ih['command'] as string).includes('claude-launchpad memory extract'),\n );\n });\n\n if (!alreadyHasExtract) {\n stopHooks.push({\n hooks: [{ type: 'command', command: extractCommand, async: true }],\n });\n hooks['Stop'] = stopHooks;\n log.info('Stop hook added (extracts facts from transcript)');\n }\n}\n\nfunction addToolPermissions(settings: Record<string, unknown>): void {\n const permissions = (settings['permissions'] ?? {}) as Record<string, unknown>;\n const allowList = (permissions['allow'] ?? []) as string[];\n\n const memoryTools = [\n 'mcp__agentic-memory__memory_store',\n 'mcp__agentic-memory__memory_search',\n 'mcp__agentic-memory__memory_recent',\n 'mcp__agentic-memory__memory_forget',\n 'mcp__agentic-memory__memory_relate',\n 'mcp__agentic-memory__memory_stats',\n 'mcp__agentic-memory__memory_update',\n ];\n\n let added = 0;\n for (const tool of memoryTools) {\n if (!allowList.includes(tool)) {\n allowList.push(tool);\n added++;\n }\n }\n\n if (added > 0) {\n permissions['allow'] = allowList;\n settings['permissions'] = permissions;\n log.info(`Auto-allowed ${added} MCP tools`);\n }\n}\n\nfunction registerMcpServer(): boolean {\n try {\n execSync(\n 'claude mcp add --scope user agentic-memory -- npx claude-launchpad memory serve',\n { stdio: 'pipe', timeout: 10000 },\n );\n return true;\n } catch {\n return false;\n }\n}\n\nconst MEMORY_GUIDANCE = `\n## Memory (agentic-memory)\nThis project uses **agentic-memory** for persistent memory across sessions.\n- **DO NOT** use the built-in auto-memory system (~/.claude/projects/*/memory/)\n- Memory context is **automatically injected** at session start via SessionStart hook - no need to call memory_recent manually\n- Use \\`memory_search\\` to find specific memories by keyword\n- Use \\`memory_store\\` to save decisions, gotchas, and learnings worth remembering\n- Use \\`memory_stats\\` to check memory health\n`;\n\nfunction injectClaudeMdGuidance(projectDir: string): boolean {\n const claudeMdPath = join(projectDir, 'CLAUDE.md');\n\n let content = '';\n try {\n content = readFileSync(claudeMdPath, 'utf-8');\n } catch {\n return false;\n }\n\n if (content.includes('## Memory (agentic-memory)')) {\n return false;\n }\n\n const updated = content.trimEnd() + '\\n' + MEMORY_GUIDANCE;\n writeFileSync(claudeMdPath, updated, 'utf-8');\n return true;\n}\n\nconst MIGRATE_MEMORY_SKILL = `---\nname: lp-migrate-memory\ndescription: Migrate legacy Claude Code auto-memory files (~/.claude/projects/*/memory/*.md) into agentic-memory. Use when setting up agentic-memory on a project that already has built-in memories.\nallowed-tools: Read, Glob, Grep, mcp__agentic-memory__memory_store, mcp__agentic-memory__memory_search\n---\n\n# Migrate Legacy Claude Code Memories\n\nMigrate memory files from Claude Code's built-in auto-memory system into agentic-memory.\n\n## Steps\n\n1. **Find legacy memory files** for this project:\n - Scan \\`~/.claude/projects/*/memory/*.md\\` for directories whose slug matches the current project path\n - The slug format is the absolute path with \\`/\\` replaced by \\`-\\` and leading \\`-\\` (e.g. \\`-Users-john-projects-myapp\\`)\n - Also check \\`~/.claude/projects/*/memory/team/*.md\\` for team memories\n\n2. **For each memory file found**, read it and parse:\n - YAML frontmatter: \\`name\\`, \\`description\\`, \\`type\\` (user/feedback/project/reference)\n - Body content (everything after the frontmatter closing \\`---\\`)\n - Skip \\`MEMORY.md\\` (it's just an index file, not a memory)\n\n3. **Before storing**, check for duplicates:\n - Call \\`memory_search\\` with the memory description or first 100 chars of content\n - If a close match exists (same topic), skip it and report\n\n4. **Map types and store** each memory via \\`memory_store\\`:\n - \\`user\\` -> type: \\`semantic\\`, tags: [\\`user\\`, \\`migrated\\`], importance: 0.7\n - \\`feedback\\` -> type: \\`semantic\\`, tags: [\\`feedback\\`, \\`migrated\\`], importance: 0.8\n - \\`project\\` -> type: \\`semantic\\`, tags: [\\`project\\`, \\`migrated\\`], importance: 0.6\n - \\`reference\\` -> type: \\`semantic\\`, tags: [\\`reference\\`, \\`migrated\\`], importance: 0.5\n - Use the frontmatter \\`name\\` as the title\n - Use the body content as the memory content\n - Set source: \\`import\\`\n - Adjust importance up/down based on the content (decisions and gotchas deserve higher importance)\n\n5. **Report results**: list what was migrated, what was skipped (duplicates), and what failed\n\n## Important\n\n- Do NOT delete the original files - the user can do that manually after verifying\n- Do NOT migrate content that is purely derived from code (architecture, file structure) - it belongs in CLAUDE.md, not memory\n- If unsure about a memory's value, migrate it anyway - the decay system will naturally prune low-value memories over time\n`;\n\nconst SKILLS: Readonly<Record<string, string>> = {\n 'lp-migrate-memory': MIGRATE_MEMORY_SKILL,\n};\n\nfunction installSkills(projectDir: string): number {\n const skillsDir = join(projectDir, '.claude', 'skills');\n let installed = 0;\n\n for (const [name, content] of Object.entries(SKILLS)) {\n const skillDir = join(skillsDir, name);\n const skillPath = join(skillDir, 'SKILL.md');\n\n if (existsSync(skillPath)) continue;\n\n mkdirSync(skillDir, { recursive: true });\n writeFileSync(skillPath, content.trimStart(), 'utf-8');\n installed++;\n }\n\n return installed;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/memory/subcommands/stats.ts"],"sourcesContent":["import { existsSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { log } from '../../../lib/output.js';\nimport { initStorage } from './init-storage.js';\n\ninterface StatsOpts {\n readonly json?: boolean;\n readonly dbPath?: string;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n\nexport async function runStats(opts: StatsOpts): Promise<void> {\n const ctx = initStorage(opts.dbPath);\n\n try {\n const total = ctx.memoryRepo.count();\n const byType = ctx.memoryRepo.countByType();\n const relations = ctx.relationRepo.count();\n const dateRange = ctx.memoryRepo.dateRange();\n const topInjected = ctx.memoryRepo.topInjected(5);\n\n let dbSize = 0;\n try {\n const dbPath = join(ctx.dataDir, 'memory.db');\n if (existsSync(dbPath)) {\n dbSize = statSync(dbPath).size;\n }\n } catch { /* ignore */ }\n\n if (opts.json) {\n process.stdout.write(JSON.stringify({\n totalMemories: total,\n byType,\n totalRelations: relations,\n dbSizeBytes: dbSize,\n oldestMemory: dateRange.oldest,\n newestMemory: dateRange.newest,\n topInjected,\n }, null, 2) + '\\n');\n } else {\n log.blank();\n log.step('Memory stats');\n log.info(`Memories: ${total}`);\n for (const [type, count] of Object.entries(byType)) {\n log.info(` ${type}: ${count}`);\n }\n log.info(`Relations: ${relations}`);\n log.info(`DB size: ${formatBytes(dbSize)}`);\n log.info(`Oldest: ${dateRange.oldest ?? 'none'}`);\n log.info(`Newest: ${dateRange.newest ?? 'none'}`);\n if (topInjected.length > 0) {\n log.blank();\n log.info('Top injected:');\n for (const m of topInjected) {\n log.info(` ${m.title ?? m.id} (${m.injectionCount}x)`);\n }\n }\n log.blank();\n }\n } finally {\n ctx.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,YAAY,gBAAgB;AACrC,SAAS,YAAY;AASrB,SAAS,YAAY,OAAuB;AAC1C,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,MAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAC5D,SAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AAC9C;AAEA,eAAsB,SAAS,MAAgC;AAC7D,QAAM,MAAM,YAAY,KAAK,MAAM;AAEnC,MAAI;AACF,UAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,UAAM,SAAS,IAAI,WAAW,YAAY;AAC1C,UAAM,YAAY,IAAI,aAAa,MAAM;AACzC,UAAM,YAAY,IAAI,WAAW,UAAU;AAC3C,UAAM,cAAc,IAAI,WAAW,YAAY,CAAC;AAEhD,QAAI,SAAS;AACb,QAAI;AACF,YAAM,SAAS,KAAK,IAAI,SAAS,WAAW;AAC5C,UAAI,WAAW,MAAM,GAAG;AACtB,iBAAS,SAAS,MAAM,EAAE;AAAA,MAC5B;AAAA,IACF,QAAQ;AAAA,IAAe;AAEvB,QAAI,KAAK,MAAM;AACb,cAAQ,OAAO,MAAM,KAAK,UAAU;AAAA,QAClC,eAAe;AAAA,QACf;AAAA,QACA,gBAAgB;AAAA,QAChB,aAAa;AAAA,QACb,cAAc,UAAU;AAAA,QACxB,cAAc,UAAU;AAAA,QACxB;AAAA,MACF,GAAG,MAAM,CAAC,IAAI,IAAI;AAAA,IACpB,OAAO;AACL,UAAI,MAAM;AACV,UAAI,KAAK,cAAc;AACvB,UAAI,KAAK,eAAe,KAAK,EAAE;AAC/B,iBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAClD,YAAI,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE;AAAA,MAChC;AACA,UAAI,KAAK,eAAe,SAAS,EAAE;AACnC,UAAI,KAAK,eAAe,YAAY,MAAM,CAAC,EAAE;AAC7C,UAAI,KAAK,eAAe,UAAU,UAAU,MAAM,EAAE;AACpD,UAAI,KAAK,eAAe,UAAU,UAAU,MAAM,EAAE;AACpD,UAAI,YAAY,SAAS,GAAG;AAC1B,YAAI,MAAM;AACV,YAAI,KAAK,eAAe;AACxB,mBAAW,KAAK,aAAa;AAC3B,cAAI,KAAK,KAAK,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,cAAc,IAAI;AAAA,QACxD;AAAA,MACF;AACA,UAAI,MAAM;AAAA,IACZ;AAAA,EACF,UAAE;AACA,QAAI,MAAM;AAAA,EACZ;AACF;","names":[]}