gnosys 5.4.2 → 5.5.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 CHANGED
@@ -52,7 +52,7 @@ Gnosys takes a different approach: the central brain is a single SQLite database
52
52
  - **Reflection API** — `gnosys.reflect(outcome)` updates confidence and consolidates memories based on real-world outcomes.
53
53
  - **Bulk import** — CSV, JSON, JSONL. Import entire datasets in seconds.
54
54
  - **Obsidian-native** — `gnosys export` generates a full vault with YAML frontmatter, `[[wikilinks]]`, summaries, and graph data.
55
- - **Multi-machine sync (v5.3.0)** — share your `gnosys.db` across machines via NAS or shared drive. Local cache for speed, remote source of truth for consistency. Built-in conflict detection with skip-and-flag resolution. Run `gnosys remote configure` to set up.
55
+ - **Multi-machine sync** — share your `gnosys.db` across machines via NAS or shared drive. Remote NAS is the canonical source of truth; local DB is an offline-resilience cache. Built-in conflict detection with skip-and-flag resolution. Run `gnosys setup remote` to set up.
56
56
  - **MCP-compatible** — also runs as a full MCP server that drops into Cursor, Claude Desktop (Chat / Cowork / Code), Claude Code, Codex, Gemini CLI, Antigravity, or any MCP client.
57
57
  - **Zero infrastructure** — no external databases, no Docker (unless you want it), no cloud services. Just `npm install`.
58
58
 
@@ -461,19 +461,19 @@ All memories live in a single `~/.gnosys/gnosys.db` with `project_id` and `scope
461
461
 
462
462
  ### Multi-Machine Sync
463
463
 
464
- Gnosys v5.3.0 supports running across multiple machines with a shared database on a NAS or network share.
464
+ Gnosys supports running across multiple machines with a shared database on a NAS or network share.
465
465
 
466
466
  **How it works:**
467
- - Local DB at `~/.gnosys/gnosys.db` is your fast working cache
468
467
  - Remote DB on a network share (e.g. `/Volumes/nas/gnosys/`) is the canonical source of truth
469
- - Reads always hit local for speed
470
- - Writes go to local first, then sync to remote
471
- - Per-memory `modified` timestamps detect conflicts
468
+ - Local DB at `~/.gnosys/gnosys.db` is an offline-resilience cache, not a performance optimization
469
+ - Reads hit remote when reachable; fall back to local cache when remote is offline
470
+ - Writes go to remote first; queue locally and auto-flush when offline
471
+ - Per-memory `modified` timestamps detect conflicts; ULID memory IDs prevent collisions across concurrent writers
472
472
  - Skip-and-flag is the safe default; `--newer-wins` for unattended sync
473
473
 
474
474
  **Setup:**
475
475
  ```bash
476
- gnosys remote configure
476
+ gnosys setup remote
477
477
  # interactive: validates path, tests SQLite locking, checks latency
