gnosys 5.4.1 → 5.4.3

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 CHANGED
@@ -269,6 +269,40 @@ cd /path/to/project-b && gnosys init
269
269
 
270
270
  ---
271
271
 
272
+ ## Setup Wizards
273
+
274
+ `gnosys setup` runs the full interactive wizard. To configure just one piece without walking the whole thing, use one of these subcommands:
275
+
276
+ ```bash
277
+ gnosys setup # full wizard (provider, models, IDE, remote sync, dream)
278
+ gnosys setup models # LLM provider + model only
279
+ gnosys setup remote # multi-machine sync (NAS/shared drive)
280
+ gnosys setup dream # Dream Mode designation, schedule, sub-tasks
281
+ ```
282
+
283
+ ### Dream Mode setup (v5.4.2+)
284
+
285
+ Dream Mode is the idle-time consolidation engine — confidence decay, summary generation, self-critique, relationship discovery. With multi-machine sync (v5.3.0+), running dream cycles from every machine wastes work and fights for SQLite write locks. v5.4.2 introduces a **single designated machine** model:
286
+
287
+ - Run `gnosys setup dream` on the machine you want to host dream cycles.
288
+ - The wizard validates your provider/model with a live API probe before saving.
289
+ - Other machines stay quiet — they see the designation in the central DB and skip the scheduler.
290
+ - `gnosys dream log` shows recent runs; `gnosys dashboard` has a `DREAM HEALTH` section with last-run timestamp, designated machine, and consecutive-failure counter.
291
+ - If the designated machine's LLM provider becomes unreachable, you'll see warnings at three layers: in audit log entries (`dream_provider_unreachable`), as stderr at MCP startup, and as a desktop notification after 3 consecutive failures.
292
+
293
+ ### Removed in v5.4.2
294
+
295
+ The following commands were removed in favor of the canonical `gnosys setup <thing>` form:
296
+
297
+ | Removed | Use instead |
298
+ |---|---|
299
+ | `gnosys models` | `gnosys setup models` |
300
+ | `gnosys remote configure` | `gnosys setup remote` |
301
+
302
+ `gnosys remote push|pull|sync|status` remain unchanged — only `configure` moved.
303
+
304
+ ---
305
+
272
306
  ### Manual config (if you prefer)
273
307
 
274
308
  If you'd rather edit configs by hand, here's where each client looks for MCP servers.
package/dist/cli.js CHANGED
@@ -65,6 +65,49 @@ const __dirname = path.dirname(__filename);
65
65
  const pkgPath = path.resolve(__dirname, "..", "package.json");
66
66
  const pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
67
67
  const program = new Command();
68
+ /**
69
+ * v5.4.3 upgrade nudge — runs once per CLI invocation. If the installed
70
+ * version differs from the last-seen one stored in central DB meta, print
71
+ * a one-line stderr nudge so the user knows to run `gnosys upgrade`.
72
+ *
73
+ * Why stderr: doesn't pollute stdout consumers (agents parsing JSON, scripts
74
+ * piping the output). Visible to humans, invisible to most parsers.
75
+ *
76
+ * Why central DB meta: survives across CLI invocations and works whether
77
+ * gnosys is installed globally, locally, or via npx. Single source of truth
78
+ * for "what was the last version this user saw."
79
+ *
80
+ * Skipped when:
81
+ * - GNOSYS_SKIP_UPGRADE_NUDGE=1
82
+ * - Running `gnosys upgrade` itself (would be redundant)
83
+ * - Central DB unavailable (graceful — never blocks the actual command)
84
+ */
85
+ function maybePrintUpgradeNudge() {
86
+ if (process.env.GNOSYS_SKIP_UPGRADE_NUDGE === "1")
87
+ return;
88
+ // Avoid printing the nudge when the user is already running upgrade.
89
+ if (process.argv[2] === "upgrade")
90
+ return;
91
+ try {
92
+ const db = GnosysDB.openLocal();
93
+ if (!db.isAvailable() || !db.isMigrated()) {
94
+ db.close();
95
+ return;
96
+ }
97
+ const seen = db.getMeta("last_seen_version");
98
+ const current = pkg.version;
99
+ if (seen !== current) {
100
+ const prefix = seen ? `upgraded to v${current} (from v${seen})` : `installed v${current}`;
101
+ process.stderr.write(`gnosys: ${prefix}. Run 'gnosys upgrade' to sync registered projects.\n`);
102
+ db.setMeta("last_seen_version", current);
103
+ }
104
+ db.close();
105
+ }
106
+ catch {
107
+ // Never block actual commands — silently skip on any error.
108
+ }
109
+ }
110
+ maybePrintUpgradeNudge();
68
111
  async function getResolver() {
69
112
  const resolver = new GnosysResolver();
70
113
  await resolver.resolve();
@@ -503,7 +546,7 @@ setupCmd
503
546
  // `gnosys setup remote` — configure remote sync (alias for `gnosys remote configure`)
504
547
  setupCmd
505
548
  .command("remote")
506
- .description("Configure multi-machine sync (alias for 'gnosys remote configure')")
549
+ .description("Configure multi-machine sync (NAS/shared drive)")
507
550
  .option("--path <path>", "Set remote path directly (non-interactive)")
508
551
  .action(async (opts) => {
509
552
  const { GnosysDB } = await import("./lib/db.js");
@@ -528,17 +571,18 @@ setupCmd
528
571
  db.close();
529
572
  }
530
573
  });
