gnosys 5.0.0 → 5.1.0
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 +355 -148
- package/dist/cli.js.map +1 -1
- package/dist/index.js +178 -80
- package/dist/index.js.map +1 -1
- package/dist/lib/archive.d.ts +3 -2
- package/dist/lib/archive.d.ts.map +1 -1
- package/dist/lib/archive.js +11 -6
- package/dist/lib/archive.js.map +1 -1
- package/dist/lib/db.d.ts +11 -0
- package/dist/lib/db.d.ts.map +1 -1
- package/dist/lib/db.js +34 -0
- package/dist/lib/db.js.map +1 -1
- package/dist/lib/dbWrite.d.ts.map +1 -1
- package/dist/lib/dbWrite.js +11 -3
- package/dist/lib/dbWrite.js.map +1 -1
- package/dist/lib/import.d.ts +2 -1
- package/dist/lib/import.d.ts.map +1 -1
- package/dist/lib/import.js +8 -13
- package/dist/lib/import.js.map +1 -1
- package/dist/lib/maintenance.d.ts +5 -3
- package/dist/lib/maintenance.d.ts.map +1 -1
- package/dist/lib/maintenance.js +27 -26
- package/dist/lib/maintenance.js.map +1 -1
- package/dist/lib/projectIdentity.d.ts +25 -0
- package/dist/lib/projectIdentity.d.ts.map +1 -1
- package/dist/lib/projectIdentity.js +95 -0
- package/dist/lib/projectIdentity.js.map +1 -1
- package/dist/lib/rulesGen.js +1 -1
- package/dist/lib/search.d.ts +12 -0
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/lib/search.js +33 -0
- package/dist/lib/search.js.map +1 -1
- package/dist/lib/setup.d.ts.map +1 -1
- package/dist/lib/setup.js +96 -36
- package/dist/lib/setup.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -311,6 +311,53 @@ server.tool("gnosys_list", "List memories across all stores, optionally filtered
|
|
|
311
311
|
projectRoot: projectRootParam,
|
|
312
312
|
}, async ({ category, tag, store: storeFilter, status, projectRoot }) => {
|
|
313
313
|
const ctx = await resolveToolContext(projectRoot);
|
|
314
|
+
// DB-first: read from central DB instead of scanning markdown files
|
|
315
|
+
const db = ctx.centralDb;
|
|
316
|
+
if (db?.isAvailable()) {
|
|
317
|
+
let dbMemories = status === "active" || !status
|
|
318
|
+
? db.getActiveMemories()
|
|
319
|
+
: db.getAllMemories();
|
|
320
|
+
// Apply filters on DB results
|
|
321
|
+
if (status && status !== "active") {
|
|
322
|
+
dbMemories = dbMemories.filter((m) => m.status === status);
|
|
323
|
+
}
|
|
324
|
+
if (storeFilter) {
|
|
325
|
+
dbMemories = dbMemories.filter((m) => m.scope === storeFilter);
|
|
326
|
+
}
|
|
327
|
+
if (category) {
|
|
328
|
+
dbMemories = dbMemories.filter((m) => m.category === category);
|
|
329
|
+
}
|
|
330
|
+
if (tag) {
|
|
331
|
+
dbMemories = dbMemories.filter((m) => {
|
|
332
|
+
try {
|
|
333
|
+
const parsed = JSON.parse(m.tags || "[]");
|
|
334
|
+
const tagList = Array.isArray(parsed)
|
|
335
|
+
? parsed
|
|
336
|
+
: Object.values(parsed).flat();
|
|
337
|
+
return tagList.includes(tag);
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
// Filter by project if we have a project ID (so scoped queries only see their project)
|
|
345
|
+
if (ctx.projectId && !storeFilter) {
|
|
346
|
+
dbMemories = dbMemories.filter((m) => m.project_id === ctx.projectId || m.scope !== "project");
|
|
347
|
+
}
|
|
348
|
+
const lines = dbMemories.map((m) => `- [${m.scope}] **${m.title}** (${m.category}/${m.id}) [${m.status}]`);
|
|
349
|
+
return {
|
|
350
|
+
content: [
|
|
351
|
+
{
|
|
352
|
+
type: "text",
|
|
353
|
+
text: lines.length > 0
|
|
354
|
+
? `${lines.length} memories:\n\n${lines.join("\n")}`
|
|
355
|
+
: "No memories match the filter.",
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
// Fallback: read from markdown files if central DB unavailable
|
|
314
361
|
let memories = await ctx.resolver.getAllMemories();
|
|
315
362
|
if (storeFilter) {
|
|
316
363
|
memories = memories.filter((m) => m.sourceLayer === storeFilter || m.sourceLabel === storeFilter);
|
|
@@ -384,7 +431,13 @@ server.tool("gnosys_add", "Add a new memory. Accepts raw text — an LLM structu
|
|
|
384
431
|
}
|
|
385
432
|
try {
|
|
386
433
|
const result = await ingestion.ingest(input);
|
|
387
|
-
|
|
434
|
+
if (!ctx.gnosysDb?.isAvailable()) {
|
|
435
|
+
return {
|
|
436
|
+
content: [{ type: "text", text: "Database not available. Cannot write memory." }],
|
|
437
|
+
isError: true,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
const id = ctx.gnosysDb.getNextId(result.category, ctx.projectId ?? undefined);
|
|
388
441
|
const today = new Date().toISOString().split("T")[0];
|
|
389
442
|
const frontmatter = {
|
|
390
443
|
id,
|
|
@@ -401,19 +454,15 @@ server.tool("gnosys_add", "Add a new memory. Accepts raw text — an LLM structu
|
|
|
401
454
|
status: "active",
|
|
402
455
|
supersedes: null,
|
|
403
456
|
};
|
|
404
|
-
const filename = `${result.filename}.md`;
|
|
405
457
|
const content = `# ${result.title}\n\n${result.content}`;
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
syncMemoryToDb(ctx.gnosysDb, frontmatter, content, relativePath);
|
|
410
|
-
auditToDb(ctx.gnosysDb, "write", id, { tool: "gnosys_add", category: result.category });
|
|
411
|
-
}
|
|
458
|
+
// Write to DB only (SQLite is sole source of truth)
|
|
459
|
+
syncMemoryToDb(ctx.gnosysDb, frontmatter, content, undefined, ctx.projectId, "project");
|
|
460
|
+
auditToDb(ctx.gnosysDb, "write", id, { tool: "gnosys_add", category: result.category });
|
|
412
461
|
// Rebuild search index across all stores
|
|
413
462
|
if (ctx.search) {
|
|
414
463
|
await reindexAllStores();
|
|
415
464
|
}
|
|
416
|
-
let response = `Memory added to [${writeTarget.label}]: **${result.title}**\
|
|
465
|
+
let response = `Memory added to [${writeTarget.label}]: **${result.title}**\nID: ${id}\nCategory: ${result.category}\nConfidence: ${result.confidence}`;
|
|
417
466
|
if (result.proposedNewTags && result.proposedNewTags.length > 0) {
|
|
418
467
|
const proposed = result.proposedNewTags
|
|
419
468
|
.map((t) => `${t.category}:${t.tag}`)
|
|
@@ -424,7 +473,7 @@ server.tool("gnosys_add", "Add a new memory. Accepts raw text — an LLM structu
|
|
|
424
473
|
if (ctx.search && result.relevance) {
|
|
425
474
|
const related = ctx.search.discover(result.relevance.split(" ").slice(0, 5).join(" "), 5);
|
|
426
475
|
// Filter out the memory we just added
|
|
427
|
-
const overlaps = related.filter((r) =>
|
|
476
|
+
const overlaps = related.filter((r) => r.title !== result.title);
|
|
428
477
|
if (overlaps.length > 0) {
|
|
429
478
|
response += `\n\n⚠️ Potential overlaps detected — review these for contradictions:`;
|
|
430
479
|
for (const o of overlaps.slice(0, 3)) {
|
|
@@ -475,12 +524,13 @@ server.tool("gnosys_add_structured", "Add a memory with structured input (no LLM
|
|
|
475
524
|
isError: true,
|
|
476
525
|
};
|
|
477
526
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
527
|
+
if (!ctx.gnosysDb?.isAvailable()) {
|
|
528
|
+
return {
|
|
529
|
+
content: [{ type: "text", text: "Database not available. Cannot write memory." }],
|
|
530
|
+
isError: true,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
const id = ctx.gnosysDb.getNextId(category, ctx.projectId ?? undefined);
|
|
484
534
|
const today = new Date().toISOString().split("T")[0];
|
|
485
535
|
const frontmatter = {
|
|
486
536
|
id,
|
|
@@ -498,19 +548,16 @@ server.tool("gnosys_add_structured", "Add a memory with structured input (no LLM
|
|
|
498
548
|
supersedes: null,
|
|
499
549
|
};
|
|
500
550
|
const fullContent = `# ${title}\n\n${content}`;
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
syncMemoryToDb(ctx.gnosysDb, frontmatter, fullContent, relativePath);
|
|
505
|
-
auditToDb(ctx.gnosysDb, "write", id, { tool: "gnosys_add_structured", category });
|
|
506
|
-
}
|
|
551
|
+
// Write to DB only (SQLite is sole source of truth)
|
|
552
|
+
syncMemoryToDb(ctx.gnosysDb, frontmatter, fullContent, undefined, ctx.projectId, "project");
|
|
553
|
+
auditToDb(ctx.gnosysDb, "write", id, { tool: "gnosys_add_structured", category });
|
|
507
554
|
if (ctx.search)
|
|
508
555
|
await reindexAllStores();
|
|
509
556
|
return {
|
|
510
557
|
content: [
|
|
511
558
|
{
|
|
512
559
|
type: "text",
|
|
513
|
-
text: `Memory added to [${writeTarget.label}]: **${title}**\
|
|
560
|
+
text: `Memory added to [${writeTarget.label}]: **${title}**\nID: ${id}`,
|
|
514
561
|
},
|
|
515
562
|
],
|
|
516
563
|
};
|
|
@@ -580,14 +627,9 @@ server.tool("gnosys_reinforce", "Signal whether a memory was useful. 'useful' re
|
|
|
580
627
|
const sourceStore = ctx.resolver
|
|
581
628
|
.getStores()
|
|
582
629
|
.find((s) => s.label === memory.sourceLabel);
|
|
583
|
-
if (sourceStore
|
|
630
|
+
if (sourceStore) {
|
|
584
631
|
const count = (memory.frontmatter.reinforcement_count || 0) + 1;
|
|
585
|
-
|
|
586
|
-
modified: new Date().toISOString().split("T")[0],
|
|
587
|
-
reinforcement_count: count,
|
|
588
|
-
last_reinforced: new Date().toISOString().split("T")[0],
|
|
589
|
-
});
|
|
590
|
-
// v2.0: Sync reinforcement to gnosys.db
|
|
632
|
+
// Write reinforcement to DB only (SQLite is sole source of truth)
|
|
591
633
|
if (ctx.gnosysDb?.isAvailable()) {
|
|
592
634
|
syncReinforcementToDb(ctx.gnosysDb, memory_id, count);
|
|
593
635
|
auditToDb(ctx.gnosysDb, "reinforce", memory_id, { signal, context });
|
|
@@ -603,7 +645,7 @@ server.tool("gnosys_reinforce", "Signal whether a memory was useful. 'useful' re
|
|
|
603
645
|
return { content: [{ type: "text", text: messages[signal] }] };
|
|
604
646
|
});
|
|
605
647
|
// ─── Tool: gnosys_init ───────────────────────────────────────────────────
|
|
606
|
-
server.tool("gnosys_init", "Initialize Gnosys in a project directory. Creates .gnosys/ with project identity (gnosys.json), registers the project in the central DB (~/.gnosys/gnosys.db), and sets up tag registry
|
|
648
|
+
server.tool("gnosys_init", "Initialize Gnosys in a project directory. Creates .gnosys/ with project identity (gnosys.json), registers the project in the central DB (~/.gnosys/gnosys.db), and sets up tag registry. You MUST run this before any other Gnosys tool in a new project. Pass the full absolute path to the project root.", {
|
|
607
649
|
directory: z
|
|
608
650
|
.string()
|
|
609
651
|
.describe("Absolute path to the project directory to initialize. Required."),
|
|
@@ -623,7 +665,7 @@ server.tool("gnosys_init", "Initialize Gnosys in a project directory. Creates .g
|
|
|
623
665
|
// Good — doesn't exist yet
|
|
624
666
|
}
|
|
625
667
|
if (!isResync) {
|
|
626
|
-
// Create directory structure
|
|
668
|
+
// Create directory structure (DB is sole source of truth — no category folders or changelog)
|
|
627
669
|
await fs.mkdir(storePath, { recursive: true });
|
|
628
670
|
await fs.mkdir(path.join(storePath, ".config"), { recursive: true });
|
|
629
671
|
// Seed default tag registry
|
|
@@ -640,22 +682,6 @@ server.tool("gnosys_init", "Initialize Gnosys in a project directory. Creates .g
|
|
|
640
682
|
status_tag: ["draft", "stable", "deprecated", "experimental"],
|
|
641
683
|
};
|
|
642
684
|
await fs.writeFile(path.join(storePath, ".config", "tags.json"), JSON.stringify(defaultRegistry, null, 2), "utf-8");
|
|
643
|
-
// Seed changelog
|
|
644
|
-
const changelog = `# Gnosys Changelog\n\n## ${new Date().toISOString().split("T")[0]}\n\n- Store initialized\n`;
|
|
645
|
-
await fs.writeFile(path.join(storePath, "CHANGELOG.md"), changelog, "utf-8");
|
|
646
|
-
// Init git
|
|
647
|
-
try {
|
|
648
|
-
const { execSync } = await import("child_process");
|
|
649
|
-
execSync("git init", { cwd: storePath, stdio: "pipe" });
|
|
650
|
-
execSync("git add -A", { cwd: storePath, stdio: "pipe" });
|
|
651
|
-
execSync('git commit -m "Initialize Gnosys store"', {
|
|
652
|
-
cwd: storePath,
|
|
653
|
-
stdio: "pipe",
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
catch {
|
|
657
|
-
// Git not available — that's fine
|
|
658
|
-
}
|
|
659
685
|
}
|
|
660
686
|
// v3.0: Create/update project identity and register in central DB
|
|
661
687
|
const identity = await createProjectIdentity(targetDir, {
|
|
@@ -680,11 +706,82 @@ server.tool("gnosys_init", "Initialize Gnosys in a project directory. Creates .g
|
|
|
680
706
|
content: [
|
|
681
707
|
{
|
|
682
708
|
type: "text",
|
|
683
|
-
text: `Gnosys store ${action} at ${storePath}\n\nProject Identity:\n- ID: ${identity.projectId}\n- Name: ${identity.projectName}\n- Directory: ${identity.workingDirectory}\n- Agent rules target: ${identity.agentRulesTarget || "none detected"}\n- Central DB: ${centralDb?.isAvailable() ? "registered ✓" : "not available"}\n\n${isResync ? "Identity re-synced." : "Created:\n- gnosys.json (project identity)\n- .config/ (internal config)\n- tags.json (tag registry)
|
|
709
|
+
text: `Gnosys store ${action} at ${storePath}\n\nProject Identity:\n- ID: ${identity.projectId}\n- Name: ${identity.projectName}\n- Directory: ${identity.workingDirectory}\n- Agent rules target: ${identity.agentRulesTarget || "none detected"}\n- Central DB: ${centralDb?.isAvailable() ? "registered ✓" : "not available"}\n\n${isResync ? "Identity re-synced." : "Created:\n- gnosys.json (project identity)\n- .config/ (internal config)\n- tags.json (tag registry)"}\n\nThe store is ready. Use gnosys_discover to find existing memories or gnosys_add to create new ones.`,
|
|
684
710
|
},
|
|
685
711
|
],
|
|
686
712
|
};
|
|
687
713
|
});
|
|
714
|
+
// ─── Tool: gnosys_migrate ────────────────────────────────────────────────
|
|
715
|
+
server.tool("gnosys_migrate", "Migrate a Gnosys store (.gnosys/) from one directory to another. Updates the project name, working directory, and central DB registration. Use this when a project has moved or you want to consolidate stores.", {
|
|
716
|
+
sourcePath: z.string().describe("Directory that currently contains .gnosys/ (absolute path)"),
|
|
717
|
+
targetPath: z.string().describe("Directory to move .gnosys/ into (absolute path)"),
|
|
718
|
+
newName: z.string().optional().describe("New project name (default: basename of target directory)"),
|
|
719
|
+
syncMemories: z.boolean().optional().default(false).describe("Sync markdown memories into central DB after migration"),
|
|
720
|
+
deleteOld: z.boolean().optional().default(false).describe("Delete the old .gnosys/ directory after successful migration"),
|
|
721
|
+
}, async ({ sourcePath, targetPath, newName, syncMemories, deleteOld }) => {
|
|
722
|
+
try {
|
|
723
|
+
const { migrateProject } = await import("./lib/projectIdentity.js");
|
|
724
|
+
const result = await migrateProject({
|
|
725
|
+
sourcePath,
|
|
726
|
+
targetPath,
|
|
727
|
+
newName,
|
|
728
|
+
deleteSource: deleteOld,
|
|
729
|
+
centralDb: centralDb || undefined,
|
|
730
|
+
});
|
|
731
|
+
const resolvedTargetPath = path.resolve(targetPath);
|
|
732
|
+
const newStorePath = path.join(resolvedTargetPath, ".gnosys");
|
|
733
|
+
let summary = `Migration complete!\n\n`;
|
|
734
|
+
summary += `Project: ${result.oldIdentity.projectName} → ${result.newIdentity.projectName}\n`;
|
|
735
|
+
summary += `Path: ${result.oldIdentity.workingDirectory} → ${result.newIdentity.workingDirectory}\n`;
|
|
736
|
+
summary += `Memory files: ${result.memoryFileCount}\n`;
|
|
737
|
+
summary += `Central DB: ${centralDb?.isAvailable() ? "updated ✓" : "not available"}`;
|
|
738
|
+
// Sync memories to central DB if requested
|
|
739
|
+
if (syncMemories && centralDb?.isAvailable()) {
|
|
740
|
+
const matter = (await import("gray-matter")).default;
|
|
741
|
+
const { syncMemoryToDb } = await import("./lib/dbWrite.js");
|
|
742
|
+
const { glob } = await import("glob");
|
|
743
|
+
const mdFiles = await glob("**/*.md", {
|
|
744
|
+
cwd: newStorePath,
|
|
745
|
+
ignore: ["**/CHANGELOG.md", "**/MANIFEST.md", "**/.git/**", "**/.obsidian/**"],
|
|
746
|
+
});
|
|
747
|
+
let synced = 0;
|
|
748
|
+
for (const file of mdFiles) {
|
|
749
|
+
try {
|
|
750
|
+
const filePath = path.join(newStorePath, file);
|
|
751
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
752
|
+
const parsed = matter(raw);
|
|
753
|
+
if (parsed.data?.id) {
|
|
754
|
+
syncMemoryToDb(centralDb, parsed.data, parsed.content, filePath, result.newIdentity.projectId, "project");
|
|
755
|
+
synced++;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
catch {
|
|
759
|
+
// Skip files that fail to parse
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
summary += `\n\nSynced ${synced} memories to central DB.`;
|
|
763
|
+
}
|
|
764
|
+
if (deleteOld) {
|
|
765
|
+
summary += `\n\nOld .gnosys/ at ${sourcePath} has been removed.`;
|
|
766
|
+
}
|
|
767
|
+
// Add the new store location to the resolver so future tool calls find it
|
|
768
|
+
await resolver.registerProject(resolvedTargetPath);
|
|
769
|
+
await resolver.addProjectStore(newStorePath);
|
|
770
|
+
const writeTarget = resolver.getWriteTarget();
|
|
771
|
+
if (writeTarget) {
|
|
772
|
+
search = new GnosysSearch(writeTarget.store.getStorePath());
|
|
773
|
+
tagRegistry = new GnosysTagRegistry(writeTarget.store.getStorePath());
|
|
774
|
+
await tagRegistry.load();
|
|
775
|
+
ingestion = new GnosysIngestion(writeTarget.store, tagRegistry);
|
|
776
|
+
await reindexAllStores();
|
|
777
|
+
}
|
|
778
|
+
return { content: [{ type: "text", text: summary }] };
|
|
779
|
+
}
|
|
780
|
+
catch (err) {
|
|
781
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
782
|
+
return { content: [{ type: "text", text: `Migration failed: ${msg}` }], isError: true };
|
|
783
|
+
}
|
|
784
|
+
});
|
|
688
785
|
// ─── Tool: gnosys_update ─────────────────────────────────────────────────
|
|
689
786
|
server.tool("gnosys_update", "Update an existing memory's frontmatter and/or content. Specify the memory path and the fields to change.", {
|
|
690
787
|
path: z
|
|
@@ -758,37 +855,25 @@ server.tool("gnosys_update", "Update an existing memory's frontmatter and/or con
|
|
|
758
855
|
if (superseded_by !== undefined)
|
|
759
856
|
updates.superseded_by = superseded_by;
|
|
760
857
|
const fullContent = newContent ? `# ${title || memory.frontmatter.title}\n\n${newContent}` : undefined;
|
|
761
|
-
const
|
|
762
|
-
if (!
|
|
858
|
+
const memoryId = memory.frontmatter.id;
|
|
859
|
+
if (!memoryId) {
|
|
763
860
|
return {
|
|
764
|
-
content: [{ type: "text", text: `
|
|
861
|
+
content: [{ type: "text", text: `Memory has no ID: ${memPath}` }],
|
|
765
862
|
isError: true,
|
|
766
863
|
};
|
|
767
864
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
const supersededStore = ctx.resolver
|
|
774
|
-
.getStores()
|
|
775
|
-
.find((s) => s.label === supersededMemory.sourceLabel);
|
|
776
|
-
if (supersededStore?.writable) {
|
|
777
|
-
await supersededStore.store.updateMemory(supersededMemory.relativePath, {
|
|
778
|
-
superseded_by: updated.frontmatter.id,
|
|
779
|
-
status: "superseded",
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
|
-
}
|
|
865
|
+
if (!ctx.gnosysDb?.isAvailable()) {
|
|
866
|
+
return {
|
|
867
|
+
content: [{ type: "text", text: "Database not available. Cannot update memory." }],
|
|
868
|
+
isError: true,
|
|
869
|
+
};
|
|
783
870
|
}
|
|
784
|
-
//
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
syncUpdateToDb(ctx.gnosysDb, supersedes, { superseded_by: updated.frontmatter.id, status: "superseded" });
|
|
791
|
-
}
|
|
871
|
+
// Write update to DB only (SQLite is sole source of truth)
|
|
872
|
+
syncUpdateToDb(ctx.gnosysDb, memoryId, updates, fullContent);
|
|
873
|
+
auditToDb(ctx.gnosysDb, "write", memoryId, { tool: "gnosys_update", changed: Object.keys(updates) });
|
|
874
|
+
// Supersession cross-linking: if A supersedes B, mark B as superseded_by A
|
|
875
|
+
if (supersedes) {
|
|
876
|
+
syncUpdateToDb(ctx.gnosysDb, supersedes, { superseded_by: memoryId, status: "superseded" });
|
|
792
877
|
}
|
|
793
878
|
// Rebuild search index
|
|
794
879
|
if (ctx.search)
|
|
@@ -796,11 +881,12 @@ server.tool("gnosys_update", "Update an existing memory's frontmatter and/or con
|
|
|
796
881
|
const changedFields = Object.keys(updates);
|
|
797
882
|
if (newContent)
|
|
798
883
|
changedFields.push("content");
|
|
884
|
+
const updatedTitle = title || memory.frontmatter.title;
|
|
799
885
|
return {
|
|
800
886
|
content: [
|
|
801
887
|
{
|
|
802
888
|
type: "text",
|
|
803
|
-
text: `Memory updated: **${
|
|
889
|
+
text: `Memory updated: **${updatedTitle}**\nID: ${memoryId}\nChanged: ${changedFields.join(", ")}`,
|
|
804
890
|
},
|
|
805
891
|
],
|
|
806
892
|
};
|
|
@@ -958,8 +1044,12 @@ Output ONLY the JSON array, no markdown fences.`,
|
|
|
958
1044
|
else {
|
|
959
1045
|
// Actually add via ingestion
|
|
960
1046
|
try {
|
|
1047
|
+
if (!ctx.gnosysDb?.isAvailable()) {
|
|
1048
|
+
results.push(`❌ FAILED: "${candidate.summary}": Database not available`);
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
961
1051
|
const result = await ingestion.ingest(candidate.summary);
|
|
962
|
-
const id =
|
|
1052
|
+
const id = ctx.gnosysDb.getNextId(result.category, ctx.projectId ?? undefined);
|
|
963
1053
|
const today = new Date().toISOString().split("T")[0];
|
|
964
1054
|
const frontmatter = {
|
|
965
1055
|
id,
|
|
@@ -976,10 +1066,11 @@ Output ONLY the JSON array, no markdown fences.`,
|
|
|
976
1066
|
status: "active",
|
|
977
1067
|
supersedes: null,
|
|
978
1068
|
};
|
|
979
|
-
const filename = `${result.filename}.md`;
|
|
980
1069
|
const content = `# ${result.title}\n\n${result.content}`;
|
|
981
|
-
|
|
982
|
-
|
|
1070
|
+
// Write to DB only (SQLite is sole source of truth)
|
|
1071
|
+
syncMemoryToDb(ctx.gnosysDb, frontmatter, content, undefined, ctx.projectId, "project");
|
|
1072
|
+
auditToDb(ctx.gnosysDb, "write", id, { tool: "gnosys_commit_context", category: result.category });
|
|
1073
|
+
results.push(`➕ ADDED: "${result.title}"\n ID: ${id}`);
|
|
983
1074
|
added++;
|
|
984
1075
|
}
|
|
985
1076
|
catch (err) {
|
|
@@ -1766,6 +1857,13 @@ async function reindexAllStores() {
|
|
|
1766
1857
|
if (!search)
|
|
1767
1858
|
return;
|
|
1768
1859
|
search.clearIndex();
|
|
1860
|
+
// DB-first: read from central DB instead of scanning markdown files
|
|
1861
|
+
if (centralDb?.isAvailable()) {
|
|
1862
|
+
const memories = centralDb.getActiveMemories();
|
|
1863
|
+
search.addDbMemories(memories);
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
// Fallback: read from markdown files if central DB unavailable
|
|
1769
1867
|
const allStores = resolver.getStores();
|
|
1770
1868
|
for (const s of allStores) {
|
|
1771
1869
|
await search.addStoreMemories(s.store, s.label);
|