478
478
  ```
479
479
 
@@ -616,7 +616,7 @@ All commands support `--json` for programmatic output. See the [User Guide](http
616
616
 
617
617
  **Web knowledge base:** `web init`, `web ingest`, `web build-index`, `web build`, `web add`, `web remove`, `web status`
618
618
 
619
- **Multi-machine sync:** `remote configure`, `remote status`, `remote sync`, `remote push`, `remote pull`, `remote resolve`
619
+ **Multi-machine sync:** `remote status`, `remote sync`, `remote push`, `remote pull`, `remote resolve` (configure via `gnosys setup remote`)
620
620
 
621
621
  **Server:** `serve`, `serve --with-maintenance`
622
622
 
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();
@@ -3304,14 +3347,7 @@ program
3304
3347
  const dreamCmd = program
3305
3348
  .command("dream")
3306
3349
  .description("Dream Mode — idle-time consolidation (run a cycle, view log)");
3307
- // Bare `gnosys dream` runs a cycle now (preserves v5.4.1 behavior).
3308
- dreamCmd
3309
- .option("--max-runtime <minutes>", "Max runtime in minutes (default: 30)")
3310
- .option("--no-critique", "Skip self-critique phase")
3311
- .option("--no-summaries", "Skip summary generation")
3312
- .option("--no-relationships", "Skip relationship discovery")
3313
- .option("--json", "Output raw JSON report")
3314
- .action(async (opts) => {
3350
+ async function runDreamCycle(opts) {
3315
3351
  const resolver = new GnosysResolver();
3316
3352
  await resolver.resolve();
3317
3353
  const stores = resolver.getStores();
@@ -3321,6 +3357,7 @@ dreamCmd
3321
3357
  }
3322
3358
  const { GnosysDB: DbClass } = await import("./lib/db.js");
3323
3359
  const { GnosysDreamEngine, formatDreamReport } = await import("./lib/dream.js");
3360
+ const { getMachineId } = await import("./lib/remote.js");
3324
3361
  const storePath = stores[0].path;
3325
3362
  const cfg = await loadConfig(storePath);
3326
3363
  const db = new DbClass(storePath);
@@ -3328,6 +3365,24 @@ dreamCmd
3328
3365
  console.error("Dream Mode requires gnosys.db (v2.0). Run 'gnosys migrate' first.");
3329
3366
  process.exit(1);
3330
3367
  }
3368
+ // Designation gate — warn (and exit unless --force) if this isn't the
3369
+ // designated dream machine. Manual runs from non-designated machines are
3370
+ // useful for testing but shouldn't happen by accident on shared brains.
3371
+ const centralDb = GnosysDB.openCentral();
3372
+ if (centralDb.isAvailable()) {
3373
+ const designated = centralDb.getDreamMachineId();
3374
+ if (designated) {
3375
+ const localId = getMachineId(centralDb);
3376
+ if (designated !== localId && !opts.force) {
3377
+ console.error(`Dream is designated to machine ${designated}, but this is ${localId}.\n` +
3378
+ `Pass --force to run anyway, or run 'gnosys setup dream' to redesignate.`);
3379
+ centralDb.close();
3380
+ db.close();
3381
+ process.exit(1);
3382
+ }
3383
+ }
3384
+ centralDb.close();
3385
+ }
3331
3386
  const dreamConfig = {
3332
3387
  enabled: true,
3333
3388
  idleMinutes: 0,
@@ -3351,7 +3406,28 @@ dreamCmd
3351
3406
  console.log(formatDreamReport(report));
3352
3407
  }
3353
3408
  db.close();
3354
- });
3409
+ }
3410
+ // Bare `gnosys dream` runs a cycle (preserves v5.4.1 behavior).
3411
+ dreamCmd
3412
+ .option("--max-runtime <minutes>", "Max runtime in minutes (default: 30)")
3413
+ .option("--no-critique", "Skip self-critique phase")
3414
+ .option("--no-summaries", "Skip summary generation")
3415
+ .option("--no-relationships", "Skip relationship discovery")
3416
+ .option("--force", "Run even if this machine is not the designated dream node")
3417
+ .option("--json", "Output raw JSON report")
3418
+ .action(runDreamCycle);
3419
+ // `gnosys dream run` — explicit alias matching the `gnosys dream log|run`
3420
+ // pattern. Same options + behavior as the bare command.
3421
+ dreamCmd
3422
+ .command("run")
3423
+ .description("Force a dream cycle now (manual trigger)")
3424
+ .option("--max-runtime <minutes>", "Max runtime in minutes (default: 30)")
3425
+ .option("--no-critique", "Skip self-critique phase")
3426
+ .option("--no-summaries", "Skip summary generation")
3427
+ .option("--no-relationships", "Skip relationship discovery")
3428
+ .option("--force", "Run even if this machine is not the designated dream node")
3429
+ .option("--json", "Output raw JSON report")
3430
+ .action(runDreamCycle);
3355
3431
  // `gnosys dream log` — view recent dream runs from audit_log
3356
3432
  dreamCmd
3357
3433
  .command("log")