gnosys 5.2.3 → 5.2.5

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.
package/dist/cli.js CHANGED
@@ -177,37 +177,39 @@ program
177
177
  }
178
178
  return;
179
179
  }
180
- // Legacy file-based discover path
181
- const resolver = await getResolver();
182
- const stores = resolver.getStores();
183
- if (stores.length === 0) {
184
- console.error("No Gnosys stores found.");
185
- process.exit(1);
180
+ // DB-based discover path — uses central DB FTS5
181
+ let centralDb = null;
182
+ try {
183
+ centralDb = GnosysDB.openCentral();
184
+ if (!centralDb.isAvailable()) {
185
+ console.error("Central DB not available. Run 'gnosys init' first.");
186
+ process.exit(1);
187
+ }
188
+ const results = centralDb.discoverFts(query, parseInt(opts.limit));
189
+ if (results.length === 0) {
190
+ outputResult(!!opts.json, { query, results: [] }, () => {
191
+ console.log(`No memories found for "${query}". Try gnosys search for full-text.`);
192
+ });
193
+ return;
194
+ }
195
+ outputResult(!!opts.json, { query, count: results.length, results }, () => {
196
+ console.log(`Found ${results.length} relevant memories for "${query}":\n`);
197
+ for (const r of results) {
198
+ console.log(` ${r.title}`);
199
+ console.log(` id: ${r.id}`);
200
+ if (r.relevance)
201
+ console.log(` Relevance: ${r.relevance}`);
202
+ console.log();
203
+ }
204
+ });
186
205
  }
187
- const search = new GnosysSearch(stores[0].path);
188
- search.clearIndex();
189
- for (const s of stores) {
190
- await search.addStoreMemories(s.store, s.label);
206
+ catch (err) {
207
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
208
+ process.exit(1);
191
209
  }
192
- const results = search.discover(query, parseInt(opts.limit));
193
- if (results.length === 0) {
194
- outputResult(!!opts.json, { query, results: [] }, () => {
195
- console.log(`No memories found for "${query}". Try gnosys search for full-text.`);
196
- });
197
- search.close();
198
- return;
210
+ finally {
211
+ centralDb?.close();
199
212
  }
200
- outputResult(!!opts.json, { query, count: results.length, results }, () => {
201
- console.log(`Found ${results.length} relevant memories for "${query}":\n`);
202
- for (const r of results) {
203
- console.log(` ${r.title}`);
204
- console.log(` ${r.relative_path}`);
205
- if (r.relevance)
206
- console.log(` Relevance: ${r.relevance}`);
207
- console.log();
208
- }
209
- });
210
- search.close();
211
213
  });
212
214
  // ─── gnosys search <query> ───────────────────────────────────────────────
213
215
  program
@@ -261,36 +263,38 @@ program
261
263
  }
262
264
  return;
263
265
  }
