gnosys 5.5.0 → 5.6.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.
Files changed (60) hide show
  1. package/README.md +44 -0
  2. package/dist/cli.js +204 -18
  3. package/dist/cli.js.map +1 -1
  4. package/dist/lib/chat/choose.d.ts +75 -0
  5. package/dist/lib/chat/choose.d.ts.map +1 -0
  6. package/dist/lib/chat/choose.js +146 -0
  7. package/dist/lib/chat/choose.js.map +1 -0
  8. package/dist/lib/chat/commands.d.ts +96 -0
  9. package/dist/lib/chat/commands.d.ts.map +1 -0
  10. package/dist/lib/chat/commands.js +367 -0
  11. package/dist/lib/chat/commands.js.map +1 -0
  12. package/dist/lib/chat/focus.d.ts +70 -0
  13. package/dist/lib/chat/focus.d.ts.map +1 -0
  14. package/dist/lib/chat/focus.js +120 -0
  15. package/dist/lib/chat/focus.js.map +1 -0
  16. package/dist/lib/chat/index.d.ts +32 -0
  17. package/dist/lib/chat/index.d.ts.map +1 -0
  18. package/dist/lib/chat/index.js +151 -0
  19. package/dist/lib/chat/index.js.map +1 -0
  20. package/dist/lib/chat/intent.d.ts +100 -0
  21. package/dist/lib/chat/intent.d.ts.map +1 -0
  22. package/dist/lib/chat/intent.js +192 -0
  23. package/dist/lib/chat/intent.js.map +1 -0
  24. package/dist/lib/chat/llmTurn.d.ts +37 -0
  25. package/dist/lib/chat/llmTurn.d.ts.map +1 -0
  26. package/dist/lib/chat/llmTurn.js +61 -0
  27. package/dist/lib/chat/llmTurn.js.map +1 -0
  28. package/dist/lib/chat/recall.d.ts +58 -0
  29. package/dist/lib/chat/recall.d.ts.map +1 -0
  30. package/dist/lib/chat/recall.js +109 -0
  31. package/dist/lib/chat/recall.js.map +1 -0
  32. package/dist/lib/chat/render.d.ts +30 -0
  33. package/dist/lib/chat/render.d.ts.map +1 -0
  34. package/dist/lib/chat/render.js +737 -0
  35. package/dist/lib/chat/render.js.map +1 -0
  36. package/dist/lib/chat/session.d.ts +121 -0
  37. package/dist/lib/chat/session.d.ts.map +1 -0
  38. package/dist/lib/chat/session.js +148 -0
  39. package/dist/lib/chat/session.js.map +1 -0
  40. package/dist/lib/chat/types.d.ts +42 -0
  41. package/dist/lib/chat/types.d.ts.map +1 -0
  42. package/dist/lib/chat/types.js +6 -0
  43. package/dist/lib/chat/types.js.map +1 -0
  44. package/dist/lib/chat/write.d.ts +66 -0
  45. package/dist/lib/chat/write.d.ts.map +1 -0
  46. package/dist/lib/chat/write.js +203 -0
  47. package/dist/lib/chat/write.js.map +1 -0
  48. package/dist/lib/db.d.ts +3 -1
  49. package/dist/lib/db.d.ts.map +1 -1
  50. package/dist/lib/db.js +18 -2
  51. package/dist/lib/db.js.map +1 -1
  52. package/dist/lib/exportProject.d.ts +51 -0
  53. package/dist/lib/exportProject.d.ts.map +1 -0
  54. package/dist/lib/exportProject.js +72 -0
  55. package/dist/lib/exportProject.js.map +1 -0
  56. package/dist/lib/importProject.d.ts +35 -0
  57. package/dist/lib/importProject.d.ts.map +1 -0
  58. package/dist/lib/importProject.js +135 -0
  59. package/dist/lib/importProject.js.map +1 -0
  60. package/package.json +7 -1
package/README.md CHANGED
@@ -96,6 +96,50 @@ The helper auto-starts the sandbox if it's not running. No MCP required.
96
96
 
97
97
  ---
98
98
 
