gnosys 5.7.1 → 5.8.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 (47) hide show
  1. package/README.md +14 -0
  2. package/dist/cli.js +61 -9
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.js +71 -21
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/ask.d.ts.map +1 -1
  7. package/dist/lib/ask.js +20 -4
  8. package/dist/lib/ask.js.map +1 -1
  9. package/dist/lib/chat/SlashPalette.d.ts +34 -0
  10. package/dist/lib/chat/SlashPalette.d.ts.map +1 -0
  11. package/dist/lib/chat/SlashPalette.js +49 -0
  12. package/dist/lib/chat/SlashPalette.js.map +1 -0
  13. package/dist/lib/chat/index.d.ts.map +1 -1
  14. package/dist/lib/chat/index.js +6 -4
  15. package/dist/lib/chat/index.js.map +1 -1
  16. package/dist/lib/chat/llmTurn.d.ts.map +1 -1
  17. package/dist/lib/chat/llmTurn.js +4 -1
  18. package/dist/lib/chat/llmTurn.js.map +1 -1
  19. package/dist/lib/chat/render.d.ts.map +1 -1
  20. package/dist/lib/chat/render.js +91 -10
  21. package/dist/lib/chat/render.js.map +1 -1
  22. package/dist/lib/config.d.ts +25 -1
  23. package/dist/lib/config.d.ts.map +1 -1
  24. package/dist/lib/config.js +30 -0
  25. package/dist/lib/config.js.map +1 -1
  26. package/dist/lib/db.d.ts +1 -0
  27. package/dist/lib/db.d.ts.map +1 -1
  28. package/dist/lib/db.js +8 -5
  29. package/dist/lib/db.js.map +1 -1
  30. package/dist/lib/import.d.ts.map +1 -1
  31. package/dist/lib/import.js +2 -1
  32. package/dist/lib/import.js.map +1 -1
  33. package/dist/lib/ingest.d.ts +7 -1
  34. package/dist/lib/ingest.d.ts.map +1 -1
  35. package/dist/lib/ingest.js +23 -4
  36. package/dist/lib/ingest.js.map +1 -1
  37. package/dist/lib/llm.d.ts +1 -1
  38. package/dist/lib/llm.d.ts.map +1 -1
  39. package/dist/lib/llm.js.map +1 -1
  40. package/dist/lib/setup/sections/routing.d.ts.map +1 -1
  41. package/dist/lib/setup/sections/routing.js +4 -2
  42. package/dist/lib/setup/sections/routing.js.map +1 -1
  43. package/dist/lib/setup.d.ts +5 -0
  44. package/dist/lib/setup.d.ts.map +1 -1
  45. package/dist/lib/setup.js +127 -0
  46. package/dist/lib/setup.js.map +1 -1
  47. package/package.json +1 -1
package/README.md CHANGED
@@ -109,6 +109,8 @@ gnosys chat --search <query> # full-text search across session logs
109
109
 
110
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
111
 
112
+ **Slash-command palette (v5.8.0).** Type `/` at column 0 → filterable popup of every chat command appears. Arrow keys navigate, Tab autocompletes the highlighted command into the input, Esc dismisses. Paste detection: when input has newlines or exceeds 200 chars, a compact "[paste: N lines, M chars]" preview surfaces above the input so the buffer stays readable.
113
+
112
114
  **24 slash commands** across reading, recall, writing, focus, and polish — type `/help` inside the TUI for the full list. Highlights:
113
115
 
114
116
  - `/pin <id>`, `/scope`, `/threshold`, `/recall <q>` — tune what shows up in context
@@ -322,6 +324,8 @@ gnosys setup # full wizard (provider, models, IDE, remote sync, dream)
322
324
  gnosys setup models # LLM provider + model only
323
325
  gnosys setup remote # multi-machine sync (NAS/shared drive)
324
326
  gnosys setup dream # Dream Mode designation, schedule, sub-tasks
327
+ gnosys setup chat # chat TUI: provider/model, recall, tools, system prompt (v5.8.0)
328
+ gnosys setup routing # per-task LLM routing (structuring/synthesis/chat/vision/transcription)
325
329
  ```
326
330
 
327
331
  ### Dream Mode setup (v5.4.2+)
@@ -358,6 +362,16 @@ The following commands were removed in favor of the canonical `gnosys setup <thi
358
362
  tells running MCP servers to exit-and-respawn against the new global
359
363
  binary. Run it on each machine.
360
364
 