531
- // ─── gnosys models (top-level shortcut) ─────────────────────────────────
532
- program
533
- .command("models")
534
- .description("Quick model operationslist available, refresh cache, or set the default")
535
- .option("--list", "List available models for the current provider")
536
- .option("--refresh", "Refresh model list from OpenRouter (clears the cache)")
537
- .option("--set <model>", "Set the default model for the current provider")
538
- .action(async (opts) => {
539
- const { runModelsCommand } = await import("./lib/setup.js");
540
- await runModelsCommand(opts);
574
+ // `gnosys setup dream` — configure dream mode (designation, provider, schedule)
575
+ setupCmd
576
+ .command("dream")
577
+ .description("Configure Dream Modedesignate this machine, pick provider/model, set schedule")
578
+ .action(async () => {
579
+ const { runDreamSetup } = await import("./lib/setup.js");
580
+ await runDreamSetup({ directory: process.cwd() });
541
581
  });
582
+ // v5.4.2 removal: `gnosys models` (top-level shortcut) was removed in favor
583
+ // of the canonical `gnosys setup models` form. The implementation function
584
+ // runModelsCommand() in setup.ts is no longer wired but kept for now in case
585
+ // we need to revive a top-level shortcut later.
542
586
  // ─── gnosys init ─────────────────────────────────────────────────────────
543
587
  program
544
588
  .command("init [ide]")
@@ -2804,39 +2848,10 @@ remoteCmd
2804
2848
  centralDb?.close();
2805
2849
  }
2806
2850
  });
2807
- remoteCmd
2808
- .command("configure")
2809
- .description("Configure or change the remote sync location (interactive wizard)")
2810
- .option("--path <path>", "Set remote path non-interactively (skips wizard)")
2811
- .option("--migrate", "Copy current local DB to remote on first setup (with --path)")
2812
- .action(async (opts) => {
2813
- let centralDb = null;
2814
- try {
2815
- centralDb = GnosysDB.openLocal();
2816
- if (!centralDb.isAvailable()) {
2817
- console.error("Central DB not available.");
2818
- process.exit(1);
2819
- }
2820
- const { runConfigureWizard, configureFromPath } = await import("./lib/remoteWizard.js");
2821
- if (opts.path) {
2822
- // Non-interactive mode
2823
- const ok = await configureFromPath(centralDb, opts.path, { migrate: opts.migrate });
2824
- process.exit(ok ? 0 : 1);
2825
- }
2826
- else {
2827
- // Interactive wizard
2828
- const ok = await runConfigureWizard(centralDb);
2829
- process.exit(ok ? 0 : 1);
2830
- }
2831
- }
2832
- catch (err) {
2833
- console.error(`Error: ${err instanceof Error ? err.message : err}`);
2834
- process.exit(1);
2835
- }
2836
- finally {
2837
- centralDb?.close();
2838
- }
2839
- });
2851
+ // v5.4.2 removal: `gnosys remote configure` was removed in favor of the
2852
+ // canonical `gnosys setup remote` form (which calls the same wizard
2853
+ // helpers from lib/remoteWizard.ts). Sync operations like push/pull/sync
2854
+ // remain under the `remote` parent.
2840
2855
  // ─── gnosys upgrade ─────────────────────────────────────────────────────
2841
2856
  program
2842
2857
  .command("upgrade")
@@ -3328,10 +3343,12 @@ program
3328
3343
  }
3329
3344
  console.log();
3330
3345
  });
3331
- // ─── gnosys dream ────────────────────────────────────────────────────────
3332
- program
3346
+ // ─── gnosys dream (parent command) ───────────────────────────────────────
3347
+ const dreamCmd = program
3333
3348
  .command("dream")