99
+ ## Interactive Chat (TUI)
100
+
101
+ `gnosys chat` opens a memory-aware terminal chat. Every prompt triggers federated recall against the central brain; the LLM sees relevant memories in context and cites them in its answers.
102
+
103
+ ```bash
104
+ gnosys chat # new session
105
+ gnosys chat --resume <id> # continue an earlier session
106
+ gnosys chat --list # see all sessions
107
+ gnosys chat --search <query> # full-text search across session logs
108
+ ```
109
+
110
+ **Free-text or slash commands.** "remember that flag default is OFF" works the same as `/remember flag default is OFF`. The TUI also recognizes "what did we decide about ULIDs?" → `/recall`, "thanks, that's all" → `/quit`. Destructive intents always confirm; non-destructive ones auto-accept after 5 confirmations of the same pattern.
111
+
112
+ **24 slash commands** across reading, recall, writing, focus, and polish — type `/help` inside the TUI for the full list. Highlights:
113
+
114
+ - `/pin <id>`, `/scope`, `/threshold`, `/recall <q>` — tune what shows up in context
115
+ - `/remember <text>`, `/save-turn`, `/attach <file>` — promote chat content to gnosys memory (PDFs, images, audio all auto-pin to the session)
116
+ - `/focus <topic>`, `/branch`, `/resume-focus` — replace the "new chat" model with cheap focus boundaries; one continuous session log, instant pivot
117
+ - `/export <file.md>`, `/search-chats <q>`, `/dream-here` — round-trip the session, find old chats, or trigger a focused dream cycle
118
+
119
+ **Multiple choice.** When the model needs you to pick from a small set, it emits a fenced `gnosys-choose` block. The TUI parses it and shows an arrow-key selectable list — provider-agnostic, no tool-use API required.
120
+
121
+ Sessions live as append-only JSONL at `~/.gnosys/chat-sessions/`; promoted memories carry `session:<id>`, `from-chat:true`, and `source:remember|save-turn|auto|attach` provenance tags so you can find them later via federated search.
122
+
123
+ ---
124
+
125
+ ## Per-Project Bundles
126
+
127
+ Move a single project's memories between machines without dragging the whole central DB.
128
+
129
+ ```bash
130
+ gnosys export project --to ./gnosys-public.json.gz # auto-detects current project
131
+ gnosys export project <projectId> --to <bundle> # explicit
132
+ gnosys import project <bundle> --strategy merge # default — skip existing
133
+ gnosys import project <bundle> --strategy replace # wipe target project first
134
+ gnosys import project <bundle> --strategy new-id # remap to a fresh project ID
135
+ ```
136
+
137
+ Bundles are gzipped JSON containing the project row, memories (with embeddings inline), relationships, and audit log. Lossless round-trip with the same DB schema; partially compatible across versions via the bundle manifest.
138
+
139
+ For an Obsidian-compatible markdown vault, use `gnosys export vault --to <dir>` (the v5.5.x form `gnosys export --to <dir>` keeps working).
140
+
141
+ ---
142
+
99
143
  ## Web Knowledge Base
100
144
 
101
145
  Turn any website into a searchable knowledge base for AI chatbots. No database required. Works on Vercel, Netlify, Cloudflare Pages, or any platform that can serve files.
package/dist/cli.js CHANGED
@@ -108,6 +108,32 @@ function maybePrintUpgradeNudge() {
108
108
  }
109
109
  }
110
110
  maybePrintUpgradeNudge();
111
+ /**
112
+ * v5.6.0 back-compat shim: rewrite `gnosys export --to <dir>` →
113
+ * `gnosys export vault --to <dir>` before commander parses argv. The v5.6.0
114
+ * restructure made `export` a parent command with `vault` and `project`
115
+ * subcommands; without this shim, the bare `--to` form prints usage instead
116
+ * of running the vault export.
117
+ *
118
+ * Pattern: argv[2]==="export" AND argv[3] is not a known subcommand AND any
119
+ * of the v5.5.x flags appear (`--to`, `--all`, `--overwrite`, etc.).
120
+ */
121
+ function rewriteLegacyExport() {
122
+ if (process.argv[2] !== "export")
123
+ return;
124
+ const next = process.argv[3];
125
+ if (next === "vault" || next === "project" || next === "--help" || next === "-h")
126
+ return;
127
+ // Any v5.5.x-style flag → assume legacy vault invocation
128
+ const looksLegacy = process.argv.slice(3).some((a) => a === "--to" || a.startsWith("--to=") ||
129
+ a === "--all" || a === "--overwrite" ||
130
+ a === "--no-summaries" || a === "--no-reviews" || a === "--no-graph" ||
131
+ a === "--json");
132
+ if (looksLegacy) {
133
+ process.argv.splice(3, 0, "vault");
134
+ }
135
+ }
136
+ rewriteLegacyExport();
111
137
  async function getResolver() {
112
138
  const resolver = new GnosysResolver();
113
139
  await resolver.resolve();
@@ -1218,6 +1244,45 @@ program
1218
1244
  centralDb?.close();
1219
1245
  }