365
+ ### Behaviour changes in v5.8.0
366
+
367
+ | What | Then | Now |
368
+ |---|---|---|
369
+ | `gnosys_sync` MCP tool | Always wrote a `GNOSYS:START/END` block into `CLAUDE.md` (a tracked file) | Inert by default — returns the block as text only. Pass `commit_to_disk: true` to actually write. |
370
+ | `gnosys_preference_set/delete` MCP success messages | Suggested "Run `gnosys_sync` to update agent rules" | No suggestion — SessionStart hook (`gnosys recall`) injects updated context next session. |
371
+ | `gnosys_add` / `gnosys_commit_context` when project's `gnosys.json` has no `llm` block | Failed with "set ANTHROPIC_API_KEY", ignoring the global xAI/OpenAI/etc. config | Resolves the LLM against the merged project+global config — falls back to the user's global provider. |
372
+ | Chat TUI input | Cleared before user turn appeared (visible glitch) | Both happen in the same render frame. |
373
+ | `gnosys --help`, `gnosys list`, etc. | Loaded `@huggingface/transformers` (80MB) every invocation | Heavy modules lazy-load on actual use (reindex, recall, chat, bootstrap, import). |
374
+
361
375
  ---
362
376
 
363
377
  ### Manual config (if you prefer)
package/dist/cli.js CHANGED
@@ -10,27 +10,33 @@ import os from "os";
10
10
  import { fileURLToPath } from "url";
11
11
  import dotenv from "dotenv";
12
12
  import { readFileSync, existsSync, copyFileSync } from "fs";
13
+ // v5.8.0 (#4): only the lightweight modules are imported at top-level.
14
+ // Anything that pulls @huggingface/transformers, mammoth/pdf-parse/turndown,
15
+ // large file-walking machinery, or otherwise costs >100ms to load gets
16
+ // `await import(...)` inside its own action handler. This keeps
17
+ // `gnosys --help` and other lightweight commands fast.
13
18
  import { GnosysResolver } from "./lib/resolver.js";
14
19
  import { getGnosysHome } from "./lib/paths.js";
15
20
  import { GnosysSearch } from "./lib/search.js";
16
21
  import { GnosysTagRegistry } from "./lib/tags.js";
17
- import { GnosysIngestion } from "./lib/ingest.js";
18
22
  import { applyLens } from "./lib/lensing.js";
19
23
  import { getFileHistory, rollbackToCommit, hasGitHistory, getFileDiff } from "./lib/history.js";
20
24
  import { computeStats } from "./lib/timeline.js";
21
25
  import { buildLinkGraph, getBacklinks, getOutgoingLinks, formatGraphSummary } from "./lib/wikilinks.js";
22
- import { bootstrap, discoverFiles } from "./lib/bootstrap.js";
23
- import { performImport, formatImportSummary } from "./lib/import.js";
24
26
  import { loadConfig, generateConfigTemplate, DEFAULT_CONFIG, writeConfig, resolveTaskModel, ALL_PROVIDERS, getProviderModel } from "./lib/config.js";
25
- import { GnosysEmbeddings } from "./lib/embeddings.js";
26
- import { GnosysHybridSearch } from "./lib/hybridSearch.js";
27
- import { GnosysAsk } from "./lib/ask.js";
28
27
  import { getLLMProvider, isProviderAvailable } from "./lib/llm.js";
29
28
  import { GnosysDB } from "./lib/db.js";
30
- import { migrate, formatMigrationReport } from "./lib/migrate.js";
31
29
  import { createProjectIdentity, readProjectIdentity, findProjectIdentity, migrateProject } from "./lib/projectIdentity.js";
32
30
  import { setPreference, getPreference, getAllPreferences, deletePreference } from "./lib/preferences.js";
33
31
  import { syncToTarget } from "./lib/rulesGen.js";
32
+ // Lazy-loaded inside action handlers (each ~200ms-2.5s on cold cache):
33
+ // - ./lib/embeddings.js (@huggingface/transformers — 80MB)
34
+ // - ./lib/hybridSearch.js (depends on embeddings)
35
+ // - ./lib/ask.js (depends on hybridSearch)
36
+ // - ./lib/import.js (mammoth, pdf-parse, turndown)
37
+ // - ./lib/bootstrap.js (file walking — 2.5s)
38
+ // - ./lib/ingest.js (LLM machinery)
39
+ // - ./lib/migrate.js (only migrate-db needs it)
34
40
  // Load API keys from ~/.config/gnosys/.env (same as MCP server)
35
41
  // IMPORTANT: We use dotenv.parse() instead of dotenv.config() because
36
42
  // dotenv v17+ writes injection notices to stdout, which corrupts
