gnosys 5.11.1 → 5.11.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
@@ -36,6 +36,7 @@ gnosys setup # configures provider, API key, and your IDE/agent
36
36
  ## Quick start
37
37
 
38
38
  ```bash
39
+ gnosys setup ides # wire MCP into your IDEs (once)
39
40
  cd your-project
40
41
  gnosys init # register the project
41
42
  gnosys add "We chose PostgreSQL over MySQL for JSON support"
@@ -61,7 +62,7 @@ All tools are exposed over stdio and HTTP transports. Many tools accept an optio
61
62
 
62
63
  This package installs two binaries:
63
64
 
64
- - **`gnosys`** — the CLI. `gnosys serve` starts the MCP server (stdio by default, `--transport http` for the central-server topology). `gnosys init <ide>` wires this into your IDE/agent automatically.
65
+ - **`gnosys`** — the CLI. `gnosys serve` starts the MCP server (stdio by default, `--transport http` for the central-server topology). `gnosys setup ides` wires `gnosys-mcp` into your IDE/agent configs.
65
66
  - **`gnosys-mcp`** — a direct alias for the MCP stdio server entry, for MCP clients that prefer to spawn the server binary directly (e.g. `npx -y gnosys-mcp`). Equivalent to `gnosys serve`.
66
67
 
67
68
  | Tool | Description |
package/dist/cli.js CHANGED
@@ -973,7 +973,7 @@ setupCmd
973
973
  // `gnosys setup ides` — configure IDE / MCP integrations standalone
974
974
  setupCmd
975
975
  .command("ides")
976
- .description("Configure IDE integrations (Claude Code/Desktop, Cursor, Codex, Gemini CLI, Antigravity)")
976
+ .description("Configure IDE MCP integrations (Claude Code/Desktop, Cursor, Codex, Grok Build, Gemini CLI, Antigravity)")
977
977
  .action(async () => {
978
978
  const readline = await import("readline/promises");
979
979
  const { runIdesSetup } = await import("./lib/setup/sections/ides.js");
@@ -1021,11 +1021,11 @@ setupCmd
1021
1021
  // we need to revive a top-level shortcut later.
1022
1022
  // ─── gnosys init ─────────────────────────────────────────────────────────
1023
1023
  program
1024
- .command("init [ide]")
1025
- .description("Initialize Gnosys in the current directory. Optionally specify IDE: cursor, claude, claude-desktop, codex, gemini-cli, or antigravity to force IDE setup.")
1024
+ .command("init")
1025
+ .description("Initialize Gnosys in the current directory (project store, identity, central DB). Wire IDE MCP servers with: gnosys setup ides")
1026
1026
  .option("-d, --directory <dir>", "Target directory (default: cwd)")
1027
1027
  .option("-n, --name <name>", "Project name (default: directory basename)")
1028
- .action(async (ide, opts) => {
1028
+ .action(async (opts) => {
1029
1029
  const targetDir = opts.directory
1030
1030
  ? path.resolve(opts.directory)
1031
1031
  : process.cwd();
@@ -1127,62 +1127,8 @@ program
1127
1127
  else {
1128
1128
  console.log(`\nIDE hooks: ${hookResult.details}`);
1129
1129
  }
1130
- // If a specific IDE was requested, force-create its config
1131
- if (ide) {
1132
- const validIdes = ["cursor", "claude", "claude-desktop", "codex", "gemini-cli", "antigravity"];
1133
- const normalizedIde = ide.toLowerCase();
1134
- if (!validIdes.includes(normalizedIde)) {
1135
- console.log(`\nUnknown IDE: "${ide}". Valid options: ${validIdes.join(", ")}`);
1136
- }
1137
- else {
1138
- const { configureCursor, configureClaudeCode, configureCodex } = await import("./lib/projectIdentity.js");
1139
- // Cursor/Claude/Codex have IDE-specific session hooks. Gemini CLI and
1140
- // Antigravity don't yet, so we skip the hook step for them.
1141
- let result;
1142
- switch (normalizedIde) {
1143
- case "cursor":
1144
- result = await configureCursor(targetDir);
1145
- break;
1146
- case "claude":
1147
- result = await configureClaudeCode(targetDir);
1148
- break;
1149
- case "codex":
1150
- result = await configureCodex(targetDir);
1151
- break;
1152
- }
1153
- if (result?.configured) {
1154
- console.log(`\nIDE setup (${result.ide}):`);
1155
- console.log(` ${result.details}`);
1156
- console.log(` File: ${result.filePath}`);
1157
- }
1158
- // Set up MCP config for the IDE
1159
- const { setupIDE } = await import("./lib/setup.js");
1160
- const mcp = await setupIDE(normalizedIde, targetDir);
1161
- if (mcp.success) {
1162
- console.log(` MCP: ${mcp.message}`);
1163
- }
1164
- // Update agentRulesTarget in gnosys.json (only for IDEs with rules files)
1165
- const targetMap = {
1166
- cursor: ".cursor/rules/gnosys.mdc",
1167
- claude: "CLAUDE.md",
1168
- codex: "CODEX.md",
1169
- };
1170
- if (targetMap[normalizedIde]) {
1171
- const identityPath = path.join(storePath, "gnosys.json");
1172
- try {
1173
- const identityContent = await fs.readFile(identityPath, "utf-8");
1174
- const identity = JSON.parse(identityContent);
1175
- identity.agentRulesTarget = targetMap[normalizedIde];
1176
- await fs.writeFile(identityPath, JSON.stringify(identity, null, 2) + "\n", "utf-8");
1177
- console.log(` Config: agentRulesTarget → ${identity.agentRulesTarget}`);
1178
- }
1179
- catch {
1180
- // Non-critical
1181
- }
1182
- }
1183
- }
1184
- }
1185
- console.log(`\nStart adding memories with: gnosys add "your knowledge here"`);
1130
+ console.log(`\nWire IDE MCP servers: gnosys setup ides`);
1131
+ console.log(`Start adding memories with: gnosys add "your knowledge here"`);
1186
1132
  });
1187
1133
  // ─── gnosys migrate ─────────────────────────────────────────────────────
1188
1134
  program
@@ -4422,7 +4368,7 @@ exportCmd
4422
4368
  // ─── gnosys serve ────────────────────────────────────────────────────────
4423
4369
  program
4424
4370
  .command("serve")
4425
- .description("Start the MCP server (stdio mode). Used by IDE integrations — Claude Code/Desktop, Cursor, Codex, etc. spawn this command in the background to talk to gnosys via the Model Context Protocol. You don't normally invoke this yourself; `gnosys init <ide>` wires it into the IDE config.")
4371
+ .description("Start the MCP server (stdio mode). Used by IDE integrations — Claude Code/Desktop, Cursor, Codex, etc. spawn this command in the background to talk to gnosys via the Model Context Protocol. You don't normally invoke this yourself; `gnosys setup ides` wires gnosys-mcp into your IDE configs.")
4426
4372
  .option("--with-maintenance", "Run maintenance every 6 hours in background")
4427
4373
  .option("--transport <mode>", "Transport: 'stdio' (default) or 'http' (central-server topology)", "stdio")
4428
4374
  .option("--host <addr>", "HTTP bind address — http transport (default 127.0.0.1; use a tailnet addr to share)", "127.0.0.1")
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  // MCP stdio JSON protocol. parse() is a pure function with no side effects.
11
11
  import dotenv from "dotenv";
12
12
  import path from "path";
13
- import { readFileSync } from "fs";
13
+ import { readFileSync, realpathSync } from "fs";
14
14
  import { fileURLToPath } from "url";
15
15
  const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
16
16
  try {
@@ -3092,8 +3092,22 @@ export async function startMcpServer() {
3092
3092
  // Notification handler setup failed — non-critical
3093
3093
  }
3094
3094
  }
3095
- const invokedAsScript = !!process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
3096
- if (invokedAsScript) {
3095
+ /** True when this file is the process entry (direct path or npm `gnosys-mcp` bin symlink). */
3096
+ function isMcpEntryPoint() {
3097
+ if (!process.argv[1])
3098
+ return false;
3099
+ const entry = path.resolve(process.argv[1]);
3100
+ const self = fileURLToPath(import.meta.url);
3101
+ if (entry === self)
3102
+ return true;
3103
+ try {
3104
+ return realpathSync(entry) === realpathSync(self);
3105
+ }
3106
+ catch {
3107
+ return false;
3108
+ }
3109
+ }
3110
+ if (isMcpEntryPoint()) {
3097
3111
  startMcpServer().catch((err) => {
3098
3112
  console.error("Fatal error:", err);
3099
3113
  process.exit(1);
@@ -38,7 +38,7 @@ const IDE_TARGET_DISPLAY = {
38
38
  codex: ".codex/mcp.json",
39
39
  "gemini-cli": "~/.gemini/settings.json",
40
40
  antigravity: "~/.gemini/antigravity/mcp_config.json",
41
- "grok-build": "~/.grok/config.toml",
41
+ "grok-build": "~/.grok/config.toml ([mcp_servers.gnosys])",
42
42
  };
43
43
  function ideTarget(ide) {
44
44
  return IDE_TARGET_DISPLAY[ide] ?? `.${ide}/mcp.json`;
@@ -56,14 +56,11 @@ export declare function writeApiKey(provider: string, key: string): Promise<void
56
56
  */
57
57
  export declare function detectIDEs(projectDir: string): Promise<string[]>;
58
58
  /**
59
- * Replace (or append) a `[mcp.<name>]` block inside the TOML text for
60
- * Grok Build's config file. Preserves every line outside that block
61
- * deci-046 read-then-merge rule. We can't pull in a TOML dependency
62
- * without adding to package.json, so we ship a minimal hand-rolled
63
- * updater scoped exactly to the `[mcp.gnosys]` use case.
59
+ * Replace (or append) a `[mcp_servers.<name>]` block inside Grok Build's
60
+ * `~/.grok/config.toml`. Preserves every line outside that block (deci-046).
64
61
  *
65
- * Spec assumption: TOML headers we touch are simple `[a.b]` lines with
66
- * no inline tables or nested arrays. Any other content is left alone.
62
+ * Grok Build reads `mcp_servers.*` only legacy `[mcp.*]` blocks are ignored
63
+ * by `grok mcp list` / the agent runtime (see ~/.grok/README.md).
67
64
  *
68
65
  * Exported for tests.
69
66
  */
package/dist/lib/setup.js CHANGED
@@ -567,33 +567,48 @@ export async function detectIDEs(projectDir) {
567
567
  }
568
568
  return detected;
569
569
  }
570
+ /** Remove a single `[section]` block from Grok TOML (hand-rolled; see upsertGrokMcpBlock). */
571
+ function removeGrokTomlSection(existing, sectionHeader) {
572
+ const lines = existing.split("\n");
573
+ const headerIdx = lines.findIndex((line) => line.trim() === sectionHeader);
574
+ if (headerIdx === -1)
575
+ return existing;
576
+ let endIdx = lines.length;
577
+ for (let i = headerIdx + 1; i < lines.length; i++) {
578
+ if (/^\s*\[/.test(lines[i])) {
579
+ endIdx = i;
580
+ break;
581
+ }
582
+ }
583
+ const before = lines.slice(0, headerIdx).join("\n");
584
+ const after = lines.slice(endIdx).join("\n");
585
+ const merged = [before, after].filter((s) => s.length > 0).join("\n");
586
+ return merged.length > 0 ? `${merged}\n` : "";
587
+ }
570
588
  /**
571
- * Replace (or append) a `[mcp.<name>]` block inside the TOML text for
572
- * Grok Build's config file. Preserves every line outside that block
573
- * deci-046 read-then-merge rule. We can't pull in a TOML dependency
574
- * without adding to package.json, so we ship a minimal hand-rolled
575
- * updater scoped exactly to the `[mcp.gnosys]` use case.
589
+ * Replace (or append) a `[mcp_servers.<name>]` block inside Grok Build's
590
+ * `~/.grok/config.toml`. Preserves every line outside that block (deci-046).
576
591
  *
577
- * Spec assumption: TOML headers we touch are simple `[a.b]` lines with
578
- * no inline tables or nested arrays. Any other content is left alone.
592
+ * Grok Build reads `mcp_servers.*` only legacy `[mcp.*]` blocks are ignored
593
+ * by `grok mcp list` / the agent runtime (see ~/.grok/README.md).
579
594
  *
580
595
  * Exported for tests.
581
596
  */
582
597
  export function upsertGrokMcpBlock(existing, name, entry) {
583
- const sectionHeader = `[mcp.${name}]`;
584
- const lines = existing.split("\n");
598
+ // Drop mistaken v5.9.4 `[mcp.<name>]` sections so we don't leave dead config.
599
+ let content = removeGrokTomlSection(existing, `[mcp.${name}]`);
600
+ const sectionHeader = `[mcp_servers.${name}]`;
601
+ const lines = content.split("\n");
585
602
  const headerIdx = lines.findIndex((line) => line.trim() === sectionHeader);
586
603
  const blockBody = renderGrokMcpBlock(entry);
587
604
  if (headerIdx === -1) {
588
- // Append a fresh block, separated by a blank line if the file has content.
589
- const prefix = existing.length === 0 || existing.endsWith("\n\n")
590
- ? existing
591
- : existing.endsWith("\n") ? `${existing}\n` : `${existing}\n\n`;
605
+ const prefix = content.length === 0 || content.endsWith("\n\n")
606
+ ? content
607
+ : content.endsWith("\n")
608
+ ? `${content}\n`
609
+ : `${content}\n\n`;
592
610
  return `${prefix}${sectionHeader}\n${blockBody}`;
593
611
  }
594
- // Replace the existing block — everything from sectionHeader up to the
595
- // next `[` header (or EOF). Count blank lines immediately after the block
596
- // so we can preserve the original spacing before the next section.
597
612
  let endIdx = lines.length;
598
613
  for (let i = headerIdx + 1; i < lines.length; i++) {
599
614
  if (/^\s*\[/.test(lines[i])) {
@@ -611,7 +626,6 @@ export function upsertGrokMcpBlock(existing, name, entry) {
611
626
  const beforeBlock = lines.slice(0, headerIdx).join("\n");
612
627
  const head = beforeBlock.length > 0 ? `${beforeBlock}\n` : "";
613
628
  if (!hasFollowingSection) {
614
- // No following section — drop trailing blank lines and end with a single \n.
615
629
  return `${head}${sectionHeader}\n${blockBody}`;
616
630
  }
617
631
  const gap = "\n".repeat(Math.max(1, trailingBlankBeforeNext));
@@ -814,10 +828,9 @@ export async function setupIDE(ide, projectDir) {
814
828
  return { success: true, message: "Antigravity MCP config updated (~/.gemini/antigravity/mcp_config.json)" };
815
829
  }
816
830
  case "grok-build": {
817
- // v5.9.4 Bug 12 — Grok Build reads its MCP servers from a
818
- // `[mcp.<name>]` block in ~/.grok/config.toml. We never clobber
819
- // unrelated TOML content (per deci-046 read-then-merge rule); the
820
- // helper preserves every line outside the `[mcp.gnosys]` block.
831
+ // v5.9.4 Bug 12 — Grok Build reads MCP from `[mcp_servers.<name>]`
832
+ // in ~/.grok/config.toml (not `[mcp.<name>]`). We never clobber
833
+ // unrelated TOML content (per deci-046 read-then-merge rule).
821
834
  const grokDir = path.join(os.homedir(), ".grok");
822
835
  const configPath = path.join(grokDir, "config.toml");
823
836
  await fs.mkdir(grokDir, { recursive: true });
@@ -77,8 +77,9 @@ async function main() {
77
77
  out();
78
78
  out(" Get started:");
79
79
  out(" 1. gnosys setup configure LLM providers and preferences");
80
- out(" 2. gnosys init initialize gnosys in a project directory");
81
- out(" 3. gnosys status check project status");
80
+ out(" 2. gnosys setup ides wire MCP into your IDEs (once per machine)");
81
+ out(" 3. gnosys init initialize gnosys in a project directory");
82
+ out(" 4. gnosys status check project status");
82
83
  out();
83
84
  // If interactive, offer to run setup automatically
84
85
  if (isInteractive) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnosys",
3
- "version": "5.11.1",
3
+ "version": "5.11.3",
4
4
  "description": "Gnosys — Persistent Memory for AI Agents. Sandbox-first runtime, central SQLite brain, federated search, Dream Mode, Web Knowledge Base, Obsidian export.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -34,7 +34,7 @@
34
34
  "docs:mcp-tools": "node scripts/gen-mcp-tools.mjs --write",
35
35
  "docs:cli": "node scripts/gen-cli-docs.mjs --write",
36
36
  "postinstall": "node dist/postinstall.js || true",
37
- "prepublishOnly": "npm run build:publish"
37
+ "prepublishOnly": "npm run build:publish && node -e \"const fs=require('fs');for (const f of ['dist/cli.js','dist/index.js']) fs.chmodSync(f,0o755)\""
38
38
  },
39
39
  "dependencies": {
40
40
  "@anthropic-ai/sdk": "^0.78.0",