1220
1246
  });
1247
+ // ─── gnosys chat (TUI) ───────────────────────────────────────────────────
1248
+ program
1249
+ .command("chat")
1250
+ .description("Interactive memory-aware terminal chat (TUI)")
1251
+ .option("--resume <sessionId>", "Resume an existing chat session")
1252
+ .option("--list", "List recent chat sessions and exit")
1253
+ .option("--search <query>", "Full-text search across session logs")
1254
+ .option("--provider <name>", "Override LLM provider (anthropic, openai, groq, ollama, …)")
1255
+ .option("--model <name>", "Override LLM model name")
1256
+ .option("--limit <n>", "Limit for --list / --search (default 20)", "20")
1257
+ .action(async (opts) => {
1258
+ const limit = parseInt(opts.limit, 10) || 20;
1259
+ const chat = await import("./lib/chat/index.js");
1260
+ if (opts.list) {
1261
+ chat.printSessionList(limit);
1262
+ return;
1263
+ }
1264
+ if (opts.search) {
1265
+ chat.printSearchResults(opts.search, limit);
1266
+ return;
1267
+ }
1268
+ // Interactive chat
1269
+ const resolver = await getResolver();
1270
+ const stores = resolver.getStores();
1271
+ const storePath = stores[0]?.path ?? process.cwd();
1272
+ let cliConfig;
1273
+ try {
1274
+ cliConfig = await loadConfig(storePath);
1275
+ }
1276
+ catch {
1277
+ cliConfig = (await import("./lib/config.js")).DEFAULT_CONFIG;
1278
+ }
1279
+ await chat.startChat({
1280
+ config: cliConfig,
1281
+ resume: opts.resume,
1282
+ providerName: opts.provider,
1283
+ modelName: opts.model,
1284
+ });
1285
+ });
1221
1286
  // ─── gnosys ingest <file> ─────────────────────────────────────────────────
1222
1287
  program
1223
1288
  .command("ingest <fileOrGlob>")
@@ -1876,12 +1941,13 @@ program
1876
1941
  }
1877
1942
  }
1878
1943
  });
1879
- // ─── gnosys import <file> ────────────────────────────────────────────────
1880
- program
1881
- .command("import <fileOrUrl>")
1882
- .description("Bulk import structured data (CSV, JSON, JSONL) into Gnosys memories")
1883
- .requiredOption("--format <format>", "Data format: csv, json, jsonl")
1884
- .requiredOption("--mapping <json>", 'Field mapping as JSON: \'{"source_field":"gnosys_field"}\'. Valid targets: title, category, content, tags, relevance')
1944
+ // ─── gnosys import (parent + subcommands) ───────────────────────────────
1945
+ const importCmd = program
1946
+ .command("import [fileOrUrl]")
1947
+ .enablePositionalOptions()
1948
+ .description("Import data into Gnosys (bulk CSV/JSON/JSONL — see also: 'gnosys import project <bundle>')")
1949
+ .option("--format <format>", "Data format: csv, json, jsonl (required for bulk import)")
1950
+ .option("--mapping <json>", 'Field mapping as JSON: \'{"source_field":"gnosys_field"}\'. Valid targets: title, category, content, tags, relevance')
1885
1951
  .option("--mode <mode>", "Processing mode: llm or structured", "structured")
1886
1952
  .option("--limit <n>", "Max records to import", parseInt)
1887
1953
  .option("--offset <n>", "Skip first N records", parseInt)
@@ -1892,6 +1958,17 @@ program
1892
1958
  .option("--dry-run", "Preview without writing")
1893
1959
  .option("--store <store>", "Target store: project, personal, global", "project")
