gnosys 5.11.2 → 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 +8 -1
- package/dist/index.js +0 -0
- package/dist/lib/ideMcpInstall.d.ts +30 -0
- package/dist/lib/ideMcpInstall.js +102 -0
- package/dist/lib/setup/sections/ides.d.ts +7 -0
- package/dist/lib/setup/sections/ides.js +25 -3
- package/dist/lib/setup.d.ts +4 -7
- package/dist/lib/setup.js +66 -148
- package/package.json +2 -2
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
|
-
.
|
|
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 });
|
package/dist/index.js
CHANGED
|
File without changes
|
|
@@ -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,10 +36,10 @@ 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: "
|
|
39
|
+
codex: "codex mcp add (CLI registry)",
|
|
39
40
|
"gemini-cli": "~/.gemini/settings.json",
|
|
40
41
|
antigravity: "~/.gemini/antigravity/mcp_config.json",
|
|
41
|
-
"grok-build": "~/.grok/config.toml",
|
|
42
|
+
"grok-build": "~/.grok/config.toml ([mcp_servers.gnosys])",
|
|
42
43
|
};
|
|
43
44
|
function ideTarget(ide) {
|
|
44
45
|
return IDE_TARGET_DISPLAY[ide] ?? `.${ide}/mcp.json`;
|
|
@@ -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.d.ts
CHANGED
|
@@ -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 `[
|
|
60
|
-
*
|
|
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
|
-
*
|
|
66
|
-
*
|
|
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
|
@@ -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";
|
|
@@ -568,32 +569,29 @@ export async function detectIDEs(projectDir) {
|
|
|
568
569
|
return detected;
|
|
569
570
|
}
|
|
570
571
|
/**
|
|
571
|
-
* Replace (or append) a `[
|
|
572
|
-
*
|
|
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.
|
|
572
|
+
* Replace (or append) a `[mcp_servers.<name>]` block inside Grok Build's
|
|
573
|
+
* `~/.grok/config.toml`. Preserves every line outside that block (deci-046).
|
|
576
574
|
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
575
|
+
* Grok Build reads `mcp_servers.*` only — legacy `[mcp.*]` blocks are ignored
|
|
576
|
+
* by `grok mcp list` / the agent runtime (see ~/.grok/README.md).
|
|
579
577
|
*
|
|
580
578
|
* Exported for tests.
|
|
581
579
|
*/
|
|
582
580
|
export function upsertGrokMcpBlock(existing, name, entry) {
|
|
583
|
-
|
|
584
|
-
|
|
581
|
+
// Drop mistaken v5.9.4 `[mcp.<name>]` sections so we don't leave dead config.
|
|
582
|
+
let content = removeTomlSection(existing, `[mcp.${name}]`);
|
|
583
|
+
const sectionHeader = `[mcp_servers.${name}]`;
|
|
584
|
+
const lines = content.split("\n");
|
|
585
585
|
const headerIdx = lines.findIndex((line) => line.trim() === sectionHeader);
|
|
586
586
|
const blockBody = renderGrokMcpBlock(entry);
|
|
587
587
|
if (headerIdx === -1) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
588
|
+
const prefix = content.length === 0 || content.endsWith("\n\n")
|
|
589
|
+
? content
|
|
590
|
+
: content.endsWith("\n")
|
|
591
|
+
? `${content}\n`
|
|
592
|
+
: `${content}\n\n`;
|
|
592
593
|
return `${prefix}${sectionHeader}\n${blockBody}`;
|
|
593
594
|
}
|
|
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
595
|
let endIdx = lines.length;
|
|
598
596
|
for (let i = headerIdx + 1; i < lines.length; i++) {
|
|
599
597
|
if (/^\s*\[/.test(lines[i])) {
|
|
@@ -611,32 +609,12 @@ export function upsertGrokMcpBlock(existing, name, entry) {
|
|
|
611
609
|
const beforeBlock = lines.slice(0, headerIdx).join("\n");
|
|
612
610
|
const head = beforeBlock.length > 0 ? `${beforeBlock}\n` : "";
|
|
613
611
|
if (!hasFollowingSection) {
|
|
614
|
-
// No following section — drop trailing blank lines and end with a single \n.
|
|
615
612
|
return `${head}${sectionHeader}\n${blockBody}`;
|
|
616
613
|
}
|
|
617
614
|
const gap = "\n".repeat(Math.max(1, trailingBlankBeforeNext));
|
|
618
615
|
const afterBlock = afterLines.join("\n");
|
|
619
616
|
return `${head}${sectionHeader}\n${blockBody}${gap}${afterBlock}`;
|
|
620
617
|
}
|
|
621
|
-
/**
|
|
622
|
-
* Absolute path to the `gnosys-mcp` stdio entry (dist/index.js).
|
|
623
|
-
* Prefer this over `gnosys serve` — v5.11.0 `gnosys serve` imported index.js but
|
|
624
|
-
* did not call startMcpServer(), so MCP hosts saw "connection closed" on init.
|
|
625
|
-
*/
|
|
626
|
-
function resolveGnosysMcpCommand() {
|
|
627
|
-
try {
|
|
628
|
-
const p = execSync("command -v gnosys-mcp", { encoding: "utf-8" }).trim();
|
|
629
|
-
if (p)
|
|
630
|
-
return p;
|
|
631
|
-
}
|
|
632
|
-
catch {
|
|
633
|
-
// Fall back to bare name on PATH.
|
|
634
|
-
}
|
|
635
|
-
return "gnosys-mcp";
|
|
636
|
-
}
|
|
637
|
-
function gnosysMcpServerEntry() {
|
|
638
|
-
return { command: resolveGnosysMcpCommand(), args: [] };
|
|
639
|
-
}
|
|
640
618
|
function renderGrokMcpBlock(entry) {
|
|
641
619
|
const argsStr = `[${entry.args.map((a) => JSON.stringify(a)).join(", ")}]`;
|
|
642
620
|
const lines = [
|
|
@@ -652,48 +630,45 @@ function renderGrokMcpBlock(entry) {
|
|
|
652
630
|
* Set up Gnosys MCP integration for a specific IDE.
|
|
653
631
|
*/
|
|
654
632
|
export async function setupIDE(ide, projectDir) {
|
|
633
|
+
const canonical = normalizeIdeKey(ide);
|
|
634
|
+
if (!canonical) {
|
|
635
|
+
return { success: false, message: `Unknown IDE: ${ide}` };
|
|
636
|
+
}
|
|
655
637
|
try {
|
|
656
|
-
switch (
|
|
638
|
+
switch (canonical) {
|
|
657
639
|
case "claude": {
|
|
658
640
|
const mcpCmd = resolveGnosysMcpCommand();
|
|
641
|
+
let codeMsg = `Claude Code MCP registered (${mcpCmd})`;
|
|
659
642
|
try {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}
|
|
663
|
-
catch {
|
|
664
|
-
// Not registered yet — fine.
|
|
665
|
-
}
|
|
666
|
-
execSync(`claude mcp add -s user gnosys -- ${mcpCmd}`, {
|
|
667
|
-
stdio: "pipe",
|
|
668
|
-
});
|
|
643
|
+
runCli("claude", ["mcp", "remove", "gnosys"], { allowFailure: true });
|
|
644
|
+
runCli("claude", ["mcp", "add", "-s", "user", "gnosys", "--", mcpCmd]);
|
|
669
645
|
}
|
|
670
646
|
catch (e) {
|
|
671
647
|
const msg = e instanceof Error ? e.message : String(e);
|
|
672
648
|
if (msg.includes("already exists")) {
|
|
673
|
-
|
|
649
|
+
codeMsg = "Claude Code MCP server already configured";
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
codeMsg = `Claude Code MCP skipped (is \`claude\` on PATH?): ${msg}`;
|
|
674
653
|
}
|
|
675
|
-
throw e;
|
|
676
654
|
}
|
|
677
|
-
|
|
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
|
+
};
|
|
678
662
|
}
|
|
679
663
|
case "cursor": {
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
await
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
}
|
|
688
|
-
catch {
|
|
689
|
-
// File doesn't exist or is invalid — start fresh
|
|
690
|
-
}
|
|
691
|
-
// Merge gnosys entry
|
|
692
|
-
const servers = (config.mcpServers ?? {});
|
|
693
|
-
servers.gnosys = gnosysMcpServerEntry();
|
|
694
|
-
config.mcpServers = servers;
|
|
695
|
-
await fs.writeFile(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
696
|
-
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
|
+
};
|
|
697
672
|
}
|
|
698
673
|
case "codex": {
|
|
699
674
|
// v5.8.5: register via `codex mcp add` (the real Codex CLI registration
|
|
@@ -705,19 +680,13 @@ export async function setupIDE(ide, projectDir) {
|
|
|
705
680
|
//
|
|
706
681
|
// We also migrate away from those legacy blocks in
|
|
707
682
|
// `~/.codex/config.toml` so users on stale configs get cleaned up.
|
|
708
|
-
|
|
709
|
-
// 1. Migrate (strip) legacy hand-written blocks in ~/.codex/config.toml.
|
|
683
|
+
// 1. Strip legacy hand-written sections in ~/.codex/config.toml.
|
|
710
684
|
const userCodexConfig = path.join(os.homedir(), ".codex", "config.toml");
|
|
711
685
|
try {
|
|
712
686
|
let existing = await fs.readFile(userCodexConfig, "utf-8");
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
// v5.8.4 shape: [mcp.gnosys] type/command
|
|
717
|
-
existing = existing.replace(/\n?\[mcp\.gnosys\][^[]*?type\s*=\s*"local"[^[]*?command\s*=\s*\[[^\]]*\]\s*\n?/, "\n");
|
|
718
|
-
if (existing !== before) {
|
|
719
|
-
existing = existing.replace(/\n{3,}/g, "\n\n");
|
|
720
|
-
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");
|
|
721
690
|
}
|
|
722
691
|
}
|
|
723
692
|
catch {
|
|
@@ -730,25 +699,16 @@ export async function setupIDE(ide, projectDir) {
|
|
|
730
699
|
// remove and re-add.
|
|
731
700
|
let alreadyCorrect = false;
|
|
732
701
|
try {
|
|
733
|
-
const existing =
|
|
734
|
-
encoding: "utf-8",
|
|
735
|
-
});
|
|
702
|
+
const existing = runCli("codex", ["mcp", "get", "gnosys"], { allowFailure: true });
|
|
736
703
|
if (existing && existing.includes(gnosysCmd) && !existing.includes(" serve")) {
|
|
737
704
|
alreadyCorrect = true;
|
|
738
705
|
}
|
|
739
706
|
else if (existing) {
|
|
740
|
-
|
|
741
|
-
try {
|
|
742
|
-
execSync("codex mcp remove gnosys", { stdio: "pipe" });
|
|
743
|
-
}
|
|
744
|
-
catch {
|
|
745
|
-
// Non-fatal — `mcp add` below will overwrite or fail loudly.
|
|
746
|
-
}
|
|
707
|
+
runCli("codex", ["mcp", "remove", "gnosys"], { allowFailure: true });
|
|
747
708
|
}
|
|
748
709
|
}
|
|
749
710
|
catch {
|
|
750
|
-
// `codex mcp get`
|
|
751
|
-
// that's the common case on first install; proceed to add.
|
|
711
|
+
// `codex mcp get` failed — proceed to add.
|
|
752
712
|
}
|
|
753
713
|
if (alreadyCorrect) {
|
|
754
714
|
return {
|
|
@@ -758,7 +718,7 @@ export async function setupIDE(ide, projectDir) {
|
|
|
758
718
|
}
|
|
759
719
|
// 4. Register via the canonical Codex CLI command.
|
|
760
720
|
try {
|
|
761
|
-
|
|
721
|
+
runCli("codex", ["mcp", "add", "gnosys", "--", gnosysCmd]);
|
|
762
722
|
}
|
|
763
723
|
catch (err) {
|
|
764
724
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -775,49 +735,24 @@ export async function setupIDE(ide, projectDir) {
|
|
|
775
735
|
};
|
|
776
736
|
}
|
|
777
737
|
case "gemini-cli": {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
await fs.mkdir(geminiDir, { recursive: true });
|
|
782
|
-
let config = {};
|
|
783
|
-
try {
|
|
784
|
-
const existing = await fs.readFile(settingsPath, "utf-8");
|
|
785
|
-
config = JSON.parse(existing);
|
|
786
|
-
}
|
|
787
|
-
catch {
|
|
788
|
-
// File doesn't exist or is invalid — start fresh
|
|
789
|
-
}
|
|
790
|
-
const servers = (config.mcpServers ?? {});
|
|
791
|
-
servers.gnosys = gnosysMcpServerEntry();
|
|
792
|
-
config.mcpServers = servers;
|
|
793
|
-
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);
|
|
794
741
|
return { success: true, message: "Gemini CLI MCP config updated (~/.gemini/settings.json)" };
|
|
795
742
|
}
|
|
796
743
|
case "antigravity": {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
const existing = await fs.readFile(configPath, "utf-8");
|
|
805
|
-
config = JSON.parse(existing);
|
|
806
|
-
}
|
|
807
|
-
catch {
|
|
808
|
-
// File doesn't exist or is invalid — start fresh
|
|
809
|
-
}
|
|
810
|
-
const servers = (config.mcpServers ?? {});
|
|
811
|
-
servers.gnosys = gnosysMcpServerEntry();
|
|
812
|
-
config.mcpServers = servers;
|
|
813
|
-
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
814
|
-
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
|
+
};
|
|
815
751
|
}
|
|
816
752
|
case "grok-build": {
|
|
817
|
-
// v5.9.4 Bug 12 — Grok Build reads
|
|
818
|
-
// `[mcp.<name>]`
|
|
819
|
-
// unrelated TOML content (per deci-046 read-then-merge rule)
|
|
820
|
-
// helper preserves every line outside the `[mcp.gnosys]` block.
|
|
753
|
+
// v5.9.4 Bug 12 — Grok Build reads MCP from `[mcp_servers.<name>]`
|
|
754
|
+
// in ~/.grok/config.toml (not `[mcp.<name>]`). We never clobber
|
|
755
|
+
// unrelated TOML content (per deci-046 read-then-merge rule).
|
|
821
756
|
const grokDir = path.join(os.homedir(), ".grok");
|
|
822
757
|
const configPath = path.join(grokDir, "config.toml");
|
|
823
758
|
await fs.mkdir(grokDir, { recursive: true });
|
|
@@ -829,32 +764,16 @@ export async function setupIDE(ide, projectDir) {
|
|
|
829
764
|
// File doesn't exist yet — start fresh
|
|
830
765
|
}
|
|
831
766
|
const updated = upsertGrokMcpBlock(existing, "gnosys", {
|
|
832
|
-
...
|
|
767
|
+
...gnosysStdioMcpEntry(),
|
|
833
768
|
startup_timeout_sec: 90,
|
|
834
769
|
});
|
|
835
770
|
await fs.writeFile(configPath, updated, "utf-8");
|
|
836
771
|
return { success: true, message: "Grok Build MCP config updated (~/.grok/config.toml)" };
|
|
837
772
|
}
|
|
838
773
|
case "claude-desktop": {
|
|
839
|
-
// Claude Desktop reads MCP servers from claude_desktop_config.json
|
|
840
|
-
// in a platform-specific app data directory. Distinct from Claude
|
|
841
|
-
// Code CLI which uses `claude mcp add`.
|
|
842
774
|
const configPath = getClaudeDesktopConfigPath();
|
|
843
|
-
|
|
844
|
-
await
|
|
845
|
-
let config = {};
|
|
846
|
-
try {
|
|
847
|
-
const existing = await fs.readFile(configPath, "utf-8");
|
|
848
|
-
config = JSON.parse(existing);
|
|
849
|
-
}
|
|
850
|
-
catch {
|
|
851
|
-
// File doesn't exist or is invalid — start fresh
|
|
852
|
-
}
|
|
853
|
-
const servers = (config.mcpServers ?? {});
|
|
854
|
-
servers.gnosys = gnosysMcpServerEntry();
|
|
855
|
-
config.mcpServers = servers;
|
|
856
|
-
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
857
|
-
// Display path with ~ prefix when inside HOME for clarity
|
|
775
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
776
|
+
await installStdioMcpJson(configPath);
|
|
858
777
|
const home = os.homedir();
|
|
859
778
|
const displayPath = configPath.startsWith(home)
|
|
860
779
|
? configPath.replace(home, "~")
|
|
@@ -864,9 +783,8 @@ export async function setupIDE(ide, projectDir) {
|
|
|
864
783
|
message: `Claude Desktop MCP config updated (${displayPath}). Restart Claude Desktop for the change to take effect.`,
|
|
865
784
|
};
|
|
866
785
|
}
|
|
867
|
-
default:
|
|
868
|
-
return { success: false, message: `Unknown IDE: ${ide}` };
|
|
869
786
|
}
|
|
787
|
+
return { success: false, message: `Unhandled IDE: ${canonical}` };
|
|
870
788
|
}
|
|
871
789
|
catch (err) {
|
|
872
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
|
+
"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",
|
|
@@ -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",
|