gnosys 5.0.1 → 5.1.1
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 +369 -148
- package/dist/cli.js.map +1 -1
- package/dist/index.js +184 -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 +39 -0
- package/dist/lib/projectIdentity.d.ts.map +1 -1
- package/dist/lib/projectIdentity.js +293 -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,194 @@ 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
|
-
|
|
551
|
-
|
|
552
|
+
}
|
|
553
|
+
// Configure IDE hooks for automatic memory recall
|
|
554
|
+
const { configureIdeHooks } = await import("./lib/projectIdentity.js");
|
|
555
|
+
const hookResult = await configureIdeHooks(targetDir);
|
|
556
|
+
if (hookResult.configured) {
|
|
557
|
+
console.log(`\nIDE hooks (${hookResult.ide}):`);
|
|
558
|
+
console.log(` ${hookResult.details}`);
|
|
559
|
+
console.log(` File: ${hookResult.filePath}`);
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
console.log(`\nIDE hooks: ${hookResult.details}`);
|
|
552
563
|
}
|
|
553
564
|
console.log(`\nStart adding memories with: gnosys add "your knowledge here"`);
|
|
554
565
|
});
|
|
566
|
+
// ─── gnosys migrate ─────────────────────────────────────────────────────
|
|
567
|
+
program
|
|
568
|
+
.command("migrate")
|
|
569
|
+
.description("Interactively migrate a .gnosys/ store to a new directory. Moves files, updates project name/paths, syncs to central DB, and cleans up.")
|
|
570
|
+
.option("--from <dir>", "Source directory containing .gnosys/ (skips prompt)")
|
|
571
|
+
.option("--to <dir>", "Target directory to move .gnosys/ into (skips prompt)")
|
|
572
|
+
.option("--name <name>", "New project name (skips prompt, default: basename of target)")
|
|
573
|
+
.option("--yes", "Skip all confirmation prompts (non-interactive mode)")
|
|
574
|
+
.action(async (opts) => {
|
|
575
|
+
const { createInterface } = await import("readline/promises");
|
|
576
|
+
const rl = opts.yes ? null : createInterface({ input: process.stdin, output: process.stdout });
|
|
577
|
+
const ask = async (question, defaultValue) => {
|
|
578
|
+
if (!rl)
|
|
579
|
+
return defaultValue || "";
|
|
580
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
581
|
+
const answer = (await rl.question(`${question}${suffix}: `)).trim();
|
|
582
|
+
return answer || defaultValue || "";
|
|
583
|
+
};
|
|
584
|
+
try {
|
|
585
|
+
console.log("\n── Gnosys Project Migration ──\n");
|
|
586
|
+
// 1. Resolve source
|
|
587
|
+
let sourceDir;
|
|
588
|
+
if (opts.from) {
|
|
589
|
+
sourceDir = path.resolve(opts.from);
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
// Try auto-detect first
|
|
593
|
+
const found = await findProjectIdentity(process.cwd());
|
|
594
|
+
const defaultSource = found ? found.projectRoot : "";
|
|
595
|
+
const sourceInput = await ask("Source directory (contains .gnosys/)", defaultSource);
|
|
596
|
+
if (!sourceInput) {
|
|
597
|
+
console.error("No source directory provided.");
|
|
598
|
+
rl?.close();
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
sourceDir = path.resolve(sourceInput);
|
|
602
|
+
}
|
|
603
|
+
// Verify source has .gnosys/
|
|
604
|
+
const storePath = path.join(sourceDir, ".gnosys");
|
|
605
|
+
try {
|
|
606
|
+
await fs.stat(storePath);
|
|
607
|
+
}
|
|
608
|
+
catch {
|
|
609
|
+
console.error(`No .gnosys/ directory found at ${sourceDir}`);
|
|
610
|
+
rl?.close();
|
|
611
|
+
process.exit(1);
|
|
612
|
+
}
|
|
613
|
+
// Read identity (may not exist for pre-v3 stores)
|
|
614
|
+
const identity = await readProjectIdentity(sourceDir);
|
|
615
|
+
// Count memory files
|
|
616
|
+
const { glob } = await import("glob");
|
|
617
|
+
const memFiles = await glob("**/*.md", {
|
|
618
|
+
cwd: storePath,
|
|
619
|
+
ignore: ["**/CHANGELOG.md", "**/MANIFEST.md", "**/.git/**", "**/.obsidian/**"],
|
|
620
|
+
});
|
|
621
|
+
console.log("\nSource project:");
|
|
622
|
+
if (identity) {
|
|
623
|
+
console.log(` Name: ${identity.projectName}`);
|
|
624
|
+
console.log(` ID: ${identity.projectId}`);
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
console.log(` Name: (unregistered — pre-v3 store)`);
|
|
628
|
+
}
|
|
629
|
+
console.log(` Directory: ${sourceDir}`);
|
|
630
|
+
console.log(` Memories: ${memFiles.length} markdown files`);
|
|
631
|
+
// 2. Resolve target
|
|
632
|
+
let targetDir;
|
|
633
|
+
if (opts.to) {
|
|
634
|
+
targetDir = path.resolve(opts.to);
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
const targetInput = await ask("\nTarget directory (where .gnosys/ should live)");
|
|
638
|
+
if (!targetInput) {
|
|
639
|
+
console.error("No target directory provided.");
|
|
640
|
+
rl?.close();
|
|
641
|
+
process.exit(1);
|
|
642
|
+
}
|
|
643
|
+
targetDir = path.resolve(targetInput);
|
|
644
|
+
}
|
|
645
|
+
// 3. Resolve name
|
|
646
|
+
const defaultName = opts.name || path.basename(targetDir);
|
|
647
|
+
const newName = opts.yes
|
|
648
|
+
? defaultName
|
|
649
|
+
: await ask("Project name", defaultName);
|
|
650
|
+
// 4. Ask about sync and cleanup
|
|
651
|
+
let doSync = true;
|
|
652
|
+
let doDelete = true;
|
|
653
|
+
if (!opts.yes) {
|
|
654
|
+
const syncAnswer = await ask("\nSync memories to central DB?", "Y");
|
|
655
|
+
doSync = syncAnswer.toLowerCase() !== "n" && syncAnswer.toLowerCase() !== "no";
|
|
656
|
+
const deleteAnswer = await ask("Delete old .gnosys/ after migration?", "Y");
|
|
657
|
+
doDelete = deleteAnswer.toLowerCase() !== "n" && deleteAnswer.toLowerCase() !== "no";
|
|
658
|
+
}
|
|
659
|
+
// 5. Show summary and confirm
|
|
660
|
+
console.log("\n── Migration Summary ──");
|
|
661
|
+
console.log(` From: ${sourceDir}/.gnosys/`);
|
|
662
|
+
console.log(` To: ${targetDir}/.gnosys/`);
|
|
663
|
+
console.log(` Name: ${identity?.projectName || "(new)"} → ${newName}`);
|
|
664
|
+
console.log(` Memories: ${memFiles.length} files`);
|
|
665
|
+
console.log(` Sync to DB: ${doSync ? "yes" : "no"}`);
|
|
666
|
+
console.log(` Delete old: ${doDelete ? "yes" : "no"}`);
|
|
667
|
+
if (!opts.yes) {
|
|
668
|
+
const confirm = await ask("\nProceed?", "Y");
|
|
669
|
+
if (confirm.toLowerCase() === "n" || confirm.toLowerCase() === "no") {
|
|
670
|
+
console.log("Aborted.");
|
|
671
|
+
rl?.close();
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
rl?.close();
|
|
676
|
+
// 6. Open central DB
|
|
677
|
+
let centralDb = null;
|
|
678
|
+
try {
|
|
679
|
+
centralDb = GnosysDB.openCentral();
|
|
680
|
+
if (!centralDb.isAvailable())
|
|
681
|
+
centralDb = null;
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
centralDb = null;
|
|
685
|
+
}
|
|
686
|
+
// 7. Run migration
|
|
687
|
+
console.log("\nMigrating...");
|
|
688
|
+
const result = await migrateProject({
|
|
689
|
+
sourcePath: sourceDir,
|
|
690
|
+
targetPath: targetDir,
|
|
691
|
+
newName,
|
|
692
|
+
deleteSource: doDelete,
|
|
693
|
+
centralDb: centralDb || undefined,
|
|
694
|
+
});
|
|
695
|
+
console.log(` Copied ${result.memoryFileCount} memory files`);
|
|
696
|
+
console.log(` Project: ${result.newIdentity.projectName} (${result.newIdentity.projectId})`);
|
|
697
|
+
console.log(` Path: ${result.newIdentity.workingDirectory}`);
|
|
698
|
+
console.log(` Central DB: ${centralDb ? "updated ✓" : "not available"}`);
|
|
699
|
+
// 8. Sync memories to central DB
|
|
700
|
+
if (doSync && centralDb) {
|
|
701
|
+
console.log("\nSyncing memories to central DB...");
|
|
702
|
+
const matter = (await import("gray-matter")).default;
|
|
703
|
+
const { syncMemoryToDb } = await import("./lib/dbWrite.js");
|
|
704
|
+
const newStorePath = path.join(targetDir, ".gnosys");
|
|
705
|
+
const mdFiles = await glob("**/*.md", {
|
|
706
|
+
cwd: newStorePath,
|
|
707
|
+
ignore: ["**/CHANGELOG.md", "**/MANIFEST.md", "**/.git/**", "**/.obsidian/**"],
|
|
708
|
+
});
|
|
709
|
+
let synced = 0;
|
|
710
|
+
for (const file of mdFiles) {
|
|
711
|
+
try {
|
|
712
|
+
const filePath = path.join(newStorePath, file);
|
|
713
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
714
|
+
const parsed = matter(raw);
|
|
715
|
+
if (parsed.data?.id) {
|
|
716
|
+
syncMemoryToDb(centralDb, parsed.data, parsed.content, filePath, result.newIdentity.projectId, "project");
|
|
717
|
+
synced++;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
catch {
|
|
721
|
+
// Skip files that fail to parse
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
console.log(` Synced ${synced} memories to central DB`);
|
|
725
|
+
}
|
|
726
|
+
if (centralDb)
|
|
727
|
+
centralDb.close();
|
|
728
|
+
if (doDelete) {
|
|
729
|
+
console.log(`\nOld .gnosys/ at ${sourceDir} removed.`);
|
|
730
|
+
}
|
|
731
|
+
console.log(`\nMigration complete! Run 'gnosys projects' to verify.`);
|
|
732
|
+
}
|
|
733
|
+
catch (err) {
|
|
734
|
+
rl?.close();
|
|
735
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
736
|
+
console.error(`\nMigration failed: ${msg}`);
|
|
737
|
+
process.exit(1);
|
|
738
|
+
}
|
|
739
|
+
});
|
|
555
740
|
// ─── gnosys stale ───────────────────────────────────────────────────────
|
|
556
741
|
program
|
|
557
742
|
.command("stale")
|
|
@@ -649,30 +834,34 @@ program
|
|
|
649
834
|
const fullContent = opts.content
|
|
650
835
|
? `# ${opts.title || memory.frontmatter.title}\n\n${opts.content}`
|
|
651
836
|
: undefined;
|
|
652
|
-
const
|
|
653
|
-
if (!
|
|
654
|
-
console.error(`
|
|
837
|
+
const memoryId = memory.frontmatter.id;
|
|
838
|
+
if (!memoryId) {
|
|
839
|
+
console.error(`Memory has no ID: ${memPath}`);
|
|
655
840
|
process.exit(1);
|
|
656
841
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
842
|
+
let centralDb = null;
|
|
843
|
+
try {
|
|
844
|
+
centralDb = GnosysDB.openCentral();
|
|
845
|
+
const { syncUpdateToDb } = await import("./lib/dbWrite.js");
|
|
846
|
+
syncUpdateToDb(centralDb, memoryId, updates, fullContent);
|
|
847
|
+
// Supersession cross-linking
|
|
848
|
+
if (opts.supersedes && memoryId) {
|
|
849
|
+
const allMemories = await resolver.getAllMemories();
|
|
850
|
+
const supersededMemory = allMemories.find((m) => m.frontmatter.id === opts.supersedes);
|
|
851
|
+
if (supersededMemory) {
|
|
852
|
+
syncUpdateToDb(centralDb, supersededMemory.frontmatter.id, { superseded_by: memoryId, status: "superseded" });
|
|
667
853
|
console.log(`Cross-linked: ${supersededMemory.frontmatter.title} marked as superseded.`);
|
|
668
854
|
}
|
|
669
855
|
}
|
|
670
856
|
}
|
|
857
|
+
finally {
|
|
858
|
+
centralDb?.close();
|
|
859
|
+
}
|
|
671
860
|
const changedFields = Object.keys(updates);
|
|
672
861
|
if (opts.content)
|
|
673
862
|
changedFields.push("content");
|
|
674
|
-
console.log(`Memory updated: ${
|
|
675
|
-
console.log(`
|
|
863
|
+
console.log(`Memory updated: ${opts.title || memory.frontmatter.title}`);
|
|
864
|
+
console.log(`ID: ${memoryId}`);
|
|
676
865
|
console.log(`Changed: ${changedFields.join(", ")}`);
|
|
677
866
|
});
|
|
678
867
|
// ─── gnosys reinforce <memoryId> ────────────────────────────────────────
|
|
@@ -701,17 +890,16 @@ program
|
|
|
701
890
|
await fs.appendFile(logPath, entry + "\n", "utf-8");
|
|
702
891
|
// If 'useful', update the memory's modified date (reset decay)
|
|
703
892
|
if (opts.signal === "useful") {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
.
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
893
|
+
let centralDb = null;
|
|
894
|
+
try {
|
|
895
|
+
centralDb = GnosysDB.openCentral();
|
|
896
|
+
const { syncUpdateToDb } = await import("./lib/dbWrite.js");
|
|
897
|
+
syncUpdateToDb(centralDb, memoryId, {
|
|
898
|
+
modified: new Date().toISOString().split("T")[0],
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
finally {
|
|
902
|
+
centralDb?.close();
|
|
715
903
|
}
|
|
716
904
|
}
|
|
717
905
|
const messages = {
|
|
@@ -787,13 +975,7 @@ program
|
|
|
787
975
|
centralDb?.close();
|
|
788
976
|
}
|
|
789
977
|
}
|
|
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
|
-
}
|
|
978
|
+
// ─── DB-only write ────────────────────────────────────────────
|
|
797
979
|
let tags;
|
|
798
980
|
try {
|
|
799
981
|
tags = JSON.parse(opts.tags);
|
|
@@ -802,32 +984,47 @@ program
|
|
|
802
984
|
console.error("Invalid --tags JSON. Example: '{\"domain\":[\"auth\"],\"type\":[\"decision\"]}'");
|
|
803
985
|
process.exit(1);
|
|
804
986
|
}
|
|
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
|
-
|
|
987
|
+
let centralDb = null;
|
|
988
|
+
try {
|
|
989
|
+
centralDb = GnosysDB.openCentral();
|
|
990
|
+
const projectId = await resolveProjectId();
|
|
991
|
+
const id = centralDb.getNextId(opts.category, projectId || undefined);
|
|
992
|
+
const now = new Date().toISOString();
|
|
993
|
+
const content = `# ${opts.title}\n\n${opts.content}`;
|
|
994
|
+
const tagsJson = Array.isArray(tags)
|
|
995
|
+
? JSON.stringify(tags)
|
|
996
|
+
: JSON.stringify(Object.values(tags).flat());
|
|
997
|
+
centralDb.insertMemory({
|
|
998
|
+
id,
|
|
999
|
+
title: opts.title,
|
|
1000
|
+
category: opts.category,
|
|
1001
|
+
content,
|
|
1002
|
+
summary: null,
|
|
1003
|
+
tags: tagsJson,
|
|
1004
|
+
relevance: opts.relevance || opts.content.slice(0, 200),
|
|
1005
|
+
author: opts.author,
|
|
1006
|
+
authority: opts.authority,
|
|
1007
|
+
confidence: parseFloat(opts.confidence),
|
|
1008
|
+
reinforcement_count: 0,
|
|
1009
|
+
content_hash: "",
|
|
1010
|
+
status: "active",
|
|
1011
|
+
tier: "active",
|
|
1012
|
+
supersedes: null,
|
|
1013
|
+
superseded_by: null,
|
|
1014
|
+
last_reinforced: null,
|
|
1015
|
+
created: now,
|
|
1016
|
+
modified: now,
|
|
1017
|
+
embedding: null,
|
|
1018
|
+
source_path: null,
|
|
1019
|
+
project_id: projectId,
|
|
1020
|
+
scope: "project",
|
|
1021
|
+
});
|
|
1022
|
+
console.log(`Memory added: ${opts.title}`);
|
|
1023
|
+
console.log(`ID: ${id}`);
|
|
1024
|
+
}
|
|
1025
|
+
finally {
|
|
1026
|
+
centralDb?.close();
|
|
1027
|
+
}
|
|
831
1028
|
});
|
|
832
1029
|
// ─── gnosys ingest <file> ─────────────────────────────────────────────────
|
|
833
1030
|
program
|
|
@@ -1029,49 +1226,70 @@ Output ONLY the JSON array, no markdown fences.`,
|
|
|
1029
1226
|
// Step 2: Check novelty and commit
|
|
1030
1227
|
let added = 0;
|
|
1031
1228
|
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}`);
|
|
1229
|
+
let centralDb = null;
|
|
1230
|
+
try {
|
|
1231
|
+
centralDb = GnosysDB.openCentral();
|
|
1232
|
+
const projectId = await resolveProjectId();
|
|
1233
|
+
for (const candidate of candidates) {
|
|
1234
|
+
const searchTerms = candidate.search_terms.join(" ");
|
|
1235
|
+
const existing = search.discover(searchTerms, 3);
|
|
1236
|
+
if (existing.length > 0) {
|
|
1237
|
+
console.log(` ⏭ SKIP: "${candidate.summary}"`);
|
|
1238
|
+
console.log(` Overlaps with: ${existing[0].title}`);
|
|
1239
|
+
skipped++;
|
|
1240
|
+
}
|
|
1241
|
+
else if (opts.dryRun) {
|
|
1242
|
+
console.log(` ➕ WOULD ADD: "${candidate.summary}" [${candidate.type}]`);
|
|
1068
1243
|
added++;
|
|
1069
1244
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1245
|
+
else {
|
|
1246
|
+
try {
|
|
1247
|
+
const result = await ingestion.ingest(candidate.summary);
|
|
1248
|
+
const id = centralDb.getNextId(result.category, projectId || undefined);
|
|
1249
|
+
const now = new Date().toISOString();
|
|
1250
|
+
const content = `# ${result.title}\n\n${result.content}`;
|
|
1251
|
+
const resultTags = result.tags;
|
|
1252
|
+
const tagsJson = Array.isArray(resultTags)
|
|
1253
|
+
? JSON.stringify(resultTags)
|
|
1254
|
+
: JSON.stringify(Object.values(resultTags).flat());
|
|
1255
|
+
centralDb.insertMemory({
|
|
1256
|
+
id,
|
|
1257
|
+
title: result.title,
|
|
1258
|
+
category: result.category,
|
|
1259
|
+
content,
|
|
1260
|
+
summary: null,
|
|
1261
|
+
tags: tagsJson,
|
|
1262
|
+
relevance: result.relevance,
|
|
1263
|
+
author: "ai",
|
|
1264
|
+
authority: "observed",
|
|
1265
|
+
confidence: result.confidence,
|
|
1266
|
+
reinforcement_count: 0,
|
|
1267
|
+
content_hash: "",
|
|
1268
|
+
status: "active",
|
|
1269
|
+
tier: "active",
|
|
1270
|
+
supersedes: null,
|
|
1271
|
+
superseded_by: null,
|
|
1272
|
+
last_reinforced: null,
|
|
1273
|
+
created: now,
|
|
1274
|
+
modified: now,
|
|
1275
|
+
embedding: null,
|
|
1276
|
+
source_path: null,
|
|
1277
|
+
project_id: projectId,
|
|
1278
|
+
scope: "project",
|
|
1279
|
+
});
|
|
1280
|
+
console.log(` ➕ ADDED: "${result.title}"`);
|
|
1281
|
+
console.log(` ID: ${id}`);
|
|
1282
|
+
added++;
|
|
1283
|
+
}
|
|
1284
|
+
catch (err) {
|
|
1285
|
+
console.error(` ❌ FAILED: "${candidate.summary}": ${err instanceof Error ? err.message : String(err)}`);
|
|
1286
|
+
}
|
|
1072
1287
|
}
|
|
1288
|
+
console.log();
|
|
1073
1289
|
}
|
|
1074
|
-
|
|
1290
|
+
}
|
|
1291
|
+
finally {
|
|
1292
|
+
centralDb?.close();
|
|
1075
1293
|
}
|
|
1076
1294
|
search.close();
|
|
1077
1295
|
const mode = opts.dryRun ? "DRY RUN" : "COMMITTED";
|
|
@@ -2217,6 +2435,9 @@ program
|
|
|
2217
2435
|
}
|
|
2218
2436
|
centralDb.close();
|
|
2219
2437
|
}
|
|
2438
|
+
// Configure IDE hooks for automatic memory recall
|
|
2439
|
+
const { configureIdeHooks } = await import("./lib/projectIdentity.js");
|
|
2440
|
+
await configureIdeHooks(projectDir);
|
|
2220
2441
|
upgraded.push(projectDir);
|
|
2221
2442
|
}
|
|
2222
2443
|
catch (err) {
|
|
@@ -2833,10 +3054,10 @@ program
|
|
|
2833
3054
|
process.exit(1);
|
|
2834
3055
|
}
|
|
2835
3056
|
});
|
|
2836
|
-
// ─── gnosys migrate
|
|
3057
|
+
// ─── gnosys migrate-db ──────────────────────────────────────────────────
|
|
2837
3058
|
program
|
|
2838
|
-
.command("migrate")
|
|
2839
|
-
.description("
|
|
3059
|
+
.command("migrate-db")
|
|
3060
|
+
.description("Legacy data migration. Use --to-central to move per-project stores into the central DB.")
|
|
2840
3061
|
.option("--to-central", "Migrate all discovered per-project stores into ~/.gnosys/gnosys.db")
|
|
2841
3062
|
.option("-v, --verbose", "Verbose output")
|
|
2842
3063
|
.action(async (opts) => {
|