@@ -313,6 +319,7 @@ program
313
319
  .option("--federated", "Use federated search with tier boosting (project > user > global)")
314
320
  .option("--scope <scope>", "Filter by scope: project, user, global (comma-separated for multiple)")
315
321
  .option("-d, --directory <dir>", "Project directory for context")
322
+ .option("--id-format <format>", "ID display format: short | long | raw (default: short)", "short")
316
323
  .action(async (query, opts) => {
317
324
  // Federated search path — uses central DB with tier boosting
318
325
  if (opts.federated || opts.scope) {
@@ -371,11 +378,16 @@ program
371
378
  });
372
379
  return;
373
380
  }
381
+ const { formatMemoryId, buildProjectNameLookup, parseIdFormat } = await import("./lib/idFormat.js");
382
+ const idFormat = parseIdFormat(opts.idFormat);
383
+ const projectNames = buildProjectNameLookup(centralDb);
374
384
  outputResult(!!opts.json, { query, count: results.length, results }, () => {
375
385
  console.log(`Found ${results.length} results for "${query}":\n`);
376
386
  for (const r of results) {
387
+ const projectName = r.project_id ? projectNames.get(r.project_id) || null : null;
388
+ const displayId = formatMemoryId(r.id, projectName, idFormat);
377
389
  console.log(` ${r.title}`);
378
- console.log(` id: ${r.id}`);
390
+ console.log(` id: ${displayId}`);
379
391
  console.log(` ${r.snippet.replace(/>>>/g, "").replace(/<<</g, "")}`);
380
392
  console.log();
381
393
  }
@@ -510,6 +522,7 @@ program
510
522
  }
511
523
  const tagRegistry = new GnosysTagRegistry(writeTarget.store.getStorePath());
512
524
  await tagRegistry.load();
525
+ const { GnosysIngestion } = await import("./lib/ingest.js");
513
526
  const ingestion = new GnosysIngestion(writeTarget.store, tagRegistry);
514
527
  if (!ingestion.isLLMAvailable) {
515
528
  console.error("Error: No LLM provider available. Add an API key to ~/.config/gnosys/.env or use a local model: gnosys config set provider ollama");
@@ -910,6 +923,14 @@ setupCmd
910
923
  const { runDreamSetup } = await import("./lib/setup.js");
911
924
  await runDreamSetup({ directory: process.cwd() });
912
925
  });
926
+ // `gnosys setup chat` — configure chat TUI (provider, recall, tools, prefix)
927
+ setupCmd
928
+ .command("chat")
929
+ .description("Configure the chat TUI — provider/model, recall behavior, tools, system-prompt prefix")
930
+ .action(async () => {
931
+ const { runChatSetup } = await import("./lib/setup.js");
932
+ await runChatSetup({ directory: process.cwd() });
933
+ });
913
934
  // `gnosys setup ides` — configure IDE / MCP integrations standalone
914
935
  setupCmd
915
936
  .command("ides")
@@ -1775,6 +1796,7 @@ program
1775
1796
  }
1776
1797
  const tagRegistry = new GnosysTagRegistry(writeTarget.store.getStorePath());
1777
1798
  await tagRegistry.load();
1799
+ const { GnosysIngestion } = await import("./lib/ingest.js");
1778
1800
  const ingestion = new GnosysIngestion(writeTarget.store, tagRegistry);
1779
1801
  if (!ingestion.isLLMAvailable) {
1780
1802
  console.error("Error: No LLM provider available. Add an API key to ~/.config/gnosys/.env or use a local model: gnosys config set provider ollama");
@@ -2324,6 +2346,7 @@ program
2324
2346
  process.exit(1);
2325
2347
  }
2326
2348
  // Show what we'll scan
2349
+ const { bootstrap, discoverFiles } = await import("./lib/bootstrap.js");
2327
2350
  const files = await discoverFiles(sourceDir, opts.pattern);
2328
2351
  console.log(`Found ${files.length} files in ${sourceDir}\n`);
2329
2352
  if (files.length === 0) {
@@ -2411,6 +2434,8 @@ const importCmd = program
2411
2434
  }
2412
2435
  const tagRegistry = new GnosysTagRegistry(writeTarget.store.getStorePath());
2413
2436
  await tagRegistry.load();
2437
+ const { GnosysIngestion } = await import("./lib/ingest.js");
2438
+ const { performImport, formatImportSummary } = await import("./lib/import.js");
2414
2439
  const ingestion = new GnosysIngestion(writeTarget.store, tagRegistry);
2415
2440
  const format = opts.format;
2416
2441
  const mode = opts.mode;
