gnosys 5.7.0 → 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.
- package/README.md +29 -2
- package/dist/cli.js +249 -117
- package/dist/cli.js.map +1 -1
- package/dist/index.js +167 -25
- package/dist/index.js.map +1 -1
- package/dist/lib/ask.d.ts.map +1 -1
- package/dist/lib/ask.js +20 -4
- package/dist/lib/ask.js.map +1 -1
- package/dist/lib/chat/SlashPalette.d.ts +34 -0
- package/dist/lib/chat/SlashPalette.d.ts.map +1 -0
- package/dist/lib/chat/SlashPalette.js +49 -0
- package/dist/lib/chat/SlashPalette.js.map +1 -0
- package/dist/lib/chat/index.d.ts.map +1 -1
- package/dist/lib/chat/index.js +6 -4
- package/dist/lib/chat/index.js.map +1 -1
- package/dist/lib/chat/llmTurn.d.ts.map +1 -1
- package/dist/lib/chat/llmTurn.js +4 -1
- package/dist/lib/chat/llmTurn.js.map +1 -1
- package/dist/lib/chat/render.d.ts.map +1 -1
- package/dist/lib/chat/render.js +91 -10
- package/dist/lib/chat/render.js.map +1 -1
- package/dist/lib/config.d.ts +25 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +30 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/db.d.ts +21 -0
- package/dist/lib/db.d.ts.map +1 -1
- package/dist/lib/db.js +44 -17
- package/dist/lib/db.js.map +1 -1
- package/dist/lib/heartbeat.d.ts +31 -0
- package/dist/lib/heartbeat.d.ts.map +1 -0
- package/dist/lib/heartbeat.js +91 -0
- package/dist/lib/heartbeat.js.map +1 -0
- package/dist/lib/idFormat.d.ts +41 -0
- package/dist/lib/idFormat.d.ts.map +1 -0
- package/dist/lib/idFormat.js +66 -0
- package/dist/lib/idFormat.js.map +1 -0
- package/dist/lib/import.d.ts.map +1 -1
- package/dist/lib/import.js +2 -1
- package/dist/lib/import.js.map +1 -1
- package/dist/lib/ingest.d.ts +7 -1
- package/dist/lib/ingest.d.ts.map +1 -1
- package/dist/lib/ingest.js +23 -4
- package/dist/lib/ingest.js.map +1 -1
- package/dist/lib/llm.d.ts +1 -1
- package/dist/lib/llm.d.ts.map +1 -1
- package/dist/lib/llm.js.map +1 -1
- package/dist/lib/progress.d.ts +54 -0
- package/dist/lib/progress.d.ts.map +1 -0
- package/dist/lib/progress.js +92 -0
- package/dist/lib/progress.js.map +1 -0
- package/dist/lib/remote.d.ts +14 -1
- package/dist/lib/remote.d.ts.map +1 -1
- package/dist/lib/remote.js +75 -28
- package/dist/lib/remote.js.map +1 -1
- package/dist/lib/setup/sections/routing.d.ts.map +1 -1
- package/dist/lib/setup/sections/routing.js +4 -2
- package/dist/lib/setup/sections/routing.js.map +1 -1
- package/dist/lib/setup.d.ts +5 -0
- package/dist/lib/setup.d.ts.map +1 -1
- package/dist/lib/setup.js +127 -0
- package/dist/lib/setup.js.map +1 -1
- package/dist/lib/upgrade.d.ts +38 -0
- package/dist/lib/upgrade.d.ts.map +1 -0
- package/dist/lib/upgrade.js +61 -0
- package/dist/lib/upgrade.js.map +1 -0
- package/package.json +1 -1
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
|
|
@@ -225,6 +231,7 @@ program
|
|
|
225
231
|
.option("--federated", "Use federated discovery with tier boosting (project > user > global)")
|
|
226
232
|
.option("--scope <scope>", "Filter by scope: project, user, global (comma-separated for multiple)")
|
|
227
233
|
.option("-d, --directory <dir>", "Project directory for context")
|
|
234
|
+
.option("--id-format <format>", "ID display format: short | long | raw (default: short)", "short")
|
|
228
235
|
.action(async (query, opts) => {
|
|
229
236
|
// Federated discover path
|
|
230
237
|
if (opts.federated || opts.scope) {
|
|
@@ -279,11 +286,16 @@ program
|
|
|
279
286
|
});
|
|
280
287
|
return;
|
|
281
288
|
}
|
|
289
|
+
const { formatMemoryId, buildProjectNameLookup, parseIdFormat } = await import("./lib/idFormat.js");
|
|
290
|
+
const idFormat = parseIdFormat(opts.idFormat);
|
|
291
|
+
const projectNames = buildProjectNameLookup(centralDb);
|
|
282
292
|
outputResult(!!opts.json, { query, count: results.length, results }, () => {
|
|
283
293
|
console.log(`Found ${results.length} relevant memories for "${query}":\n`);
|
|
284
294
|
for (const r of results) {
|
|
295
|
+
const projectName = r.project_id ? projectNames.get(r.project_id) || null : null;
|
|
296
|
+
const displayId = formatMemoryId(r.id, projectName, idFormat);
|
|
285
297
|
console.log(` ${r.title}`);
|
|
286
|
-
console.log(` id: ${
|
|
298
|
+
console.log(` id: ${displayId}`);
|
|
287
299
|
if (r.relevance)
|
|
288
300
|
console.log(` Relevance: ${r.relevance}`);
|
|
289
301
|
console.log();
|
|
@@ -307,6 +319,7 @@ program
|
|
|
307
319
|
.option("--federated", "Use federated search with tier boosting (project > user > global)")
|
|
308
320
|
.option("--scope <scope>", "Filter by scope: project, user, global (comma-separated for multiple)")
|
|
309
321
|
.option("-d, --directory <dir>", "Project directory for context")
|
|
322
|
+
.option("--id-format <format>", "ID display format: short | long | raw (default: short)", "short")
|
|
310
323
|
.action(async (query, opts) => {
|
|
311
324
|
// Federated search path — uses central DB with tier boosting
|
|
312
325
|
if (opts.federated || opts.scope) {
|
|
@@ -365,11 +378,16 @@ program
|
|
|
365
378
|
});
|
|
366
379
|
return;
|
|
367
380
|
}
|
|
381
|
+
const { formatMemoryId, buildProjectNameLookup, parseIdFormat } = await import("./lib/idFormat.js");
|
|
382
|
+
const idFormat = parseIdFormat(opts.idFormat);
|
|
383
|
+
const projectNames = buildProjectNameLookup(centralDb);
|
|
368
384
|
outputResult(!!opts.json, { query, count: results.length, results }, () => {
|
|
369
385
|
console.log(`Found ${results.length} results for "${query}":\n`);
|
|
370
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);
|
|
371
389
|
console.log(` ${r.title}`);
|
|
372
|
-
console.log(` id: ${
|
|
390
|
+
console.log(` id: ${displayId}`);
|
|
373
391
|
console.log(` ${r.snippet.replace(/>>>/g, "").replace(/<<</g, "")}`);
|
|
374
392
|
console.log();
|
|
375
393
|
}
|
|
@@ -391,6 +409,7 @@ program
|
|
|
391
409
|
.option("-t, --tag <tag>", "Filter by tag")
|
|
392
410
|
.option("-s, --store <store>", "Filter by store layer (project|user|global)")
|
|
393
411
|
.option("--json", "Output as JSON")
|
|
412
|
+
.option("--id-format <format>", "ID display format: short | long | raw (default: short)", "short")
|
|
394
413
|
.action(async (opts) => {
|
|
395
414
|
let centralDb = null;
|
|
396
415
|
try {
|
|
@@ -424,6 +443,9 @@ program
|
|
|
424
443
|
}
|
|
425
444
|
});
|
|
426
445
|
}
|
|
446
|
+
const { formatMemoryId, buildProjectNameLookup, parseIdFormat } = await import("./lib/idFormat.js");
|
|
447
|
+
const idFormat = parseIdFormat(opts.idFormat);
|
|
448
|
+
const projectNames = buildProjectNameLookup(centralDb);
|
|
427
449
|
outputResult(!!opts.json, {
|
|
428
450
|
count: memories.length,
|
|
429
451
|
memories: memories.map((m) => ({
|
|
@@ -433,12 +455,15 @@ program
|
|
|
433
455
|
status: m.status,
|
|
434
456
|
scope: m.scope,
|
|
435
457
|
confidence: m.confidence,
|
|
458
|
+
project: m.project_id ? projectNames.get(m.project_id) || null : null,
|
|
436
459
|
})),
|
|
437
460
|
}, () => {
|
|
438
461
|
console.log(`${memories.length} memories:\n`);
|
|
439
462
|
for (const m of memories) {
|
|
463
|
+
const projectName = m.project_id ? projectNames.get(m.project_id) || null : null;
|
|
464
|
+
const displayId = formatMemoryId(m.id, projectName, idFormat);
|
|
440
465
|
console.log(` [${m.scope}] [${m.status}] ${m.title}`);
|
|
441
|
-
console.log(` id: ${
|
|
466
|
+
console.log(` id: ${displayId} | category: ${m.category} | confidence: ${m.confidence}`);
|
|
442
467
|
console.log();
|
|
443
468
|
}
|
|
444
469
|
});
|
|
@@ -497,6 +522,7 @@ program
|
|
|
497
522
|
}
|
|
498
523
|
const tagRegistry = new GnosysTagRegistry(writeTarget.store.getStorePath());
|
|
499
524
|
await tagRegistry.load();
|
|
525
|
+
const { GnosysIngestion } = await import("./lib/ingest.js");
|
|
500
526
|
const ingestion = new GnosysIngestion(writeTarget.store, tagRegistry);
|
|
501
527
|
if (!ingestion.isLLMAvailable) {
|
|
502
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");
|
|
@@ -654,8 +680,9 @@ setupRemoteCmd
|
|
|
654
680
|
return;
|
|
655
681
|
}
|
|
656
682
|
const { RemoteSync, formatStatus } = await import("./lib/remote.js");
|
|
683
|
+
const { withHeartbeat } = await import("./lib/heartbeat.js");
|
|
657
684
|
const sync = new RemoteSync(centralDb, remotePath);
|
|
658
|
-
const status = await sync.getStatus();
|
|
685
|
+
const status = await withHeartbeat("Checking remote sync status", () => sync.getStatus());
|
|
659
686
|
sync.closeRemote();
|
|
660
687
|
if (opts.json) {
|
|
661
688
|
console.log(JSON.stringify(status, null, 2));
|
|
@@ -685,6 +712,7 @@ setupRemoteCmd
|
|
|
685
712
|
.command("push")
|
|
686
713
|
.description("Push local changes to remote")
|
|
687
714
|
.option("--newer-wins", "Auto-resolve conflicts by taking the newer version")
|
|
715
|
+
.option("--verbose", "Stream per-memory progress to stderr")
|
|
688
716
|
.action(async (opts) => {
|
|
689
717
|
let centralDb = null;
|
|
690
718
|
try {
|
|
@@ -699,8 +727,18 @@ setupRemoteCmd
|
|
|
699
727
|
process.exit(1);
|
|
700
728
|
}
|
|
701
729
|
const { RemoteSync } = await import("./lib/remote.js");
|
|
730
|
+
const { withHeartbeat } = await import("./lib/heartbeat.js");
|
|
731
|
+
const { createProgress } = await import("./lib/progress.js");
|
|
732
|
+
const progress = createProgress(!!opts.verbose);
|
|
702
733
|
const sync = new RemoteSync(centralDb, remotePath);
|
|
703
|
-
|
|
734
|
+
// Suppress heartbeat when verbose is on (progress already streams).
|
|
735
|
+
const runPush = () => sync.push({
|
|
736
|
+
strategy: opts.newerWins ? "newer-wins" : "skip-and-flag",
|
|
737
|
+
onProgress: progress.noop ? undefined : progress.emit.bind(progress),
|
|
738
|
+
});
|
|
739
|
+
const result = opts.verbose
|
|
740
|
+
? await runPush()
|
|
741
|
+
: await withHeartbeat("Pushing to remote", runPush);
|
|
704
742
|
sync.closeRemote();
|
|
705
743
|
const projParts = (result.projectsPushed || 0) > 0 ? ` | Projects pushed: ${result.projectsPushed}` : "";
|
|
706
744
|
const auditParts = (result.auditPushed || 0) > 0 ? ` | Audit pushed: ${result.auditPushed}` : "";
|
|
@@ -728,6 +766,7 @@ setupRemoteCmd
|
|
|
728
766
|
.command("pull")
|
|
729
767
|
.description("Pull remote changes to local")
|
|
730
768
|
.option("--newer-wins", "Auto-resolve conflicts by taking the newer version")
|
|
769
|
+
.option("--verbose", "Stream per-memory progress to stderr")
|
|
731
770
|
.action(async (opts) => {
|
|
732
771
|
let centralDb = null;
|
|
733
772
|
try {
|
|
@@ -742,8 +781,17 @@ setupRemoteCmd
|
|
|
742
781
|
process.exit(1);
|
|
743
782
|
}
|
|
744
783
|
const { RemoteSync } = await import("./lib/remote.js");
|
|
784
|
+
const { withHeartbeat } = await import("./lib/heartbeat.js");
|
|
785
|
+
const { createProgress } = await import("./lib/progress.js");
|
|
786
|
+
const progress = createProgress(!!opts.verbose);
|
|
745
787
|
const sync = new RemoteSync(centralDb, remotePath);
|
|
746
|
-
const
|
|
788
|
+
const runPull = () => sync.pull({
|
|
789
|
+
strategy: opts.newerWins ? "newer-wins" : "skip-and-flag",
|
|
790
|
+
onProgress: progress.noop ? undefined : progress.emit.bind(progress),
|
|
791
|
+
});
|
|
792
|
+
const result = opts.verbose
|
|
793
|
+
? await runPull()
|
|
794
|
+
: await withHeartbeat("Pulling from remote", runPull);
|
|
747
795
|
sync.closeRemote();
|
|
748
796
|
const projParts = (result.projectsPulled || 0) > 0 ? ` | Projects pulled: ${result.projectsPulled}` : "";
|
|
749
797
|
const auditParts = (result.auditPulled || 0) > 0 ? ` | Audit pulled: ${result.auditPulled}` : "";
|
|
@@ -767,6 +815,7 @@ setupRemoteCmd
|
|
|
767
815
|
.description("Two-way sync: push local changes then pull remote changes")
|
|
768
816
|
.option("--auto", "Run silently for cron/LaunchAgent (skip-and-flag for conflicts)")
|
|
769
817
|
.option("--newer-wins", "Auto-resolve conflicts by taking the newer version")
|
|
818
|
+
.option("--verbose", "Stream per-memory progress to stderr")
|
|
770
819
|
.action(async (opts) => {
|
|
771
820
|
let centralDb = null;
|
|
772
821
|
try {
|
|
@@ -783,11 +832,20 @@ setupRemoteCmd
|
|
|
783
832
|
process.exit(opts.auto ? 0 : 1);
|
|
784
833
|
}
|
|
785
834
|
const { RemoteSync } = await import("./lib/remote.js");
|
|
835
|
+
const { withHeartbeat } = await import("./lib/heartbeat.js");
|
|
836
|
+
const { createProgress } = await import("./lib/progress.js");
|
|
837
|
+
const progress = createProgress(!!opts.verbose);
|
|
786
838
|
const sync = new RemoteSync(centralDb, remotePath);
|
|
787
|
-
const
|
|
839
|
+
const runSync = () => sync.sync({
|
|
788
840
|
auto: opts.auto,
|
|
789
841
|
strategy: opts.newerWins ? "newer-wins" : "skip-and-flag",
|
|
842
|
+
onProgress: progress.noop ? undefined : progress.emit.bind(progress),
|
|
790
843
|
});
|
|
844
|
+
// Auto mode + verbose mode both bypass the heartbeat. Auto mode is
|
|
845
|
+
// for non-interactive runs (no spinner). Verbose streams its own output.
|
|
846
|
+
const result = opts.auto || opts.verbose
|
|
847
|
+
? await runSync()
|
|
848
|
+
: await withHeartbeat("Syncing with remote", runSync);
|
|
791
849
|
sync.closeRemote();
|
|
792
850
|
if (!opts.auto || result.conflicts.length > 0 || result.errors.length > 0) {
|
|
793
851
|
const pp = result.projectsPushed || 0;
|
|
@@ -865,6 +923,14 @@ setupCmd
|
|
|
865
923
|
const { runDreamSetup } = await import("./lib/setup.js");
|
|
866
924
|
await runDreamSetup({ directory: process.cwd() });
|
|
867
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
|
+
});
|
|
868
934
|
// `gnosys setup ides` — configure IDE / MCP integrations standalone
|
|
869
935
|
setupCmd
|
|
870
936
|
.command("ides")
|
|
@@ -1730,6 +1796,7 @@ program
|
|
|
1730
1796
|
}
|
|
1731
1797
|
const tagRegistry = new GnosysTagRegistry(writeTarget.store.getStorePath());
|
|
1732
1798
|
await tagRegistry.load();
|
|
1799
|
+
const { GnosysIngestion } = await import("./lib/ingest.js");
|
|
1733
1800
|
const ingestion = new GnosysIngestion(writeTarget.store, tagRegistry);
|
|
1734
1801
|
if (!ingestion.isLLMAvailable) {
|
|
1735
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");
|
|
@@ -2279,6 +2346,7 @@ program
|
|
|
2279
2346
|
process.exit(1);
|
|
2280
2347
|
}
|
|
2281
2348
|
// Show what we'll scan
|
|
2349
|
+
const { bootstrap, discoverFiles } = await import("./lib/bootstrap.js");
|
|
2282
2350
|
const files = await discoverFiles(sourceDir, opts.pattern);
|
|
2283
2351
|
console.log(`Found ${files.length} files in ${sourceDir}\n`);
|
|
2284
2352
|
if (files.length === 0) {
|
|
@@ -2366,6 +2434,8 @@ const importCmd = program
|
|
|
2366
2434
|
}
|
|
2367
2435
|
const tagRegistry = new GnosysTagRegistry(writeTarget.store.getStorePath());
|
|
2368
2436
|
await tagRegistry.load();
|
|
2437
|
+
const { GnosysIngestion } = await import("./lib/ingest.js");
|
|
2438
|
+
const { performImport, formatImportSummary } = await import("./lib/import.js");
|
|
2369
2439
|
const ingestion = new GnosysIngestion(writeTarget.store, tagRegistry);
|
|
2370
2440
|
const format = opts.format;
|
|
2371
2441
|
const mode = opts.mode;
|
|
@@ -2485,6 +2555,8 @@ program
|
|
|
2485
2555
|
for (const s of stores) {
|
|
2486
2556
|
await search.addStoreMemories(s.store, s.label);
|
|
2487
2557
|
}
|
|
2558
|
+
const { GnosysEmbeddings } = await import("./lib/embeddings.js");
|
|
2559
|
+
const { GnosysHybridSearch } = await import("./lib/hybridSearch.js");
|
|
2488
2560
|
const embeddings = new GnosysEmbeddings(storePath);
|
|
2489
2561
|
const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
|
|
2490
2562
|
console.log("Building semantic embeddings (downloading model on first run)...");
|
|
@@ -2561,6 +2633,8 @@ program
|
|
|
2561
2633
|
for (const s of stores) {
|
|
2562
2634
|
await search.addStoreMemories(s.store, s.label);
|
|
2563
2635
|
}
|
|
2636
|
+
const { GnosysEmbeddings } = await import("./lib/embeddings.js");
|
|
2637
|
+
const { GnosysHybridSearch } = await import("./lib/hybridSearch.js");
|
|
2564
2638
|
const embeddings = new GnosysEmbeddings(storePath);
|
|
2565
2639
|
const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
|
|
2566
2640
|
const mode = opts.mode;
|
|
@@ -2608,6 +2682,8 @@ program
|
|
|
2608
2682
|
for (const s of stores) {
|
|
2609
2683
|
await search.addStoreMemories(s.store, s.label);
|
|
2610
2684
|
}
|
|
2685
|
+
const { GnosysEmbeddings } = await import("./lib/embeddings.js");
|
|
2686
|
+
const { GnosysHybridSearch } = await import("./lib/hybridSearch.js");
|
|
2611
2687
|
const embeddings = new GnosysEmbeddings(storePath);
|
|
2612
2688
|
const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
|
|
2613
2689
|
const results = await hybridSearch.hybridSearch(query, parseInt(opts.limit), "semantic");
|
|
@@ -2656,11 +2732,30 @@ program
|
|
|
2656
2732
|
for (const s of stores) {
|
|
2657
2733
|
await search.addStoreMemories(s.store, s.label);
|
|
2658
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");
|
|
2659
2738
|
const embeddings = new GnosysEmbeddings(storePath);
|
|
2660
2739
|
const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
|
|
2661
2740
|
const ask = new GnosysAsk(hybridSearch, cliConfig, resolver, storePath);
|
|
2662
2741
|
if (!ask.isLLMAvailable) {
|
|
2663
|
-
|
|
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
|
+
}
|
|
2664
2759
|
process.exit(1);
|
|
2665
2760
|
}
|
|
2666
2761
|
// If --federated, pre-retrieve from central DB and inject as context
|
|
@@ -3007,41 +3102,9 @@ program
|
|
|
3007
3102
|
console.log("");
|
|
3008
3103
|
console.log(formatGraphStats(stats));
|
|
3009
3104
|
});
|
|
3010
|
-
//
|
|
3011
|
-
//
|
|
3012
|
-
//
|
|
3013
|
-
program
|
|
3014
|
-
.command("dashboard")
|
|
3015
|
-
.description("(alias) Show system health — equivalent to 'gnosys status --system'")
|
|
3016
|
-
.option("--json", "Output as JSON instead of pretty table")
|
|
3017
|
-
.action(async (opts) => {
|
|
3018
|
-
const { collectDashboardData, formatDashboard, formatDashboardJSON } = await import("./lib/dashboard.js");
|
|
3019
|
-
const resolver = await getResolver();
|
|
3020
|
-
const stores = resolver.getStores();
|
|
3021
|
-
if (stores.length === 0) {
|
|
3022
|
-
console.error("No Gnosys stores found. Run gnosys init first.");
|
|
3023
|
-
process.exit(1);
|
|
3024
|
-
}
|
|
3025
|
-
const cfg = await loadConfig(stores[0].path);
|
|
3026
|
-
// v5.1: Use central DB for dashboard stats
|
|
3027
|
-
let dashDb;
|
|
3028
|
-
try {
|
|
3029
|
-
const db = GnosysDB.openCentral();
|
|
3030
|
-
if (db.isAvailable() && db.isMigrated()) {
|
|
3031
|
-
dashDb = db;
|
|
3032
|
-
}
|
|
3033
|
-
}
|
|
3034
|
-
catch {
|
|
3035
|
-
// Central DB not available — legacy dashboard only
|
|
3036
|
-
}
|
|
3037
|
-
const data = await collectDashboardData(resolver, cfg, pkg.version, dashDb);
|
|
3038
|
-
if (opts.json) {
|
|
3039
|
-
console.log(formatDashboardJSON(data));
|
|
3040
|
-
}
|
|
3041
|
-
else {
|
|
3042
|
-
console.log(formatDashboard(data));
|
|
3043
|
-
}
|
|
3044
|
-
});
|
|
3105
|
+
// `gnosys dashboard` was removed in v5.7.1.
|
|
3106
|
+
// Use `gnosys status --system` instead. Hard removal — commander will emit
|
|
3107
|
+
// the standard "unknown command" error.
|
|
3045
3108
|
// ─── gnosys maintain ─────────────────────────────────────────────────────
|
|
3046
3109
|
program
|
|
3047
3110
|
.command("maintain")
|
|
@@ -3125,12 +3188,19 @@ program
|
|
|
3125
3188
|
}
|
|
3126
3189
|
});
|
|
3127
3190
|
// NOTE: gnosys migrate is defined below (near the end) with --to-central support
|
|
3128
|
-
// ─── gnosys upgrade
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3191
|
+
// ─── gnosys upgrade + gnosys setup sync-projects ──────────────────────
|
|
3192
|
+
//
|
|
3193
|
+
// v5.7.1 (#15) split this command:
|
|
3194
|
+
//
|
|
3195
|
+
// gnosys upgrade — upgrade the gnosys CLI/MCP itself
|
|
3196
|
+
// (npm install + restart signal to MCPs)
|
|
3197
|
+
// gnosys setup sync-projects — what the old `gnosys upgrade` used to do
|
|
3198
|
+
// (re-init project identities, agent rules,
|
|
3199
|
+
// central DB stamp, portfolio dashboard)
|
|
3200
|
+
//
|
|
3201
|
+
// The body of the legacy command is preserved verbatim below as
|
|
3202
|
+
// `syncProjectsAction`, called from the new `setup sync-projects` command.
|
|
3203
|
+
async function syncProjectsAction(opts) {
|
|
3134
3204
|
const currentVersion = pkg.version;
|
|
3135
3205
|
console.log(`Gnosys v${currentVersion} — upgrading registered projects...\n`);
|
|
3136
3206
|
// 1. Read registered projects from file registry AND central DB
|
|
@@ -3349,6 +3419,76 @@ program
|
|
|
3349
3419
|
console.log(`\n Could not regenerate portfolio dashboard`);
|
|
3350
3420
|
}
|
|
3351
3421
|
}
|
|
3422
|
+
}
|
|
3423
|
+
// `gnosys setup sync-projects` — re-init project identities + agent rules.
|
|
3424
|
+
// (This is what `gnosys upgrade` used to do; renamed in v5.7.1.)
|
|
3425
|
+
setupCmd
|
|
3426
|
+
.command("sync-projects")
|
|
3427
|
+
.description("Re-initialize all registered projects after upgrading gnosys: refresh agent rules, project registry, central DB stamp, and portfolio dashboard.")
|
|
3428
|
+
.option("--skip-dashboard", "Skip regenerating the portfolio dashboard")
|
|
3429
|
+
.action(syncProjectsAction);
|
|
3430
|
+
// `gnosys upgrade` — upgrade the gnosys CLI/MCP itself, then prompt the
|
|
3431
|
+
// user to run sync-projects. Writes ~/.gnosys/last-upgrade-at so running
|
|
3432
|
+
// MCP servers exit cleanly and the host respawns them against the new
|
|
3433
|
+
// global binary (see src/lib/upgrade.ts).
|
|
3434
|
+
program
|
|
3435
|
+
.command("upgrade")
|
|
3436
|
+
.description("Upgrade gnosys itself (npm install -g gnosys@latest) and signal running MCP servers to restart. After upgrading, suggests running 'gnosys setup sync-projects'.")
|
|
3437
|
+
.option("--yes", "Skip the post-upgrade sync-projects prompt and exit")
|
|
3438
|
+
.option("--no-sync", "Don't suggest running sync-projects afterward")
|
|
3439
|
+
.action(async (opts) => {
|
|
3440
|
+
const currentVersion = pkg.version;
|
|
3441
|
+
console.log(`Gnosys CLI: currently v${currentVersion}`);
|
|
3442
|
+
console.log(`Running: npm install -g gnosys@latest ...`);
|
|
3443
|
+
const { execSync } = await import("child_process");
|
|
3444
|
+
try {
|
|
3445
|
+
execSync("npm install -g gnosys@latest", { stdio: "inherit" });
|
|
3446
|
+
}
|
|
3447
|
+
catch (err) {
|
|
3448
|
+
console.error(`\nUpgrade failed: ${err instanceof Error ? err.message : err}`);
|
|
3449
|
+
console.error(`Try running 'npm install -g gnosys@latest' manually.`);
|
|
3450
|
+
process.exit(1);
|
|
3451
|
+
}
|
|
3452
|
+
// Read the newly-installed version (best-effort — we may still be the
|
|
3453
|
+
// old binary in-process; this is purely informational).
|
|
3454
|
+
let newVersion = "(see npm output)";
|
|
3455
|
+
try {
|
|
3456
|
+
const out = execSync("npm ls -g gnosys --depth=0 --json", { encoding: "utf8" });
|
|
3457
|
+
const parsed = JSON.parse(out);
|
|
3458
|
+
newVersion = parsed?.dependencies?.gnosys?.version || newVersion;
|
|
3459
|
+
}
|
|
3460
|
+
catch {
|
|
3461
|
+
// Best-effort lookup only.
|
|
3462
|
+
}
|
|
3463
|
+
// Write the marker so any running MCP servers exit and respawn.
|
|
3464
|
+
const { writeUpgradeMarker } = await import("./lib/upgrade.js");
|
|
3465
|
+
try {
|
|
3466
|
+
writeUpgradeMarker(typeof newVersion === "string" && newVersion !== "(see npm output)"
|
|
3467
|
+
? newVersion
|
|
3468
|
+
: currentVersion);
|
|
3469
|
+
console.log(`\n✓ Upgrade marker written: ~/.gnosys/last-upgrade-at`);
|
|
3470
|
+
console.log(` Any running MCP servers will detect this within 10s and restart cleanly.`);
|
|
3471
|
+
console.log(` (Your MCP client — Claude Code, Cursor, VS Code — will auto-respawn.)`);
|
|
3472
|
+
}
|
|
3473
|
+
catch (err) {
|
|
3474
|
+
console.error(`\nCould not write upgrade marker: ${err instanceof Error ? err.message : err}`);
|
|
3475
|
+
console.error(`Running MCP servers will need to be restarted manually.`);
|
|
3476
|
+
}
|
|
3477
|
+
if (opts.sync === false || opts.yes) {
|
|
3478
|
+
console.log(`\nDone. Run 'gnosys setup sync-projects' when you're ready to refresh registered projects.`);
|
|
3479
|
+
return;
|
|
3480
|
+
}
|
|
3481
|
+
// Prompt for sync-projects.
|
|
3482
|
+
const readline = await import("readline");
|
|
3483
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3484
|
+
const answer = await new Promise((resolve) => rl.question(`\nRun 'gnosys setup sync-projects' now to refresh registered projects? [Y/n] `, resolve));
|
|
3485
|
+
rl.close();
|
|
3486
|
+
if (answer.trim().toLowerCase() === "n" || answer.trim().toLowerCase() === "no") {
|
|
3487
|
+
console.log(`Done. You can run 'gnosys setup sync-projects' later.`);
|
|
3488
|
+
return;
|
|
3489
|
+
}
|
|
3490
|
+
console.log(``);
|
|
3491
|
+
await syncProjectsAction({});
|
|
3352
3492
|
});
|
|
3353
3493
|
// ─── gnosys doctor ──────────────────────────────────────────────────────
|
|
3354
3494
|
program
|
|
@@ -3494,6 +3634,7 @@ program
|
|
|
3494
3634
|
// Check embeddings
|
|
3495
3635
|
if (stores.length > 0) {
|
|
3496
3636
|
console.log("Embeddings:");
|
|
3637
|
+
const { GnosysEmbeddings } = await import("./lib/embeddings.js");
|
|
3497
3638
|
const embeddings = new GnosysEmbeddings(stores[0].path);
|
|
3498
3639
|
try {
|
|
3499
3640
|
const stats = embeddings.getStats();
|
|
@@ -4299,6 +4440,7 @@ program
|
|
|
4299
4440
|
console.error("No writable store found. Run 'gnosys init' first.");
|
|
4300
4441
|
process.exit(1);
|
|
4301
4442
|
}
|
|
4443
|
+
const { migrate, formatMigrationReport } = await import("./lib/migrate.js");
|
|
4302
4444
|
const stats = await migrate(writeTarget.store.getStorePath(), { verbose: opts.verbose });
|
|
4303
4445
|
console.log(formatMigrationReport(stats));
|
|
4304
4446
|
return;
|
|
@@ -4858,80 +5000,70 @@ program
|
|
|
4858
5000
|
centralDb?.close();
|
|
4859
5001
|
}
|
|
4860
5002
|
});
|
|
4861
|
-
//
|
|
5003
|
+
// `gnosys portfolio` was removed in v5.7.1.
|
|
5004
|
+
// Use `gnosys status --projects` (formerly --global) for the projects
|
|
5005
|
+
// overview, or `gnosys status --web` for the HTML dashboard, or
|
|
5006
|
+
// `gnosys status --projects --output file.html` to write to disk.
|
|
5007
|
+
// ─── gnosys status ──────────────────────────────────────────────────────
|
|
5008
|
+
// v5.7.1 (#11): the catch-all status command. Section flags select what to
|
|
5009
|
+
// show; output flags control format. Default (no flag) is the current
|
|
5010
|
+
// project. `dashboard` and `portfolio` were removed in v5.7.1 — their
|
|
5011
|
+
// content lives under `--system` and `--projects` respectively.
|
|
4862
5012
|
program
|
|
4863
|
-
.command("
|
|
4864
|
-
.description("
|
|
4865
|
-
.option("-
|
|
4866
|
-
.option("--
|
|
5013
|
+
.command("status")
|
|
5014
|
+
.description("Show status. Sections: --projects (all projects) · --remote (sync) · --system (memory/LLM health) · default: current project. Output: --web · --json. Note: 'gnosys dashboard' and 'gnosys portfolio' were removed in v5.7.1 — use 'gnosys status --system' and 'gnosys status --projects' instead.")
|
|
5015
|
+
.option("-d, --directory <dir>", "Project directory (auto-detects if omitted)")
|
|
5016
|
+
.option("-p, --project <id>", "Project ID")
|
|
5017
|
+
.option("-g, --global", "(deprecated alias for --projects)")
|
|
5018
|
+
.option("--projects", "Show all projects portfolio (replaces the old 'gnosys portfolio')")
|
|
5019
|
+
.option("-r, --remote", "Show remote sync status (alias for 'gnosys setup remote status')")
|
|
5020
|
+
.option("-w, --web", "Open the HTML dashboard in the browser")
|
|
5021
|
+
.option("-s, --system", "Show system health (memory count, LLM connectivity, embeddings, archive)")
|
|
4867
5022
|
.option("--json", "Output as JSON")
|
|
4868
5023
|
.action(async (opts) => {
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
const useJson = opts.json || (opts.output?.endsWith(".json") ?? false);
|
|
4881
|
-
if (useJson) {
|
|
4882
|
-
const json = JSON.stringify(report, null, 2);
|
|
4883
|
-
if (opts.output) {
|
|
4884
|
-
const { writeFileSync } = await import("fs");
|
|
4885
|
-
writeFileSync(opts.output, json, "utf-8");
|
|
4886
|
-
console.log(`Portfolio written to ${opts.output}`);
|
|
5024
|
+
// v5.7.1: --projects supersedes --global (kept as alias).
|
|
5025
|
+
if (opts.projects)
|
|
5026
|
+
opts.global = true;
|
|
5027
|
+
// v5.7.1: --remote — dispatch to RemoteSync.getStatus()
|
|
5028
|
+
if (opts.remote) {
|
|
5029
|
+
let remoteCentralDb = null;
|
|
5030
|
+
try {
|
|
5031
|
+
remoteCentralDb = GnosysDB.openLocal();
|
|
5032
|
+
if (!remoteCentralDb.isAvailable()) {
|
|
5033
|
+
console.error("Central DB not available.");
|
|
5034
|
+
process.exit(1);
|
|
4887
5035
|
}
|
|
4888
|
-
|
|
4889
|
-
|
|
5036
|
+
const remotePath = remoteCentralDb.getMeta("remote_path");
|
|
5037
|
+
if (!remotePath) {
|
|
5038
|
+
if (opts.json) {
|
|
5039
|
+
console.log(JSON.stringify({ configured: false, message: "Remote not configured. Run 'gnosys setup remote'." }, null, 2));
|
|
5040
|
+
}
|
|
5041
|
+
else {
|
|
5042
|
+
console.log("Remote sync: not configured. Run 'gnosys setup remote' to set up multi-machine sync.");
|
|
5043
|
+
}
|
|
5044
|
+
return;
|
|
4890
5045
|
}
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
const
|
|
4895
|
-
|
|
4896
|
-
if (opts.
|
|
4897
|
-
|
|
4898
|
-
writeFileSync(opts.output, html, "utf-8");
|
|
4899
|
-
console.log(`Portfolio dashboard written to ${opts.output}`);
|
|
5046
|
+
const { RemoteSync, formatStatus } = await import("./lib/remote.js");
|
|
5047
|
+
const { withHeartbeat } = await import("./lib/heartbeat.js");
|
|
5048
|
+
const sync = new RemoteSync(remoteCentralDb, remotePath);
|
|
5049
|
+
const status = await withHeartbeat("Checking remote sync status", () => sync.getStatus());
|
|
5050
|
+
sync.closeRemote();
|
|
5051
|
+
if (opts.json) {
|
|
5052
|
+
console.log(JSON.stringify(status, null, 2));
|
|
4900
5053
|
}
|
|
4901
5054
|
else {
|
|
4902
|
-
console.log(
|
|
5055
|
+
console.log(formatStatus(status));
|
|
4903
5056
|
}
|
|
4904
5057
|
return;
|
|
4905
5058
|
}
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
writeFileSync(opts.output, markdown, "utf-8");
|
|
4910
|
-
console.log(`Portfolio dashboard written to ${opts.output}`);
|
|
5059
|
+
catch (err) {
|
|
5060
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
5061
|
+
process.exit(1);
|
|
4911
5062
|
}
|
|
4912
|
-
|
|
4913
|
-
|
|
5063
|
+
finally {
|
|
5064
|
+
remoteCentralDb?.close();
|
|
4914
5065
|
}
|
|
4915
5066
|
}
|
|
4916
|
-
catch (err) {
|
|
4917
|
-
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
4918
|
-
process.exit(1);
|
|
4919
|
-
}
|
|
4920
|
-
finally {
|
|
4921
|
-
centralDb?.close();
|
|
4922
|
-
}
|
|
4923
|
-
});
|
|
4924
|
-
// ─── gnosys status ──────────────────────────────────────────────────────
|
|
4925
|
-
program
|
|
4926
|
-
.command("status")
|
|
4927
|
-
.description("Show project status (--global: all projects, --web: HTML dashboard, --system: memory/LLM health)")
|
|
4928
|
-
.option("-d, --directory <dir>", "Project directory (auto-detects if omitted)")
|
|
4929
|
-
.option("-p, --project <id>", "Project ID")
|
|
4930
|
-
.option("-g, --global", "Show all projects")
|
|
4931
|
-
.option("-w, --web", "Open the HTML dashboard in the browser")
|
|
4932
|
-
.option("-s, --system", "Show system health (memory count, LLM connectivity, embeddings, archive)")
|
|
4933
|
-
.option("--json", "Output as JSON")
|
|
4934
|
-
.action(async (opts) => {
|
|
4935
5067
|
// --system delegates to the dashboard formatter (formerly `gnosys dashboard`).
|
|
4936
5068
|
if (opts.system) {
|
|
4937
5069
|
const { collectDashboardData, formatDashboard, formatDashboardJSON } = await import("./lib/dashboard.js");
|