3334
- .description("Run a Dream Mode cycle — idle-time consolidation (decay, summaries, self-critique, relationships)")
3349
+ .description("Dream Mode — idle-time consolidation (run a cycle, view log)");
3350
+ // Bare `gnosys dream` runs a cycle now (preserves v5.4.1 behavior).
3351
+ dreamCmd
3335
3352
  .option("--max-runtime <minutes>", "Max runtime in minutes (default: 30)")
3336
3353
  .option("--no-critique", "Skip self-critique phase")
3337
3354
  .option("--no-summaries", "Skip summary generation")
@@ -3378,6 +3395,73 @@ program
3378
3395
  }
3379
3396
  db.close();
3380
3397
  });
3398
+ // `gnosys dream log` — view recent dream runs from audit_log
3399
+ dreamCmd
3400
+ .command("log")
3401
+ .description("Show recent dream runs from the audit log (default: last 20)")
3402
+ .option("--last <N>", "Number of most recent runs to show", "20")
3403
+ .option("--since <YYYY-MM-DD>", "Only runs since this date")
3404
+ .option("--failures-only", "Only runs with errors or unreachable provider")
3405
+ .option("--json", "Output raw audit rows as JSON")
3406
+ .action(async function (opts) {
3407
+ let centralDb = null;
3408
+ try {
3409
+ centralDb = GnosysDB.openCentral();
3410
+ if (!centralDb.isAvailable()) {
3411
+ console.error("Central DB not available.");
3412
+ process.exit(1);
3413
+ }
3414
+ const limit = Math.max(1, parseInt(opts.last) || 20);
3415
+ const sinceIso = opts.since ? `${opts.since}T00:00:00Z` : undefined;
3416
+ const runs = centralDb.getRecentDreamRuns(limit, {
3417
+ failuresOnly: !!opts.failuresOnly,
3418
+ sinceIso,
3419
+ });
3420
+ // Commander v13 hoists `--json` to the parent when both parent and
3421
+ // subcommand define it. Check the parent (dreamCmd) too so users can
3422
+ // type `gnosys dream log --json` and get JSON output.
3423
+ const wantJson = !!opts.json || !!(this.parent?.opts().json);
3424
+ // JSON path always emits a structured response — including empty runs.
3425
+ if (wantJson) {
3426
+ console.log(JSON.stringify({ count: runs.length, runs }, null, 2));
3427
+ return;
3428
+ }
3429
+ if (runs.length === 0) {
3430
+ console.log("No dream runs recorded.");
3431
+ return;
3432
+ }
3433
+ const DIM = "\x1b[2m";
3434
+ const RESET = "\x1b[0m";
3435
+ const RED = "\x1b[31m";
3436
+ const GREEN = "\x1b[32m";
3437
+ console.log(`${runs.length} dream run(s):\n`);
3438
+ for (const r of runs) {
3439
+ const d = r.details;
3440
+ const dur = r.durationMs != null ? `${(r.durationMs / 1000).toFixed(1)}s` : "—";
3441
+ const summaries = Number(d.summariesGenerated || 0);
3442
+ const decays = Number(d.decayUpdated || 0);
3443
+ const reviews = Number(d.reviewSuggestions || 0);
3444
+ const rels = Number(d.relationshipsDiscovered || 0);
3445
+ const errors = Number(d.errors || 0);
3446
+ const unreachable = Boolean(d.providerUnreachable);
3447
+ const status = unreachable
3448
+ ? `${RED}provider unreachable${RESET}`
3449
+ : errors > 0
3450
+ ? `${RED}${errors} error(s)${RESET}`
3451
+ : summaries + decays + rels > 0
3452
+ ? `${GREEN}did work${RESET}`
3453
+ : `${DIM}no LLM work${RESET}`;
3454
+ console.log(` ${r.completed} ${DIM}(${dur})${RESET} ${status}`);
3455
+ console.log(` decays=${decays} summaries=${summaries} reviews=${reviews} relations=${rels}`);
3456
+ if (d.provider) {
3457
+ console.log(` ${DIM}provider=${d.provider}${d.model ? "/" + d.model : ""}${RESET}`);
3458
+ }
3459
+ }
3460
+ }
3461
+ finally {
3462
+ centralDb?.close();
3463
+ }
3464
+ });
3381
3465
  // ─── gnosys export ───────────────────────────────────────────────────────
3382
3466
  program
3383
3467
  .command("export")