264
- // Legacy file-based search path
265
- const resolver = await getResolver();
266
- const stores = resolver.getStores();
267
- if (stores.length === 0) {
268
- console.error("No Gnosys stores found.");
269
- process.exit(1);
266
+ // DB-based search path — uses central DB FTS5
267
+ let centralDb = null;
268
+ try {
269
+ centralDb = GnosysDB.openCentral();
270
+ if (!centralDb.isAvailable()) {
271
+ console.error("Central DB not available. Run 'gnosys init' first.");
272
+ process.exit(1);
273
+ }
274
+ const results = centralDb.searchFts(query, parseInt(opts.limit));
275
+ if (results.length === 0) {
276
+ outputResult(!!opts.json, { query, results: [] }, () => {
277
+ console.log(`No results for "${query}".`);
278
+ });
279
+ return;
280
+ }
281
+ outputResult(!!opts.json, { query, count: results.length, results }, () => {
282
+ console.log(`Found ${results.length} results for "${query}":\n`);
283
+ for (const r of results) {
284
+ console.log(` ${r.title}`);
285
+ console.log(` id: ${r.id}`);
286
+ console.log(` ${r.snippet.replace(/>>>/g, "").replace(/<<</g, "")}`);
287
+ console.log();
288
+ }
289
+ });
270
290
  }
271
- const search = new GnosysSearch(stores[0].path);
272
- search.clearIndex();
273
- for (const s of stores) {
274
- await search.addStoreMemories(s.store, s.label);
291
+ catch (err) {
292
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
293
+ process.exit(1);
275
294
  }
276
- const results = search.search(query, parseInt(opts.limit));
277
- if (results.length === 0) {
278
- outputResult(!!opts.json, { query, results: [] }, () => {
279
- console.log(`No results for "${query}".`);
280
- });
281
- search.close();
282
- return;
295
+ finally {
296
+ centralDb?.close();
283
297
  }
284
- outputResult(!!opts.json, { query, count: results.length, results }, () => {
285
- console.log(`Found ${results.length} results for "${query}":\n`);
286
- for (const r of results) {
287
- console.log(` ${r.title}`);
288
- console.log(` ${r.relative_path}`);
289
- console.log(` ${r.snippet.replace(/>>>/g, "").replace(/<<</g, "")}`);
290
- console.log();
291
- }
292
- });
293
- search.close();
294
298
  });
295
299
  // ─── gnosys list ─────────────────────────────────────────────────────────
296
300
  program
@@ -298,43 +302,67 @@ program
298
302
  .description("List all memories across all stores")
299
303
  .option("-c, --category <category>", "Filter by category")
300
304
  .option("-t, --tag <tag>", "Filter by tag")
301
- .option("-s, --store <store>", "Filter by store layer")
305
+ .option("-s, --store <store>", "Filter by store layer (project|user|global)")
302
306
  .option("--json", "Output as JSON")
303
307
  .action(async (opts) => {
304
- const resolver = await getResolver();
305
- let memories = await resolver.getAllMemories();
306
- if (opts.store) {
307
- memories = memories.filter((m) => m.sourceLayer === opts.store || m.sourceLabel === opts.store);
308
+ let centralDb = null;
309
+ try {
310
+ centralDb = GnosysDB.openCentral();
311
+ if (!centralDb.isAvailable()) {
312
+ console.error("Central DB not available. Run 'gnosys init' first.");
313
+ process.exit(1);
314
+ }
315
+ // Detect current project to scope the listing
316
+ const projIdentity = await findProjectIdentity(process.cwd());
317
+ const projectId = projIdentity?.identity.projectId || null;
318
+ let memories = centralDb.getActiveMemories();
319
+ // Filter to current project's memories (plus user/global scope)
320
+ if (projectId) {
321
+ memories = memories.filter((m) => m.project_id === projectId || m.scope === "user" || m.scope === "global");
322
+ }
323
+ if (opts.store) {
324
+ memories = memories.filter((m) => m.scope === opts.store);
325
+ }
326
+ if (opts.category) {
327
+ memories = memories.filter((m) => m.category === opts.category);
328
+ }
329
+ if (opts.tag) {
330
+ memories = memories.filter((m) => {
331
+ try {
332
+ const tags = JSON.parse(m.tags || "[]");
333
+ return tags.includes(opts.tag);
334
+ }
335
+ catch {
336
+ return false;
337
+ }
338
+ });
339
+ }
340
+ outputResult(!!opts.json, {
341
+ count: memories.length,
342
+ memories: memories.map((m) => ({
343
+ id: m.id,
344
+ title: m.title,
345
+ category: m.category,
346
+ status: m.status,
347
+ scope: m.scope,
348
+ confidence: m.confidence,
349
+ })),
350
+ }, () => {
351
+ console.log(`${memories.length} memories:\n`);
352
+ for (const m of memories) {
353
+ console.log(` [${m.scope}] [${m.status}] ${m.title}`);
354
+ console.log(` id: ${m.id} | category: ${m.category} | confidence: ${m.confidence}`);
355
+ console.log();
356
+ }
357
+ });
308
358
  }
309
- if (opts.category) {
310
- memories = memories.filter((m) => m.frontmatter.category === opts.category);
359
+ catch (err) {
360
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
361
+ process.exit(1);
311
362
  }
312
- if (opts.tag) {
313
- memories = memories.filter((m) => {
314
- const tags = Array.isArray(m.frontmatter.tags)
315
- ? m.frontmatter.tags
316
- : Object.values(m.frontmatter.tags).flat();
317
- return tags.includes(opts.tag);
318
- });
363
+ finally {
364
+ centralDb?.close();
319
365
  }
320
- outputResult(!!opts.json, {
321
- count: memories.length,
322
- memories: memories.map((m) => ({
323
- id: m.frontmatter.id,
324
- title: m.frontmatter.title,
325
- category: m.frontmatter.category,
326
- status: m.frontmatter.status,
327
- source: m.sourceLabel,
328
- path: `${m.sourceLabel}:${m.relativePath}`,
329
- })),
330
- }, () => {
331
- console.log(`${memories.length} memories:\n`);
332
- for (const m of memories) {
333
- console.log(` [${m.sourceLabel}] [${m.frontmatter.status}] ${m.frontmatter.title}`);
334
- console.log(` ${m.sourceLabel}:${m.relativePath}`);
335
- console.log();
336
- }
337
- });
338
366
  });
339
367
  // ─── gnosys add <input> ──────────────────────────────────────────────────
340
368
  program
@@ -1504,34 +1532,77 @@ program
1504
1532
  .description("Show summary statistics for the memory store")
1505
1533
  .option("--json", "Output as JSON")
1506
1534
  .action(async (opts) => {
1507
- const resolver = await getResolver();
1508
- const allMemories = await resolver.getAllMemories();
1509
- if (allMemories.length === 0) {
1510
- outputResult(!!opts.json, { totalCount: 0 }, () => {
1511
- console.log("No memories found.");
1535
+ let centralDb = null;
1536
+ try {
1537
+ centralDb = GnosysDB.openCentral();
1538
+ if (!centralDb.isAvailable()) {
1539
+ console.error("Central DB not available. Run 'gnosys init' first.");
1540
+ process.exit(1);
1541
+ }
1542
+ const projIdentity = await findProjectIdentity(process.cwd());
1543
+ const projectId = projIdentity?.identity.projectId || null;
1544
+ let dbMemories = centralDb.getActiveMemories();
1545
+ if (projectId) {
1546
+ dbMemories = dbMemories.filter((m) => m.project_id === projectId || m.scope === "user" || m.scope === "global");
1547
+ }
1548
+ if (dbMemories.length === 0) {
1549
+ outputResult(!!opts.json, { totalCount: 0 }, () => {
1550
+ console.log("No memories found.");
1551
+ });
1552
+ return;
1553
+ }
1554
+ // Convert DbMemory[] to Memory[] shape for computeStats
1555
+ const allMemories = dbMemories.map((m) => ({
1556
+ frontmatter: {
1557
+ id: m.id,
1558
+ title: m.title,
1559
+ category: m.category,
1560
+ tags: (() => { try {
1561
+ return JSON.parse(m.tags || "[]");
1562
+ }
1563
+ catch {
1564
+ return [];
1565
+ } })(),
1566
+ relevance: m.relevance,
1567
+ author: m.author,
1568
+ authority: m.authority,
1569
+ confidence: m.confidence,
1570
+ created: m.created,
1571
+ modified: m.modified,
1572
+ status: m.status,
1573
+ },
1574
+ content: m.content,
1575
+ filePath: "",
1576
+ relativePath: "",
1577
+ }));
1578
+ const stats = computeStats(allMemories);
1579
+ outputResult(!!opts.json, stats, () => {
1580
+ console.log(`Gnosys Store Statistics:\n`);
1581
+ console.log(` Total memories: ${stats.totalCount}`);
1582
+ console.log(` Average confidence: ${stats.averageConfidence}`);
1583
+ console.log(` Date range: ${stats.oldestCreated} → ${stats.newestCreated}`);
1584
+ console.log(` Last modified: ${stats.lastModified}`);
1585
+ console.log(`\n By category:`);
1586
+ for (const [cat, count] of Object.entries(stats.byCategory).sort((a, b) => b[1] - a[1])) {
1587
+ console.log(` ${cat}: ${count}`);
1588
+ }
1589
+ console.log(`\n By status:`);
1590
+ for (const [st, count] of Object.entries(stats.byStatus)) {
1591
+ console.log(` ${st}: ${count}`);
1592
+ }
1593
+ console.log(`\n By author:`);
1594
+ for (const [author, count] of Object.entries(stats.byAuthor)) {
1595
+ console.log(` ${author}: ${count}`);
1596
+ }
1512
1597
  });
1513
- return;
1514
1598
  }
1515
- const stats = computeStats(allMemories);
1516
- outputResult(!!opts.json, stats, () => {
1517
- console.log(`Gnosys Store Statistics:\n`);
1518
- console.log(` Total memories: ${stats.totalCount}`);
1519
- console.log(` Average confidence: ${stats.averageConfidence}`);
1520
- console.log(` Date range: ${stats.oldestCreated} → ${stats.newestCreated}`);
1521
- console.log(` Last modified: ${stats.lastModified}`);
1522
- console.log(`\n By category:`);
1523
- for (const [cat, count] of Object.entries(stats.byCategory).sort((a, b) => b[1] - a[1])) {
1524
- console.log(` ${cat}: ${count}`);
1525
- }
1526
- console.log(`\n By status:`);
1527
- for (const [st, count] of Object.entries(stats.byStatus)) {
1528
- console.log(` ${st}: ${count}`);
1529
- }
1530
- console.log(`\n By author:`);
1531
- for (const [author, count] of Object.entries(stats.byAuthor)) {
1532
- console.log(` ${author}: ${count}`);
1533
- }
1534
- });
1599
+ catch (err) {
1600
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
1601
+ process.exit(1);
1602
+ }
1603
+ finally {
1604
+ centralDb?.close();
1605
+ }
1535
1606
  });
1536
1607
  // ─── gnosys links <path> ─────────────────────────────────────────────────
1537
1608
  program
@@ -2289,17 +2360,16 @@ program
2289
2360
  process.exit(1);
2290
2361
  }
2291
2362
  const cfg = await loadConfig(stores[0].path);
2292
- // v2.0: Try to open GnosysDB for dashboard stats
2363
+ // v5.1: Use central DB for dashboard stats
2293
2364
  let dashDb;
2294
2365
  try {
2295
- const { GnosysDB: DbClass } = await import("./lib/db.js");
2296
- const db = new DbClass(stores[0].path);
2366
+ const db = GnosysDB.openCentral();
2297
2367
  if (db.isAvailable() && db.isMigrated()) {
2298
2368
  dashDb = db;
2299
2369
  }
2300
2370
  }
2301
2371
  catch {
2302
- // GnosysDB not available — legacy dashboard only
2372
+ // Central DB not available — legacy dashboard only
2303
2373
  }
2304
2374
  const data = await collectDashboardData(resolver, cfg, pkg.version, dashDb);
2305
2375
  if (opts.json) {
@@ -2735,7 +2805,28 @@ program
2735
2805
  .action(async (opts) => {
2736
2806
  const projectDir = opts.directory ? path.resolve(opts.directory) : process.cwd();
2737
2807
  const storePath = path.join(projectDir, ".gnosys");
2738
- const cfg = await loadConfig(storePath);
2808
+ const globalStorePath = path.join(os.homedir(), ".gnosys");
2809
+ // Load config: try project-level first, fall back to global ~/.gnosys/
2810
+ let cfg;
2811
+ let configSource;
2812
+ try {
2813
+ const projectCfg = await loadConfig(storePath);
2814
+ // Check if it's just defaults (no actual config file) by seeing if dream has been configured
2815
+ const hasProjectConfig = projectCfg.dream?.provider !== DEFAULT_CONFIG.dream?.provider ||
2816
+ projectCfg.llm?.defaultProvider !== DEFAULT_CONFIG.llm?.defaultProvider;
2817
+ if (hasProjectConfig) {
2818
+ cfg = projectCfg;
2819
+ configSource = storePath;
2820
+ }
2821
+ else {
2822
+ cfg = await loadConfig(globalStorePath);
2823
+ configSource = globalStorePath;
2824
+ }
2825
+ }
2826
+ catch {
2827
+ cfg = await loadConfig(globalStorePath);
2828
+ configSource = globalStorePath;
2829
+ }
2739
2830
  const GREEN = "\x1b[32m";
2740
2831
  const RED = "\x1b[31m";
2741
2832
  const YELLOW = "\x1b[33m";
@@ -2745,7 +2836,7 @@ program
2745
2836
  const CHECK = `${GREEN}✓${RESET}`;
2746
2837
  const CROSS = `${RED}✗${RESET}`;
2747
2838
  const WARN = `${YELLOW}⚠${RESET}`;
2748
- console.log(`\n${BOLD}Gnosys LLM Check${RESET}\n`);
2839
+ console.log(`\n${BOLD}Gnosys LLM Check${RESET} ${DIM}(config: ${configSource})${RESET}\n`);
2749
2840
  const tasks = [
2750
2841
  {
2751
2842
  name: "structuring",