1894
1960
  .action(async (fileOrUrl, opts) => {
1961
+ if (!fileOrUrl) {
1962
+ console.error("Usage:");
1963
+ console.error(" gnosys import <file> --format csv|json|jsonl --mapping '{...}' (bulk)");
1964
+ console.error(" gnosys import project <bundle.json.gz> (project bundle)");
1965
+ process.exit(1);
1966
+ }
1967
+ if (!opts.format || !opts.mapping) {
1968
+ console.error("Error: --format and --mapping are required for bulk imports.");
1969
+ console.error(" For project bundles, use 'gnosys import project <bundle>'.");
1970
+ process.exit(1);
1971
+ }
1895
1972
  // Parse mapping JSON
1896
1973
  let mapping;
1897
1974
  try {
@@ -1966,6 +2043,51 @@ program
1966
2043
  process.exit(1);
1967
2044
  }
1968
2045
  });
2046
+ // `gnosys import project <bundle>` — restore a portable .json.gz bundle
2047
+ importCmd
2048
+ .command("project <bundlePath>")
2049
+ .description("Import a project bundle (.json.gz) created by 'gnosys export project'")
2050
+ .option("--strategy <strategy>", "Conflict handling: merge (default), replace, new-id", "merge")
2051
+ .option("--working-directory <dir>", "Override the bundle's working_directory (e.g. when restoring on a different machine)")
2052
+ .option("--json", "Output the result as JSON")
2053
+ .action(async (bundlePath, opts) => {
2054
+ const validStrategies = ["merge", "replace", "new-id"];
2055
+ if (!validStrategies.includes(opts.strategy)) {
2056
+ console.error(`Invalid strategy: ${opts.strategy}. Use one of: ${validStrategies.join(", ")}`);
2057
+ process.exit(1);
2058
+ }
2059
+ const { GnosysDB: DbClass } = await import("./lib/db.js");
2060
+ const { importProject } = await import("./lib/importProject.js");
2061
+ const centralDb = DbClass.openCentral();
2062
+ if (!centralDb.isAvailable()) {
2063
+ console.error("Central DB unavailable.");
2064
+ process.exit(1);
2065
+ }
2066
+ try {
2067
+ const result = importProject(centralDb, {
2068
+ bundlePath: path.resolve(bundlePath),
2069
+ strategy: opts.strategy,
2070
+ workingDirectoryOverride: opts.workingDirectory,
2071
+ });
2072
+ if (opts.json) {
2073
+ console.log(JSON.stringify(result, null, 2));
2074
+ }
2075
+ else {
2076
+ console.log(`Imported project ${result.projectName} (${result.projectId})`);
2077
+ console.log(` Strategy: ${result.strategy}`);
2078
+ console.log(` Memories: ${result.memoriesInserted} inserted, ${result.memoriesSkipped} skipped, ${result.memoriesReplaced} replaced`);
2079
+ console.log(` Relationships: ${result.relationshipsInserted}`);
2080
+ console.log(` Audit entries: ${result.auditEntriesInserted}`);
2081
+ }
2082
+ }
2083
+ catch (err) {
2084
+ console.error(`Import failed: ${err instanceof Error ? err.message : String(err)}`);
2085
+ process.exit(1);
2086
+ }
2087
+ finally {
2088
+ centralDb.close();
2089
+ }
2090
+ });
1969
2091
  // ─── gnosys reindex ──────────────────────────────────────────────────────
1970
2092
  program
1971
2093
  .command("reindex")
@@ -3495,18 +3617,7 @@ dreamCmd
3495
3617
  centralDb?.close();
3496
3618
  }
3497
3619
  });
