gnosys 5.11.3 → 5.11.4

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/dist/cli.js CHANGED
@@ -974,7 +974,14 @@ setupCmd
974
974
  setupCmd
975
975
  .command("ides")
976
976
  .description("Configure IDE MCP integrations (Claude Code/Desktop, Cursor, Codex, Grok Build, Gemini CLI, Antigravity)")
977
- .action(async () => {
977
+ .option("--all", "Configure MCP for all supported IDEs (non-interactive)")
978
+ .action(async (opts) => {
979
+ if (opts.all) {
980
+ const { runIdesSetupAll } = await import("./lib/setup/sections/ides.js");
981
+ const { configured, errors } = await runIdesSetupAll(process.cwd());
982
+ console.log(`\n${configured} ides configured · ${errors} errors`);
983
+ return;
984
+ }
978
985
  const readline = await import("readline/promises");
979
986
  const { runIdesSetup } = await import("./lib/setup/sections/ides.js");
980
987
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Shared stdio MCP install helpers for IDE setup (`gnosys setup ides`).
3
+ */
4
+ /** IDE keys handled by `setupIDE()`. */
5
+ export declare const SUPPORTED_IDE_KEYS: readonly ["claude", "claude-desktop", "cursor", "codex", "gemini-cli", "antigravity", "grok-build"];
6
+ export type SupportedIde = (typeof SUPPORTED_IDE_KEYS)[number];
7
+ /** User-facing aliases → canonical IDE key. */
8
+ export declare const IDE_ALIASES: Record<string, SupportedIde>;
9
+ export declare function normalizeIdeKey(ide: string): SupportedIde | null;
10
+ /** Absolute path to `gnosys-mcp` when on PATH, else bare name. */
11
+ export declare function resolveGnosysMcpCommand(): string;
12
+ export declare function gnosysStdioMcpEntry(): {
13
+ command: string;
14
+ args: string[];
15
+ };
16
+ /** True when an existing JSON MCP entry still points at broken `gnosys serve`. */
17
+ export declare function isStaleGnosysMcpEntry(entry: unknown): boolean;
18
+ /** Merge (or replace) the gnosys stdio entry in a JSON MCP config file. */
19
+ export declare function installStdioMcpJson(file: string): Promise<void>;
20
+ /** Project + user-global Cursor MCP paths. */
21
+ export declare function cursorMcpPaths(projectDir: string): {
22
+ project: string;
23
+ user: string;
24
+ };
25
+ /** Run a CLI with argv (safe for paths containing spaces). */
26
+ export declare function runCli(command: string, args: string[], opts?: {
27
+ allowFailure?: boolean;
28
+ }): string;
29
+ /** Remove a `[section]` block from hand-rolled TOML text. */
30
+ export declare function removeTomlSection(existing: string, sectionHeader: string): string;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Shared stdio MCP install helpers for IDE setup (`gnosys setup ides`).
3
+ */
4
+ import { execFileSync, execSync } from "child_process";
5
+ import path from "path";
6
+ import os from "os";
7
+ import { mergeJsonMcpServer } from "./mcpClientConfig.js";
8
+ /** IDE keys handled by `setupIDE()`. */
9
+ export const SUPPORTED_IDE_KEYS = [
10
+ "claude",
11
+ "claude-desktop",
12
+ "cursor",
13
+ "codex",
14
+ "gemini-cli",
15
+ "antigravity",
16
+ "grok-build",
17
+ ];
18
+ /** User-facing aliases → canonical IDE key. */
19
+ export const IDE_ALIASES = {
20
+ grok: "grok-build",
21
+ "grok-build": "grok-build",
22
+ };
23
+ export function normalizeIdeKey(ide) {
24
+ const key = ide.toLowerCase();
25
+ if (SUPPORTED_IDE_KEYS.includes(key)) {
26
+ return key;
27
+ }
28
+ return IDE_ALIASES[key] ?? null;
29
+ }
30
+ /** Absolute path to `gnosys-mcp` when on PATH, else bare name. */
31
+ export function resolveGnosysMcpCommand() {
32
+ try {
33
+ const p = execSync("command -v gnosys-mcp", { encoding: "utf-8" }).trim();
34
+ if (p)
35
+ return p;
36
+ }
37
+ catch {
38
+ // Fall back to bare name on PATH.
39
+ }
40
+ return "gnosys-mcp";
41
+ }
42
+ export function gnosysStdioMcpEntry() {
43
+ return { command: resolveGnosysMcpCommand(), args: [] };
44
+ }
45
+ /** True when an existing JSON MCP entry still points at broken `gnosys serve`. */
46
+ export function isStaleGnosysMcpEntry(entry) {
47
+ if (!entry || typeof entry !== "object")
48
+ return false;
49
+ const e = entry;
50
+ const cmd = String(e.command ?? "");
51
+ const args = Array.isArray(e.args) ? e.args.map(String) : [];
52
+ if (cmd.includes("gnosys-mcp"))
53
+ return false;
54
+ if (cmd.endsWith("/gnosys") || cmd === "gnosys") {
55
+ return args.includes("serve") || args.length === 0;
56
+ }
57
+ return false;
58
+ }
59
+ /** Merge (or replace) the gnosys stdio entry in a JSON MCP config file. */
60
+ export async function installStdioMcpJson(file) {
61
+ await mergeJsonMcpServer(file, gnosysStdioMcpEntry());
62
+ }
63
+ /** Project + user-global Cursor MCP paths. */
64
+ export function cursorMcpPaths(projectDir) {
65
+ return {
66
+ project: path.join(projectDir, ".cursor", "mcp.json"),
67
+ user: path.join(os.homedir(), ".cursor", "mcp.json"),
68
+ };
69
+ }
70
+ /** Run a CLI with argv (safe for paths containing spaces). */
71
+ export function runCli(command, args, opts) {
72
+ try {
73
+ return execFileSync(command, args, {
74
+ encoding: "utf-8",
75
+ stdio: ["pipe", "pipe", "pipe"],
76
+ });
77
+ }
78
+ catch (err) {
79
+ if (opts?.allowFailure) {
80
+ return err instanceof Error && "stdout" in err ? String(err.stdout ?? "") : "";
81
+ }
82
+ throw err;
83
+ }
84
+ }
85
+ /** Remove a `[section]` block from hand-rolled TOML text. */
86
+ export function removeTomlSection(existing, sectionHeader) {
87
+ const lines = existing.split("\n");
88
+ const headerIdx = lines.findIndex((line) => line.trim() === sectionHeader);
89
+ if (headerIdx === -1)
90
+ return existing;
91
+ let endIdx = lines.length;
92
+ for (let i = headerIdx + 1; i < lines.length; i++) {
93
+ if (/^\s*\[/.test(lines[i])) {
94
+ endIdx = i;
95
+ break;
96
+ }
97
+ }
98
+ const before = lines.slice(0, headerIdx).join("\n");
99
+ const after = lines.slice(endIdx).join("\n");
100
+ const merged = [before, after].filter((s) => s.length > 0).join("\n");
101
+ return merged.length > 0 ? `${merged.replace(/\n{3,}/g, "\n\n")}\n` : "";
102
+ }
@@ -25,3 +25,10 @@ export interface IdesSetupOptions {
25
25
  * Returns true if at least one IDE config was written.
26
26
  */
27
27
  export declare function runIdesSetup(opts: IdesSetupOptions): Promise<boolean>;
28
+ /**
29
+ * Non-interactive: wire MCP for every supported IDE (user-level + project cursor).
30
+ */
31
+ export declare function runIdesSetupAll(directory: string): Promise<{
32
+ configured: number;
33
+ errors: number;
34
+ }>;
@@ -9,6 +9,7 @@
9
9
  import fs from "fs/promises";
10
10
  import path from "path";
11
11
  import { detectIDEs, setupIDE } from "../../setup.js";
12
+ import { SUPPORTED_IDE_KEYS } from "../../ideMcpInstall.js";
12
13
  import { safeQuestion } from "../ui/safePrompt.js";
13
14
  import { renderTable } from "../ui/table.js";
14
15
  const DIM = "\x1b[2m";
@@ -18,7 +19,7 @@ const RESET = "\x1b[0m";
18
19
  const CHECK = `${GREEN}✓${RESET}`;
19
20
  const CROSS = `${RED}✗${RESET}`;
20
21
  const IDE_LABELS = {
21
- claude: "Claude Code",
22
+ claude: "Claude Code + Desktop",
22
23
  "claude-desktop": "Claude Desktop",
23
24
  cursor: "Cursor",
24
25
  codex: "Codex",
@@ -35,7 +36,7 @@ const IDE_TARGET_DISPLAY = {
35
36
  claude: "claude mcp add (CLI)",
36
37
  "claude-desktop": "~/Library/.../claude_desktop_config.json",
37
38
  cursor: ".cursor/mcp.json",
38
- codex: ".codex/mcp.json",
39
+ codex: "codex mcp add (CLI registry)",
39
40
  "gemini-cli": "~/.gemini/settings.json",
40
41
  antigravity: "~/.gemini/antigravity/mcp_config.json",
41
42
  "grok-build": "~/.grok/config.toml ([mcp_servers.gnosys])",
@@ -178,3 +179,24 @@ export async function runIdesSetup(opts) {
178
179
  console.log(` ${color(c.textDim, `${configured} ides configured · ${errors} errors`)}`);
179
180
  return configured > 0;
180
181
  }
182
+ /**
183
+ * Non-interactive: wire MCP for every supported IDE (user-level + project cursor).
184
+ */
185
+ export async function runIdesSetupAll(directory) {
186
+ let configured = 0;
187
+ let errors = 0;
188
+ // Claude setup also wires Claude Desktop — skip duplicate pass.
189
+ const idesToRun = SUPPORTED_IDE_KEYS.filter((k) => k !== "claude-desktop");
190
+ for (const ide of idesToRun) {
191
+ const result = await setupIDE(ide, directory);
192
+ if (result.success) {
193
+ console.log(` ${CHECK} ${IDE_LABELS[ide] ?? ide}: ${result.message}`);
194
+ configured++;
195
+ }
196
+ else {
197
+ console.log(` ${CROSS} ${IDE_LABELS[ide] ?? ide}: ${result.message}`);
198
+ errors++;
199
+ }
200
+ }
201
+ return { configured, errors };
202
+ }
package/dist/lib/setup.js CHANGED
@@ -19,6 +19,7 @@ import { validateModel } from "./modelValidation.js";
19
19
  import { resolveActiveStorePath, ensureActiveStorePath } from "./setup/storePath.js";
20
20
  import { safeQuestion } from "./setup/ui/safePrompt.js";
21
21
  import { getClaudeDesktopConfigPath, getApiKeySkipHints } from "./platform.js";
22
+ import { gnosysStdioMcpEntry, installStdioMcpJson, normalizeIdeKey, resolveGnosysMcpCommand, cursorMcpPaths, runCli, removeTomlSection, } from "./ideMcpInstall.js";
22
23
  // ─── ANSI Colors ────────────────────────────────────────────────────────────
23
24
  const BOLD = "\x1b[1m";
24
25
  const DIM = "\x1b[2m";
@@ -567,24 +568,6 @@ export async function detectIDEs(projectDir) {
567
568
  }
568
569
  return detected;
569
570
  }
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
- }
588
571
  /**
589
572
  * Replace (or append) a `[mcp_servers.<name>]` block inside Grok Build's
590
573
  * `~/.grok/config.toml`. Preserves every line outside that block (deci-046).
@@ -596,7 +579,7 @@ function removeGrokTomlSection(existing, sectionHeader) {
596
579
  */
597
580
  export function upsertGrokMcpBlock(existing, name, entry) {
598
581
  // Drop mistaken v5.9.4 `[mcp.<name>]` sections so we don't leave dead config.
599
- let content = removeGrokTomlSection(existing, `[mcp.${name}]`);
582
+ let content = removeTomlSection(existing, `[mcp.${name}]`);
600
583
  const sectionHeader = `[mcp_servers.${name}]`;
601
584
  const lines = content.split("\n");
602
585
  const headerIdx = lines.findIndex((line) => line.trim() === sectionHeader);
@@ -632,25 +615,6 @@ export function upsertGrokMcpBlock(existing, name, entry) {
632
615
  const afterBlock = afterLines.join("\n");
633
616
  return `${head}${sectionHeader}\n${blockBody}${gap}${afterBlock}`;
634
617
  }
635
- /**
636
- * Absolute path to the `gnosys-mcp` stdio entry (dist/index.js).
637
- * Prefer this over `gnosys serve` — v5.11.0 `gnosys serve` imported index.js but
638
- * did not call startMcpServer(), so MCP hosts saw "connection closed" on init.
639
- */
640
- function resolveGnosysMcpCommand() {
641
- try {
642
- const p = execSync("command -v gnosys-mcp", { encoding: "utf-8" }).trim();
643
- if (p)
644
- return p;
645
- }
646
- catch {
647
- // Fall back to bare name on PATH.
648
- }
649
- return "gnosys-mcp";
650
- }
651
- function gnosysMcpServerEntry() {
652
- return { command: resolveGnosysMcpCommand(), args: [] };
653
- }
654
618
  function renderGrokMcpBlock(entry) {
655
619
  const argsStr = `[${entry.args.map((a) => JSON.stringify(a)).join(", ")}]`;
656
620
  const lines = [
@@ -666,48 +630,45 @@ function renderGrokMcpBlock(entry) {
666
630
  * Set up Gnosys MCP integration for a specific IDE.
667
631
  */
668
632
  export async function setupIDE(ide, projectDir) {
633
+ const canonical = normalizeIdeKey(ide);
634
+ if (!canonical) {
635
+ return { success: false, message: `Unknown IDE: ${ide}` };
636
+ }
669
637
  try {
670
- switch (ide) {
638
+ switch (canonical) {
671
639
  case "claude": {
672
640
  const mcpCmd = resolveGnosysMcpCommand();
641
+ let codeMsg = `Claude Code MCP registered (${mcpCmd})`;
673
642
  try {
674
- try {
675
- execSync("claude mcp remove gnosys", { stdio: "pipe" });
676
- }
677
- catch {
678
- // Not registered yet — fine.
679
- }
680
- execSync(`claude mcp add -s user gnosys -- ${mcpCmd}`, {
681
- stdio: "pipe",
682
- });
643
+ runCli("claude", ["mcp", "remove", "gnosys"], { allowFailure: true });
644
+ runCli("claude", ["mcp", "add", "-s", "user", "gnosys", "--", mcpCmd]);
683
645
  }
684
646
  catch (e) {
685
647
  const msg = e instanceof Error ? e.message : String(e);
686
648
  if (msg.includes("already exists")) {
687
- return { success: true, message: "Claude Code MCP server already configured" };
649
+ codeMsg = "Claude Code MCP server already configured";
650
+ }
651
+ else {
652
+ codeMsg = `Claude Code MCP skipped (is \`claude\` on PATH?): ${msg}`;
688
653
  }
689
- throw e;
690
654
  }
691
- return { success: true, message: `Claude Code MCP server registered (${mcpCmd})` };
655
+ const desktop = await setupIDE("claude-desktop", projectDir);
656
+ return {
657
+ success: desktop.success,
658
+ message: desktop.success
659
+ ? `${codeMsg}; ${desktop.message}`
660
+ : `${codeMsg}; Claude Desktop: ${desktop.message}`,
661
+ };
692
662
  }
693
663
  case "cursor": {
694
- const cursorDir = path.join(projectDir, ".cursor");
695
- const mcpPath = path.join(cursorDir, "mcp.json");
696
- await fs.mkdir(cursorDir, { recursive: true });
697
- let config = {};
698
- try {
699
- const existing = await fs.readFile(mcpPath, "utf-8");
700
- config = JSON.parse(existing);
701
- }
702
- catch {
703
- // File doesn't exist or is invalid — start fresh
704
- }
705
- // Merge gnosys entry
706
- const servers = (config.mcpServers ?? {});
707
- servers.gnosys = gnosysMcpServerEntry();
708
- config.mcpServers = servers;
709
- await fs.writeFile(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
710
- return { success: true, message: "Cursor MCP config updated (.cursor/mcp.json)" };
664
+ const paths = cursorMcpPaths(projectDir);
665
+ await fs.mkdir(path.dirname(paths.project), { recursive: true });
666
+ await installStdioMcpJson(paths.project);
667
+ await installStdioMcpJson(paths.user);
668
+ return {
669
+ success: true,
670
+ message: "Cursor MCP updated (project .cursor/mcp.json and ~/.cursor/mcp.json)",
671
+ };
711
672
  }
712
673
  case "codex": {
713
674
  // v5.8.5: register via `codex mcp add` (the real Codex CLI registration
@@ -719,19 +680,13 @@ export async function setupIDE(ide, projectDir) {
719
680
  //
720
681
  // We also migrate away from those legacy blocks in
721
682
  // `~/.codex/config.toml` so users on stale configs get cleaned up.
722
- const os = await import("os");
723
- // 1. Migrate (strip) legacy hand-written blocks in ~/.codex/config.toml.
683
+ // 1. Strip legacy hand-written sections in ~/.codex/config.toml.
724
684
  const userCodexConfig = path.join(os.homedir(), ".codex", "config.toml");
725
685
  try {
726
686
  let existing = await fs.readFile(userCodexConfig, "utf-8");
727
- const before = existing;
728
- // Old shape (pre-v5.8.4): [gnosys] command/args
729
- existing = existing.replace(/\n?\[gnosys\][^[]*?command\s*=\s*"gnosys"[^[]*?args\s*=\s*\[[^\]]*\]\s*\n?/, "\n");
730
- // v5.8.4 shape: [mcp.gnosys] type/command
731
- existing = existing.replace(/\n?\[mcp\.gnosys\][^[]*?type\s*=\s*"local"[^[]*?command\s*=\s*\[[^\]]*\]\s*\n?/, "\n");
732
- if (existing !== before) {
733
- existing = existing.replace(/\n{3,}/g, "\n\n");
734
- await fs.writeFile(userCodexConfig, existing, "utf-8");
687
+ const cleaned = removeTomlSection(removeTomlSection(existing, "[mcp.gnosys]"), "[gnosys]");
688
+ if (cleaned !== existing) {
689
+ await fs.writeFile(userCodexConfig, cleaned, "utf-8");
735
690
  }
736
691
  }
737
692
  catch {
@@ -744,25 +699,16 @@ export async function setupIDE(ide, projectDir) {
744
699
  // remove and re-add.
745
700
  let alreadyCorrect = false;
746
701
  try {
747
- const existing = execSync("codex mcp get gnosys 2>/dev/null", {
748
- encoding: "utf-8",
749
- });
702
+ const existing = runCli("codex", ["mcp", "get", "gnosys"], { allowFailure: true });
750
703
  if (existing && existing.includes(gnosysCmd) && !existing.includes(" serve")) {
751
704
  alreadyCorrect = true;
752
705
  }
753
706
  else if (existing) {
754
- // Different command — remove so we can re-add with the right one.
755
- try {
756
- execSync("codex mcp remove gnosys", { stdio: "pipe" });
757
- }
758
- catch {
759
- // Non-fatal — `mcp add` below will overwrite or fail loudly.
760
- }
707
+ runCli("codex", ["mcp", "remove", "gnosys"], { allowFailure: true });
761
708
  }
762
709
  }
763
710
  catch {
764
- // `codex mcp get` returns non-zero when the server isn't registered —
765
- // that's the common case on first install; proceed to add.
711
+ // `codex mcp get` failed proceed to add.
766
712
  }
767
713
  if (alreadyCorrect) {
768
714
  return {
@@ -772,7 +718,7 @@ export async function setupIDE(ide, projectDir) {
772
718
  }
773
719
  // 4. Register via the canonical Codex CLI command.
774
720
  try {
775
- execSync(`codex mcp add gnosys -- ${gnosysCmd}`, { stdio: "pipe" });
721
+ runCli("codex", ["mcp", "add", "gnosys", "--", gnosysCmd]);
776
722
  }
777
723
  catch (err) {
778
724
  const msg = err instanceof Error ? err.message : String(err);
@@ -789,43 +735,19 @@ export async function setupIDE(ide, projectDir) {
789
735
  };
790
736
  }
791
737
  case "gemini-cli": {
792
- // Gemini CLI reads MCP servers from ~/.gemini/settings.json (user-level)
793
- const geminiDir = path.join(os.homedir(), ".gemini");
794
- const settingsPath = path.join(geminiDir, "settings.json");
795
- await fs.mkdir(geminiDir, { recursive: true });
796
- let config = {};
797
- try {
798
- const existing = await fs.readFile(settingsPath, "utf-8");
799
- config = JSON.parse(existing);
800
- }
801
- catch {
802
- // File doesn't exist or is invalid — start fresh
803
- }
804
- const servers = (config.mcpServers ?? {});
805
- servers.gnosys = gnosysMcpServerEntry();
806
- config.mcpServers = servers;
807
- await fs.writeFile(settingsPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
738
+ const settingsPath = path.join(os.homedir(), ".gemini", "settings.json");
739
+ await fs.mkdir(path.dirname(settingsPath), { recursive: true });
740
+ await installStdioMcpJson(settingsPath);
808
741
  return { success: true, message: "Gemini CLI MCP config updated (~/.gemini/settings.json)" };
809
742
  }
810
743
  case "antigravity": {
811
- // Antigravity reads MCP servers from ~/.gemini/antigravity/mcp_config.json
812
- // (separate file from Gemini CLI's settings.json, even though they share the parent dir)
813
- const antigravityDir = path.join(os.homedir(), ".gemini", "antigravity");
814
- const configPath = path.join(antigravityDir, "mcp_config.json");
815
- await fs.mkdir(antigravityDir, { recursive: true });
816
- let config = {};
817
- try {
818
- const existing = await fs.readFile(configPath, "utf-8");
819
- config = JSON.parse(existing);
820
- }
821
- catch {
822
- // File doesn't exist or is invalid — start fresh
823
- }
824
- const servers = (config.mcpServers ?? {});
825
- servers.gnosys = gnosysMcpServerEntry();
826
- config.mcpServers = servers;
827
- await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
828
- return { success: true, message: "Antigravity MCP config updated (~/.gemini/antigravity/mcp_config.json)" };
744
+ const configPath = path.join(os.homedir(), ".gemini", "antigravity", "mcp_config.json");
745
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
746
+ await installStdioMcpJson(configPath);
747
+ return {
748
+ success: true,
749
+ message: "Antigravity MCP config updated (~/.gemini/antigravity/mcp_config.json)",
750
+ };
829
751
  }
830
752
  case "grok-build": {
831
753
  // v5.9.4 Bug 12 — Grok Build reads MCP from `[mcp_servers.<name>]`
@@ -842,32 +764,16 @@ export async function setupIDE(ide, projectDir) {
842
764
  // File doesn't exist yet — start fresh
843
765
  }
844
766
  const updated = upsertGrokMcpBlock(existing, "gnosys", {
845
- ...gnosysMcpServerEntry(),
767
+ ...gnosysStdioMcpEntry(),
846
768
  startup_timeout_sec: 90,
847
769
  });
848
770
  await fs.writeFile(configPath, updated, "utf-8");
849
771
  return { success: true, message: "Grok Build MCP config updated (~/.grok/config.toml)" };
850
772
  }
851
773
  case "claude-desktop": {
852
- // Claude Desktop reads MCP servers from claude_desktop_config.json
853
- // in a platform-specific app data directory. Distinct from Claude
854
- // Code CLI which uses `claude mcp add`.
855
774
  const configPath = getClaudeDesktopConfigPath();
856
- const configDir = path.dirname(configPath);
857
- await fs.mkdir(configDir, { recursive: true });
858
- let config = {};
859
- try {
860
- const existing = await fs.readFile(configPath, "utf-8");
861
- config = JSON.parse(existing);
862
- }
863
- catch {
864
- // File doesn't exist or is invalid — start fresh
865
- }
866
- const servers = (config.mcpServers ?? {});
867
- servers.gnosys = gnosysMcpServerEntry();
868
- config.mcpServers = servers;
869
- await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
870
- // Display path with ~ prefix when inside HOME for clarity
775
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
776
+ await installStdioMcpJson(configPath);
871
777
  const home = os.homedir();
872
778
  const displayPath = configPath.startsWith(home)
873
779
  ? configPath.replace(home, "~")
@@ -877,9 +783,8 @@ export async function setupIDE(ide, projectDir) {
877
783
  message: `Claude Desktop MCP config updated (${displayPath}). Restart Claude Desktop for the change to take effect.`,
878
784
  };
879
785
  }
880
- default:
881
- return { success: false, message: `Unknown IDE: ${ide}` };
882
786
  }
787
+ return { success: false, message: `Unhandled IDE: ${canonical}` };
883
788
  }
884
789
  catch (err) {
885
790
  const msg = err instanceof Error ? err.message : String(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnosys",
3
- "version": "5.11.3",
3
+ "version": "5.11.4",
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",