@@ -2530,6 +2555,8 @@ program
2530
2555
  for (const s of stores) {
2531
2556
  await search.addStoreMemories(s.store, s.label);
2532
2557
  }
2558
+ const { GnosysEmbeddings } = await import("./lib/embeddings.js");
2559
+ const { GnosysHybridSearch } = await import("./lib/hybridSearch.js");
2533
2560
  const embeddings = new GnosysEmbeddings(storePath);
2534
2561
  const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
2535
2562
  console.log("Building semantic embeddings (downloading model on first run)...");
@@ -2606,6 +2633,8 @@ program
2606
2633
  for (const s of stores) {
2607
2634
  await search.addStoreMemories(s.store, s.label);
2608
2635
  }
2636
+ const { GnosysEmbeddings } = await import("./lib/embeddings.js");
2637
+ const { GnosysHybridSearch } = await import("./lib/hybridSearch.js");
2609
2638
  const embeddings = new GnosysEmbeddings(storePath);
2610
2639
  const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
2611
2640
  const mode = opts.mode;
@@ -2653,6 +2682,8 @@ program
2653
2682
  for (const s of stores) {
2654
2683
  await search.addStoreMemories(s.store, s.label);
2655
2684
  }
2685
+ const { GnosysEmbeddings } = await import("./lib/embeddings.js");
2686
+ const { GnosysHybridSearch } = await import("./lib/hybridSearch.js");
2656
2687
  const embeddings = new GnosysEmbeddings(storePath);
2657
2688
  const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
2658
2689
  const results = await hybridSearch.hybridSearch(query, parseInt(opts.limit), "semantic");
@@ -2701,11 +2732,30 @@ program
2701
2732
  for (const s of stores) {
2702
2733
  await search.addStoreMemories(s.store, s.label);
2703
2734
  }
2735
+ const { GnosysEmbeddings } = await import("./lib/embeddings.js");
2736
+ const { GnosysHybridSearch } = await import("./lib/hybridSearch.js");
2737
+ const { GnosysAsk } = await import("./lib/ask.js");
2704
2738
  const embeddings = new GnosysEmbeddings(storePath);
2705
2739
  const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
2706
2740
  const ask = new GnosysAsk(hybridSearch, cliConfig, resolver, storePath);
2707
2741
  if (!ask.isLLMAvailable) {
2708
- console.error("No LLM provider available. Set ANTHROPIC_API_KEY or switch to Ollama: gnosys config set provider ollama");
2742
+ // v5.8.0 (#8): provider-aware error instead of hardcoded ANTHROPIC_API_KEY.
2743
+ const providerName = cliConfig.llm.defaultProvider;
2744
+ const envVarMap = {
2745
+ anthropic: "ANTHROPIC_API_KEY",
2746
+ openai: "OPENAI_API_KEY",
2747
+ groq: "GROQ_API_KEY",
2748
+ xai: "XAI_API_KEY",
2749
+ mistral: "MISTRAL_API_KEY",
2750
+ };
2751
+ const envVar = envVarMap[providerName];
2752
+ if (envVar) {
2753
+ console.error(`No LLM provider available. Configured default is "${providerName}" but its key wasn't found. ` +
2754
+ `Set ${envVar}, run 'gnosys setup' to store one in the macOS Keychain, or add llm.${providerName}.apiKey to gnosys.json.`);
2755
+ }
2756
+ else {
2757
+ console.error(`No LLM provider available. Provider "${providerName}" is not reachable. Run 'gnosys setup' to configure one.`);
2758
+ }
2709
2759
  process.exit(1);
2710
2760
  }
2711
2761
  // If --federated, pre-retrieve from central DB and inject as context
@@ -3584,6 +3634,7 @@ program
3584
3634
  // Check embeddings
3585
3635
  if (stores.length > 0) {
3586
3636
  console.log("Embeddings:");
3637
+ const { GnosysEmbeddings } = await import("./lib/embeddings.js");
3587
3638
  const embeddings = new GnosysEmbeddings(stores[0].path);
3588
3639
  try {
3589
3640
  const stats = embeddings.getStats();
@@ -4389,6 +4440,7 @@ program
4389
4440
  console.error("No writable store found. Run 'gnosys init' first.");
4390
4441
  process.exit(1);
4391
4442
  }
4443
+ const { migrate, formatMigrationReport } = await import("./lib/migrate.js");
4392
4444
  const stats = await migrate(writeTarget.store.getStorePath(), { verbose: opts.verbose });
4393
4445
  console.log(formatMigrationReport(stats));
4394
4446
  return;