3498
- // ─── gnosys export ───────────────────────────────────────────────────────
3499
- program
3500
- .command("export")
3501
- .description("Export gnosys.db to Obsidian-compatible vault (one-way)")
3502
- .requiredOption("--to <dir>", "Target directory for export")
3503
- .option("--all", "Export all memories (active + archived)")
3504
- .option("--overwrite", "Overwrite existing files")
3505
- .option("--no-summaries", "Skip category summaries")
3506
- .option("--no-reviews", "Skip review suggestions")
3507
- .option("--no-graph", "Skip relationship graph")
3508
- .option("--json", "Output raw JSON report")
3509
- .action(async (opts) => {
3620
+ async function runVaultExport(opts) {
3510
3621
  const resolver = new GnosysResolver();
3511
3622
  await resolver.resolve();
3512
3623
  const stores = resolver.getStores();
@@ -3545,6 +3656,81 @@ program
3545
3656
  console.log(formatExportReport(report));
3546
3657
  }
3547
3658
  db.close();
3659
+ }
3660
+ const exportCmd = program
3661
+ .command("export")
3662
+ .description("Export memory to a vault (markdown) or a project bundle (.json.gz)")
3663
+ .enablePositionalOptions();
3664
+ // Bare `gnosys export` shows the canonical subcommand forms. Back-compat for
3665
+ // the v5.5.x form `gnosys export --to <dir>` is handled in a pre-parse shim
3666
+ // at the top of the file (rewrites argv to insert "vault" before "--to").
3667
+ exportCmd.action(() => {
3668
+ console.error("Usage: gnosys export vault --to <dir> # Obsidian vault export");
3669
+ console.error(" gnosys export project [id] --to <bundle> # portable .json.gz bundle");
3670
+ process.exit(1);
3671
+ });
3672
+ // `gnosys export vault` — explicit alias for the default behavior
3673
+ exportCmd
3674
+ .command("vault")
3675
+ .description("Export gnosys.db to an Obsidian-compatible vault (one-way)")
3676
+ .requiredOption("--to <dir>", "Target directory for export")
3677
+ .option("--all", "Export all memories (active + archived)")
3678
+ .option("--overwrite", "Overwrite existing files")
3679
+ .option("--no-summaries", "Skip category summaries")
3680
+ .option("--no-reviews", "Skip review suggestions")
3681
+ .option("--no-graph", "Skip relationship graph")
3682
+ .option("--json", "Output raw JSON report")
3683
+ .action(runVaultExport);
3684
+ // `gnosys export project [id]` — bundle a single project for portability
3685
+ exportCmd
3686
+ .command("project [projectId]")
3687
+ .description("Export a single project to a portable .json.gz bundle (round-trips with 'gnosys import project')")
3688
+ .requiredOption("--to <file>", "Output bundle file path (e.g. ./gnosys-public.gnosys.json.gz)")
3689
+ .option("--include-archived", "Include archived and superseded memories (default: active only)")
3690
+ .option("--no-audit", "Skip the audit log")
3691
+ .option("--json", "Output the result as JSON")
3692
+ .action(async (projectIdArg, opts) => {
3693
+ const { GnosysDB: DbClass } = await import("./lib/db.js");
3694
+ const { exportProject } = await import("./lib/exportProject.js");
3695
+ const centralDb = DbClass.openCentral();
3696
+ if (!centralDb.isAvailable()) {
3697
+ console.error("Central DB unavailable.");
3698
+ process.exit(1);
3699
+ }
3700
+ let projectId = projectIdArg;
3701
+ if (!projectId) {
3702
+ // Auto-detect from cwd
3703
+ const proj = centralDb.getProjectByDirectory(process.cwd());
3704
+ if (!proj) {
3705
+ console.error("No project ID given and current directory is not a registered project.");
3706
+ console.error("Usage: gnosys export project <projectId> --to <file>");
3707
+ process.exit(1);
3708
+ }
3709
+ projectId = proj.id;
3710
+ }
3711
+ try {
3712
+ const result = exportProject(centralDb, {
3713
+ projectId,
3714
+ outputPath: path.resolve(opts.to),
3715
+ includeArchived: !!opts.includeArchived,
3716
+ includeAudit: opts.audit !== false,
3717
+ });
3718
+ if (opts.json) {
3719
+ console.log(JSON.stringify(result, null, 2));
3720
+ }
3721
+ else {
3722
+ const ratio = (result.compressedBytes / result.uncompressedBytes * 100).toFixed(1);
3723
+ console.log(`Exported project ${projectId}`);
3724
+ console.log(` Memories: ${result.memoryCount}`);
3725
+ console.log(` Relationships: ${result.relationshipCount}`);
3726
+ console.log(` Audit entries: ${result.auditEntryCount}`);
3727
+ console.log(` Bundle: ${result.outputPath}`);
3728
+ console.log(` Size: ${(result.compressedBytes / 1024).toFixed(1)} KB compressed (${ratio}% of ${(result.uncompressedBytes / 1024).toFixed(1)} KB)`);
3729
+ }
3730
+ }
3731
+ finally {
3732
+ centralDb.close();
3733
+ }
3548
3734
  });
3549
3735
  // ─── gnosys serve ────────────────────────────────────────────────────────
3550
3736
  program