gnosys 5.0.1 → 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/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -27,7 +27,7 @@ import { GnosysAsk } from "./lib/ask.js";
|
|
|
27
27
|
import { getLLMProvider, isProviderAvailable } from "./lib/llm.js";
|
|
28
28
|
import { GnosysDB } from "./lib/db.js";
|
|
29
29
|
import { migrate, formatMigrationReport } from "./lib/migrate.js";
|
|
30
|
-
import { createProjectIdentity, readProjectIdentity, findProjectIdentity } from "./lib/projectIdentity.js";
|
|
30
|
+
import { createProjectIdentity, readProjectIdentity, findProjectIdentity, migrateProject } from "./lib/projectIdentity.js";
|
|
31
31
|
import { setPreference, getPreference, getAllPreferences, deletePreference } from "./lib/preferences.js";
|
|
32
32
|
import { syncToTarget } from "./lib/rulesGen.js";
|
|
33
33
|
// Load API keys from ~/.config/gnosys/.env (same as MCP server)
|
|
@@ -389,29 +389,51 @@ program
|
|
|
389
389
|
}
|
|
390
390
|
console.log("Structuring memory via LLM...");
|
|
391
391
|
const result = await ingestion.ingest(input);
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
392
|
+
let centralDb = null;
|
|
393
|
+
try {
|
|
394
|
+
centralDb = GnosysDB.openCentral();
|
|
395
|
+
const projectId = await resolveProjectId();
|
|
396
|
+
const id = centralDb.getNextId(result.category, projectId || undefined);
|
|
397
|
+
const today = new Date().toISOString().split("T")[0];
|
|
398
|
+
const now = new Date().toISOString();
|
|
399
|
+
const content = `# ${result.title}\n\n${result.content}`;
|
|
400
|
+
const tags = result.tags;
|
|
401
|
+
const tagsJson = Array.isArray(tags)
|
|
402
|
+
? JSON.stringify(tags)
|
|
403
|
+
: JSON.stringify(Object.values(tags).flat());
|
|
404
|
+
centralDb.insertMemory({
|
|
405
|
+
id,
|
|
406
|
+
title: result.title,
|
|
407
|
+
category: result.category,
|
|
408
|
+
content,
|
|
409
|
+
summary: null,
|
|
410
|
+
tags: tagsJson,
|
|
411
|
+
relevance: result.relevance,
|
|
412
|
+
author: opts.author,
|
|
413
|
+
authority: opts.authority,
|
|
414
|
+
confidence: result.confidence,
|
|
415
|
+
reinforcement_count: 0,
|
|
416
|
+
content_hash: "",
|
|
417
|
+
status: "active",
|
|
418
|
+
tier: "active",
|
|
419
|
+
supersedes: null,
|
|
420
|
+
superseded_by: null,
|
|
421
|
+
last_reinforced: null,
|
|
422
|
+
created: now,
|
|
423
|
+
modified: now,
|
|
424
|
+
embedding: null,
|
|
425
|
+
source_path: null,
|
|
426
|
+
project_id: projectId,
|
|
427
|
+
scope: "project",
|
|
428
|
+
});
|
|
429
|
+
console.log(`\nMemory added to [${writeTarget.label}]: ${result.title}`);
|
|
430
|
+
console.log(`ID: ${id}`);
|
|
431
|
+
console.log(`Category: ${result.category}`);
|
|
432
|
+
console.log(`Confidence: ${result.confidence}`);
|
|
433
|
+
}
|
|
434
|
+
finally {
|
|
435
|
+
centralDb?.close();
|
|
436
|
+
}
|
|
415
437
|
if (result.proposedNewTags && result.proposedNewTags.length > 0) {
|
|
416
438
|
console.log("\nProposed new tags (not yet in registry):");
|
|
417
439
|
for (const t of result.proposedNewTags) {
|
|
@@ -452,6 +474,7 @@ program
|
|
|
452
474
|
// Good — fresh init
|
|
453
475
|
}
|
|
454
476
|
if (!isResync) {
|
|
477
|
+
// Create directory structure (DB is sole source of truth — no category folders or changelog)
|
|
455
478
|
await fs.mkdir(storePath, { recursive: true });
|
|
456
479
|
await fs.mkdir(path.join(storePath, ".config"), { recursive: true });
|
|
457
480
|
const defaultRegistry = {
|
|
@@ -475,27 +498,6 @@ program
|
|
|
475
498
|
// Create .gitignore inside .gnosys to exclude large binary attachments
|
|
476
499
|
const storeGitignore = "# Large binary attachments (tracked via manifest, not git)\nattachments/\n";
|
|
477
500
|
await fs.writeFile(path.join(storePath, ".gitignore"), storeGitignore, "utf-8");
|
|
478
|
-
const changelog = `# Gnosys Changelog\n\n## ${new Date().toISOString().split("T")[0]}\n\n- Store initialized\n`;
|
|
479
|
-
await fs.writeFile(path.join(storePath, "CHANGELOG.md"), changelog, "utf-8");
|
|
480
|
-
try {
|
|
481
|
-
const { execSync } = await import("child_process");
|
|
482
|
-
execSync("git init", { cwd: storePath, stdio: "pipe" });
|
|
483
|
-
try {
|
|
484
|
-
execSync("git config user.name", { cwd: storePath, stdio: "pipe" });
|
|
485
|
-
}
|
|
486
|
-
catch {
|
|
487
|
-
execSync('git config user.name "Gnosys"', { cwd: storePath, stdio: "pipe" });
|
|
488
|
-
execSync('git config user.email "gnosys@local"', { cwd: storePath, stdio: "pipe" });
|
|
489
|
-
}
|
|
490
|
-
execSync("git add -A && git add -f .config/", { cwd: storePath, stdio: "pipe" });
|
|
491
|
-
execSync('git commit -m "Initialize Gnosys store"', {
|
|
492
|
-
cwd: storePath,
|
|
493
|
-
stdio: "pipe",
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
catch {
|
|
497
|
-
// Git not available
|
|
498
|
-
}
|
|
499
501
|
}
|
|
500
502
|
// v3.0: Create/update project identity and register in central DB
|
|
501
503
|
let centralDb = null;
|
|
@@ -547,11 +549,183 @@ program
|
|
|
547
549
|
console.log(` gnosys.json (project identity)`);
|
|
548
550
|
console.log(` .config/ (internal config)`);
|
|
549
551
|
console.log(` tags.json (tag registry)`);
|
|
550
|
-
console.log(` CHANGELOG.md`);
|
|
551
|
-
console.log(` git repo`);
|
|
552
552
|
}
|
|
553
553
|
console.log(`\nStart adding memories with: gnosys add "your knowledge here"`);
|
|
554
554
|
});
|
|
555
|
+
// ─── gnosys migrate ─────────────────────────────────────────────────────
|
|
556
|
+
program
|
|
557
|
+
.command("migrate")
|
|
558
|
+
.description("Interactively migrate a .gnosys/ store to a new directory. Moves files, updates project name/paths, syncs to central DB, and cleans up.")
|
|
559
|
+
.option("--from <dir>", "Source directory containing .gnosys/ (skips prompt)")
|
|
560
|
+
.option("--to <dir>", "Target directory to move .gnosys/ into (skips prompt)")
|
|
561
|
+
.option("--name <name>", "New project name (skips prompt, default: basename of target)")
|
|
562
|
+
.option("--yes", "Skip all confirmation prompts (non-interactive mode)")
|
|
563
|
+
.action(async (opts) => {
|
|
564
|
+
const { createInterface } = await import("readline/promises");
|
|
565
|
+
const rl = opts.yes ? null : createInterface({ input: process.stdin, output: process.stdout });
|
|
566
|
+
const ask = async (question, defaultValue) => {
|
|
567
|
+
if (!rl)
|
|
568
|
+
return defaultValue || "";
|
|
569
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
570
|
+
const answer = (await rl.question(`${question}${suffix}: `)).trim();
|
|
571
|
+
return answer || defaultValue || "";
|
|
572
|
+
};
|
|
573
|
+
try {
|
|
574
|
+
console.log("\n── Gnosys Project Migration ──\n");
|
|
575
|
+
// 1. Resolve source
|
|
576
|
+
let sourceDir;
|
|
577
|
+
if (opts.from) {
|
|
578
|
+
sourceDir = path.resolve(opts.from);
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
// Try auto-detect first
|
|
582
|
+
const found = await findProjectIdentity(process.cwd());
|
|
583
|
+
const defaultSource = found ? found.projectRoot : "";
|
|
584
|
+
const sourceInput = await ask("Source directory (contains .gnosys/)", defaultSource);
|
|
585
|
+
if (!sourceInput) {
|
|
586
|
+
console.error("No source directory provided.");
|
|
587
|
+
rl?.close();
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
sourceDir = path.resolve(sourceInput);
|
|
591
|
+
}
|
|
592
|
+
// Verify source has .gnosys/
|
|
593
|
+
const storePath = path.join(sourceDir, ".gnosys");
|
|
594
|
+
try {
|
|
595
|
+
await fs.stat(storePath);
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
console.error(`No .gnosys/ directory found at ${sourceDir}`);
|
|
599
|
+
rl?.close();
|
|
600
|
+
process.exit(1);
|
|
601
|
+
}
|
|
602
|
+
// Read identity (may not exist for pre-v3 stores)
|
|
603
|
+
const identity = await readProjectIdentity(sourceDir);
|
|
604
|
+
// Count memory files
|
|
605
|
+
const { glob } = await import("glob");
|
|
606
|
+
const memFiles = await glob("**/*.md", {
|
|
607
|
+
cwd: storePath,
|
|
608
|
+
ignore: ["**/CHANGELOG.md", "**/MANIFEST.md", "**/.git/**", "**/.obsidian/**"],
|
|
609
|
+
});
|
|
610
|
+
console.log("\nSource project:");
|
|
611
|
+
if (identity) {
|
|
612
|
+
console.log(` Name: ${identity.projectName}`);
|
|
613
|
+
console.log(` ID: ${identity.projectId}`);
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
console.log(` Name: (unregistered — pre-v3 store)`);
|
|
617
|
+
}
|
|
618
|
+
console.log(` Directory: ${sourceDir}`);
|
|
619
|
+
console.log(` Memories: ${memFiles.length} markdown files`);
|
|
620
|
+
// 2. Resolve target
|
|
621
|
+
let targetDir;
|
|
622
|
+
if (opts.to) {
|
|
623
|
+
targetDir = path.resolve(opts.to);
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
const targetInput = await ask("\nTarget directory (where .gnosys/ should live)");
|
|
627
|
+
if (!targetInput) {
|
|
628
|
+
console.error("No target directory provided.");
|
|
629
|
+
rl?.close();
|
|
630
|
+
process.exit(1);
|
|
631
|
+
}
|
|
632
|
+
targetDir = path.resolve(targetInput);
|
|
633
|
+
}
|
|
634
|
+
// 3. Resolve name
|
|
635
|
+
const defaultName = opts.name || path.basename(targetDir);
|
|
636
|
+
const newName = opts.yes
|
|
637
|
+
? defaultName
|
|
638
|
+
: await ask("Project name", defaultName);
|
|
639
|
+
// 4. Ask about sync and cleanup
|
|
640
|
+
let doSync = true;
|
|
641
|
+
let doDelete = true;
|
|
642
|
+
if (!opts.yes) {
|
|
643
|
+
const syncAnswer = await ask("\nSync memories to central DB?", "Y");
|
|
644
|
+
doSync = syncAnswer.toLowerCase() !== "n" && syncAnswer.toLowerCase() !== "no";
|
|
645
|
+
const deleteAnswer = await ask("Delete old .gnosys/ after migration?", "Y");
|
|
646
|
+
doDelete = deleteAnswer.toLowerCase() !== "n" && deleteAnswer.toLowerCase() !== "no";
|
|
647
|
+
}
|
|
648
|
+
// 5. Show summary and confirm
|
|
649
|
+
console.log("\n── Migration Summary ──");
|
|
650
|
+
console.log(` From: ${sourceDir}/.gnosys/`);
|
|
651
|
+
console.log(` To: ${targetDir}/.gnosys/`);
|
|
652
|
+
console.log(` Name: ${identity?.projectName || "(new)"} → ${newName}`);
|
|
653
|
+
console.log(` Memories: ${memFiles.length} files`);
|
|
654
|
+
console.log(` Sync to DB: ${doSync ? "yes" : "no"}`);
|
|
655
|
+
console.log(` Delete old: ${doDelete ? "yes" : "no"}`);
|
|
656
|
+
if (!opts.yes) {
|
|
657
|
+
const confirm = await ask("\nProceed?", "Y");
|
|
658
|
+
if (confirm.toLowerCase() === "n" || confirm.toLowerCase() === "no") {
|
|
659
|
+
console.log("Aborted.");
|
|
660
|
+
rl?.close();
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
rl?.close();
|
|
665
|
+
// 6. Open central DB
|
|
666
|
+
let centralDb = null;
|
|
667
|
+
try {
|
|
668
|
+
centralDb = GnosysDB.openCentral();
|
|
669
|
+
if (!centralDb.isAvailable())
|
|
670
|
+
centralDb = null;
|
|
671
|
+
}
|
|
672
|
+
catch {
|
|
673
|
+
centralDb = null;
|
|
674
|
+
}
|
|
675
|
+
// 7. Run migration
|
|
676
|
+
console.log("\nMigrating...");
|
|
677
|
+
const result = await migrateProject({
|
|
678
|
+
sourcePath: sourceDir,
|
|
679
|
+
targetPath: targetDir,
|
|
680
|
+
newName,
|
|
681
|
+
deleteSource: doDelete,
|
|
682
|
+
centralDb: centralDb || undefined,
|
|
683
|
+
});
|
|
684
|
+
console.log(` Copied ${result.memoryFileCount} memory files`);
|
|
685
|
+
console.log(` Project: ${result.newIdentity.projectName} (${result.newIdentity.projectId})`);
|
|
686
|
+
console.log(` Path: ${result.newIdentity.workingDirectory}`);
|
|
687
|
+
console.log(` Central DB: ${centralDb ? "updated ✓" : "not available"}`);
|
|
688
|
+
// 8. Sync memories to central DB
|
|
689
|
+
if (doSync && centralDb) {
|
|
690
|
+
console.log("\nSyncing memories to central DB...");
|
|
691
|
+
const matter = (await import("gray-matter")).default;
|
|
692
|
+
const { syncMemoryToDb } = await import("./lib/dbWrite.js");
|
|
693
|
+
const newStorePath = path.join(targetDir, ".gnosys");
|
|
694
|
+
const mdFiles = await glob("**/*.md", {
|
|
695
|
+
cwd: newStorePath,
|
|
696
|
+
ignore: ["**/CHANGELOG.md", "**/MANIFEST.md", "**/.git/**", "**/.obsidian/**"],
|
|
697
|
+
});
|
|
698
|
+
let synced = 0;
|
|
699
|
+
for (const file of mdFiles) {
|
|
700
|
+
try {
|
|
701
|
+
const filePath = path.join(newStorePath, file);
|
|
702
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
703
|
+
const parsed = matter(raw);
|
|
704
|
+
if (parsed.data?.id) {
|
|
705
|
+
syncMemoryToDb(centralDb, parsed.data, parsed.content, filePath, result.newIdentity.projectId, "project");
|
|
706
|
+
synced++;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
710
|
+
// Skip files that fail to parse
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
console.log(` Synced ${synced} memories to central DB`);
|
|
714
|
+
}
|
|
715
|
+
if (centralDb)
|
|
716
|
+
centralDb.close();
|
|
717
|
+
if (doDelete) {
|
|
718
|
+
console.log(`\nOld .gnosys/ at ${sourceDir} removed.`);
|
|
719
|
+
}
|
|
720
|
+
console.log(`\nMigration complete! Run 'gnosys projects' to verify.`);
|
|
721
|
+
}
|
|
722
|
+
catch (err) {
|
|
723
|
+
rl?.close();
|
|
724
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
725
|
+
console.error(`\nMigration failed: ${msg}`);
|
|
726
|
+
process.exit(1);
|
|
727
|
+
}
|
|
728
|
+
});
|
|
555
729
|
// ─── gnosys stale ───────────────────────────────────────────────────────
|
|
556
730
|
program
|
|
557
731
|
.command("stale")
|
|
@@ -649,30 +823,34 @@ program
|
|
|
649
823
|
const fullContent = opts.content
|
|
650
824
|
? `# ${opts.title || memory.frontmatter.title}\n\n${opts.content}`
|
|
651
825
|
: undefined;
|
|
652
|
-
const
|
|
653
|
-
if (!
|
|
654
|
-
console.error(`
|
|
826
|
+
const memoryId = memory.frontmatter.id;
|
|
827
|
+
if (!memoryId) {
|
|
828
|
+
console.error(`Memory has no ID: ${memPath}`);
|
|
655
829
|
process.exit(1);
|
|
656
830
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
831
|
+
let centralDb = null;
|
|
832
|
+
try {
|
|
833
|
+
centralDb = GnosysDB.openCentral();
|
|
834
|
+
const { syncUpdateToDb } = await import("./lib/dbWrite.js");
|
|
835
|
+
syncUpdateToDb(centralDb, memoryId, updates, fullContent);
|
|
836
|
+
// Supersession cross-linking
|
|
837
|
+
if (opts.supersedes && memoryId) {
|
|
838
|
+
const allMemories = await resolver.getAllMemories();
|
|
839
|
+
const supersededMemory = allMemories.find((m) => m.frontmatter.id === opts.supersedes);
|
|
840
|
+
if (supersededMemory) {
|
|
841
|
+
syncUpdateToDb(centralDb, supersededMemory.frontmatter.id, { superseded_by: memoryId, status: "superseded" });
|
|
667
842
|
console.log(`Cross-linked: ${supersededMemory.frontmatter.title} marked as superseded.`);
|
|
668
843
|
}
|
|
669
844
|
}
|
|
670
845
|
}
|
|
846
|
+
finally {
|
|
847
|
+
centralDb?.close();
|
|
848
|
+
}
|
|
671
849
|
const changedFields = Object.keys(updates);
|
|
672
850
|
if (opts.content)
|
|
673
851
|
changedFields.push("content");
|
|
674
|
-
console.log(`Memory updated: ${
|
|
675
|
-
console.log(`
|
|
852
|
+
console.log(`Memory updated: ${opts.title || memory.frontmatter.title}`);
|
|
853
|
+
console.log(`ID: ${memoryId}`);
|
|
676
854
|
console.log(`Changed: ${changedFields.join(", ")}`);
|
|
677
855
|
});
|
|
678
856
|
// ─── gnosys reinforce <memoryId> ────────────────────────────────────────
|
|
@@ -701,17 +879,16 @@ program
|
|
|
701
879
|
await fs.appendFile(logPath, entry + "\n", "utf-8");
|
|
702
880
|
// If 'useful', update the memory's modified date (reset decay)
|
|
703
881
|
if (opts.signal === "useful") {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
.
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
882
|
+
let centralDb = null;
|
|
883
|
+
try {
|
|
884
|
+
centralDb = GnosysDB.openCentral();
|
|
885
|
+
const { syncUpdateToDb } = await import("./lib/dbWrite.js");
|
|
886
|
+
syncUpdateToDb(centralDb, memoryId, {
|
|
887
|
+
modified: new Date().toISOString().split("T")[0],
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
finally {
|
|
891
|
+
centralDb?.close();
|
|
715
892
|
}
|
|
716
893
|
}
|
|
717
894
|
const messages = {
|
|
@@ -787,13 +964,7 @@ program
|
|
|
787
964
|
centralDb?.close();
|
|
788
965
|
}
|
|
789
966
|
}
|
|
790
|
-
// ───
|
|
791
|
-
const resolver = await getResolver();
|
|
792
|
-
const writeTarget = resolver.getWriteTarget(opts.store || undefined);
|
|
793
|
-
if (!writeTarget) {
|
|
794
|
-
console.error("No writable store found.");
|
|
795
|
-
process.exit(1);
|
|
796
|
-
}
|
|
967
|
+
// ─── DB-only write ────────────────────────────────────────────
|
|
797
968
|
let tags;
|
|
798
969
|
try {
|
|
799
970
|
tags = JSON.parse(opts.tags);
|
|
@@ -802,32 +973,47 @@ program
|
|
|
802
973
|
console.error("Invalid --tags JSON. Example: '{\"domain\":[\"auth\"],\"type\":[\"decision\"]}'");
|
|
803
974
|
process.exit(1);
|
|
804
975
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
.
|
|
808
|
-
|
|
809
|
-
.
|
|
810
|
-
.
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
976
|
+
let centralDb = null;
|
|
977
|
+
try {
|
|
978
|
+
centralDb = GnosysDB.openCentral();
|
|
979
|
+
const projectId = await resolveProjectId();
|
|
980
|
+
const id = centralDb.getNextId(opts.category, projectId || undefined);
|
|
981
|
+
const now = new Date().toISOString();
|
|
982
|
+
const content = `# ${opts.title}\n\n${opts.content}`;
|
|
983
|
+
const tagsJson = Array.isArray(tags)
|
|
984
|
+
? JSON.stringify(tags)
|
|
985
|
+
: JSON.stringify(Object.values(tags).flat());
|
|
986
|
+
centralDb.insertMemory({
|
|
987
|
+
id,
|
|
988
|
+
title: opts.title,
|
|
989
|
+
category: opts.category,
|
|
990
|
+
content,
|
|
991
|
+
summary: null,
|
|
992
|
+
tags: tagsJson,
|
|
993
|
+
relevance: opts.relevance || opts.content.slice(0, 200),
|
|
994
|
+
author: opts.author,
|
|
995
|
+
authority: opts.authority,
|
|
996
|
+
confidence: parseFloat(opts.confidence),
|
|
997
|
+
reinforcement_count: 0,
|
|
998
|
+
content_hash: "",
|
|
999
|
+
status: "active",
|
|
1000
|
+
tier: "active",
|
|
1001
|
+
supersedes: null,
|
|
1002
|
+
superseded_by: null,
|
|
1003
|
+
last_reinforced: null,
|
|
1004
|
+
created: now,
|
|
1005
|
+
modified: now,
|
|
1006
|
+
embedding: null,
|
|
1007
|
+
source_path: null,
|
|
1008
|
+
project_id: projectId,
|
|
1009
|
+
scope: "project",
|
|
1010
|
+
});
|
|
1011
|
+
console.log(`Memory added: ${opts.title}`);
|
|
1012
|
+
console.log(`ID: ${id}`);
|
|
1013
|
+
}
|
|
1014
|
+
finally {
|
|
1015
|
+
centralDb?.close();
|
|
1016
|
+
}
|
|
831
1017
|
});
|
|
832
1018
|
// ─── gnosys ingest <file> ─────────────────────────────────────────────────
|
|
833
1019
|
program
|
|
@@ -1029,49 +1215,70 @@ Output ONLY the JSON array, no markdown fences.`,
|
|
|
1029
1215
|
// Step 2: Check novelty and commit
|
|
1030
1216
|
let added = 0;
|
|
1031
1217
|
let skipped = 0;
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
const result = await ingestion.ingest(candidate.summary);
|
|
1047
|
-
const id = await writeTarget.store.generateId(result.category);
|
|
1048
|
-
const today = new Date().toISOString().split("T")[0];
|
|
1049
|
-
const frontmatter = {
|
|
1050
|
-
id,
|
|
1051
|
-
title: result.title,
|
|
1052
|
-
category: result.category,
|
|
1053
|
-
tags: result.tags,
|
|
1054
|
-
relevance: result.relevance,
|
|
1055
|
-
author: "ai",
|
|
1056
|
-
authority: "observed",
|
|
1057
|
-
confidence: result.confidence,
|
|
1058
|
-
created: today,
|
|
1059
|
-
modified: today,
|
|
1060
|
-
last_reviewed: today,
|
|
1061
|
-
status: "active",
|
|
1062
|
-
supersedes: null,
|
|
1063
|
-
};
|
|
1064
|
-
const content = `# ${result.title}\n\n${result.content}`;
|
|
1065
|
-
const relPath = await writeTarget.store.writeMemory(result.category, `${result.filename}.md`, frontmatter, content);
|
|
1066
|
-
console.log(` ➕ ADDED: "${result.title}"`);
|
|
1067
|
-
console.log(` Path: ${writeTarget.label}:${relPath}`);
|
|
1218
|
+
let centralDb = null;
|
|
1219
|
+
try {
|
|
1220
|
+
centralDb = GnosysDB.openCentral();
|
|
1221
|
+
const projectId = await resolveProjectId();
|
|
1222
|
+
for (const candidate of candidates) {
|
|
1223
|
+
const searchTerms = candidate.search_terms.join(" ");
|
|
1224
|
+
const existing = search.discover(searchTerms, 3);
|
|
1225
|
+
if (existing.length > 0) {
|
|
1226
|
+
console.log(` ⏭ SKIP: "${candidate.summary}"`);
|
|
1227
|
+
console.log(` Overlaps with: ${existing[0].title}`);
|
|
1228
|
+
skipped++;
|
|
1229
|
+
}
|
|
1230
|
+
else if (opts.dryRun) {
|
|
1231
|
+
console.log(` ➕ WOULD ADD: "${candidate.summary}" [${candidate.type}]`);
|
|
1068
1232
|
added++;
|
|
1069
1233
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1234
|
+
else {
|
|
1235
|
+
try {
|
|
1236
|
+
const result = await ingestion.ingest(candidate.summary);
|
|
1237
|
+
const id = centralDb.getNextId(result.category, projectId || undefined);
|
|
1238
|
+
const now = new Date().toISOString();
|
|
1239
|
+
const content = `# ${result.title}\n\n${result.content}`;
|
|
1240
|
+
const resultTags = result.tags;
|
|
1241
|
+
const tagsJson = Array.isArray(resultTags)
|
|
1242
|
+
? JSON.stringify(resultTags)
|
|
1243
|
+
: JSON.stringify(Object.values(resultTags).flat());
|
|
1244
|
+
centralDb.insertMemory({
|
|
1245
|
+
id,
|
|
1246
|
+
title: result.title,
|
|
1247
|
+
category: result.category,
|
|
1248
|
+
content,
|
|
1249
|
+
summary: null,
|
|
1250
|
+
tags: tagsJson,
|
|
1251
|
+
relevance: result.relevance,
|
|
1252
|
+
author: "ai",
|
|
1253
|
+
authority: "observed",
|
|
1254
|
+
confidence: result.confidence,
|
|
1255
|
+
reinforcement_count: 0,
|
|
1256
|
+
content_hash: "",
|
|
1257
|
+
status: "active",
|
|
1258
|
+
tier: "active",
|
|
1259
|
+
supersedes: null,
|
|
1260
|
+
superseded_by: null,
|
|
1261
|
+
last_reinforced: null,
|
|
1262
|
+
created: now,
|
|
1263
|
+
modified: now,
|
|
1264
|
+
embedding: null,
|
|
1265
|
+
source_path: null,
|
|
1266
|
+
project_id: projectId,
|
|
1267
|
+
scope: "project",
|
|
1268
|
+
});
|
|
1269
|
+
console.log(` ➕ ADDED: "${result.title}"`);
|
|
1270
|
+
console.log(` ID: ${id}`);
|
|
1271
|
+
added++;
|
|
1272
|
+
}
|
|
1273
|
+
catch (err) {
|
|
1274
|
+
console.error(` ❌ FAILED: "${candidate.summary}": ${err instanceof Error ? err.message : String(err)}`);
|
|
1275
|
+
}
|
|
1072
1276
|
}
|
|
1277
|
+
console.log();
|
|
1073
1278
|
}
|
|
1074
|
-
|
|
1279
|
+
}
|
|
1280
|
+
finally {
|
|
1281
|
+
centralDb?.close();
|
|
1075
1282
|
}
|
|
1076
1283
|
search.close();
|
|
1077
1284
|
const mode = opts.dryRun ? "DRY RUN" : "COMMITTED";
|
|
@@ -2833,10 +3040,10 @@ program
|
|
|
2833
3040
|
process.exit(1);
|
|
2834
3041
|
}
|
|
2835
3042
|
});
|
|
2836
|
-
// ─── gnosys migrate
|
|
3043
|
+
// ─── gnosys migrate-db ──────────────────────────────────────────────────
|
|
2837
3044
|
program
|
|
2838
|
-
.command("migrate")
|
|
2839
|
-
.description("
|
|
3045
|
+
.command("migrate-db")
|
|
3046
|
+
.description("Legacy data migration. Use --to-central to move per-project stores into the central DB.")
|
|
2840
3047
|
.option("--to-central", "Migrate all discovered per-project stores into ~/.gnosys/gnosys.db")
|
|
2841
3048
|
.option("-v, --verbose", "Verbose output")
|
|
2842
3049
|
.action(async (opts) => {
|