@solongate/proxy 0.31.0 → 0.33.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/dist/create.js CHANGED
@@ -214,7 +214,6 @@ console.log('You need an MCP client to connect:');
214
214
  console.log('');
215
215
  console.log(' Claude Code Open this folder, .mcp.json is auto-detected');
216
216
  console.log(' Claude Desktop Add to Settings > MCP Servers');
217
- console.log(' Cursor Open this folder, .mcp.json is auto-detected');
218
217
  console.log(' Windsurf Open this folder, .mcp.json is auto-detected');
219
218
  console.log(' Cline VS Code extension, add server in settings');
220
219
  console.log(' Zed Add to settings.json under mcp_servers');
@@ -312,7 +311,7 @@ async function main() {
312
311
  bEmpty();
313
312
  log(` ${c.dim}\u251C${hr}\u2524${c.reset}`);
314
313
  bEmpty();
315
- bLine(`${c.yellow}Use with Claude Code / Cursor / MCP client:${c.reset}`);
314
+ bLine(`${c.yellow}Use with Claude Code / MCP client:${c.reset}`);
316
315
  bEmpty();
317
316
  bLine(` ${c.dim}1.${c.reset} Replace ${c.blue3}sg_live_YOUR_KEY_HERE${c.reset} in .mcp.json`);
318
317
  bLine(` ${c.dim}2.${c.reset} Open this folder in your MCP client`);
package/dist/index.js CHANGED
@@ -459,9 +459,8 @@ var init_cli_utils = __esm({
459
459
 
460
460
  // src/init.ts
461
461
  var init_exports = {};
462
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
463
- import { resolve as resolve3, join, dirname as dirname2 } from "path";
464
- import { homedir } from "os";
462
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
463
+ import { resolve as resolve3, join as join2, dirname as dirname2 } from "path";
465
464
  import { fileURLToPath } from "url";
466
465
  import { execFileSync } from "child_process";
467
466
  import { createInterface } from "readline";
@@ -580,24 +579,15 @@ function parseInitArgs(argv) {
580
579
  case "--all":
581
580
  options.all = true;
582
581
  break;
583
- case "--claude":
584
- options.tools.push("claude");
585
- break;
586
- case "--cursor":
587
- options.tools.push("cursor");
582
+ case "--claude-code":
583
+ options.tools.push("claude-code");
588
584
  break;
589
585
  case "--gemini":
590
586
  options.tools.push("gemini");
591
587
  break;
592
- case "--antigravity":
593
- options.tools.push("antigravity");
594
- break;
595
588
  case "--openclaw":
596
589
  options.tools.push("openclaw");
597
590
  break;
598
- case "--perplexity":
599
- options.tools.push("perplexity");
600
- break;
601
591
  case "--help":
602
592
  case "-h":
603
593
  printHelp();
@@ -621,25 +611,22 @@ OPTIONS
621
611
  -h, --help Show this help message
622
612
 
623
613
  AI TOOL HOOKS (default: all)
624
- --claude Install hooks for Claude Code
625
- --cursor Install hooks for Cursor
614
+ --claude-code Install hooks for Claude Code
626
615
  --gemini Install hooks for Gemini CLI
627
- --antigravity Install hooks for Antigravity
628
616
  --openclaw Install hooks for OpenClaw
629
- --perplexity Install hooks for Perplexity
630
617
 
631
618
  EXAMPLES
632
619
  npx @solongate/proxy init --all # Protect everything, all tools
633
- npx @solongate/proxy init --all --claude --cursor # Only Claude + Cursor hooks
620
+ npx @solongate/proxy init --all --claude-code --gemini # Only Claude Code + Gemini hooks
634
621
  npx @solongate/proxy init --all --policy policy.json # With custom policy
635
622
  `;
636
623
  console.log(help);
637
624
  }
638
625
  function readHookScript(filename) {
639
- return readFileSync4(join(HOOKS_DIR, filename), "utf-8");
626
+ return readFileSync4(join2(HOOKS_DIR, filename), "utf-8");
640
627
  }
641
628
  function unlockProtectedDirs() {
642
- const dirs = [".solongate", ".claude", ".cursor", ".gemini", ".antigravity", ".openclaw", ".perplexity"];
629
+ const dirs = [".solongate", ".claude", ".gemini", ".openclaw"];
643
630
  for (const dir of dirs) {
644
631
  const fullDir = resolve3(dir);
645
632
  if (!existsSync4(fullDir)) continue;
@@ -691,12 +678,12 @@ function unlockProtectedDirs() {
691
678
  function installHooks(selectedTools = [], wrappedMcpConfig) {
692
679
  unlockProtectedDirs();
693
680
  const hooksDir = resolve3(".solongate", "hooks");
694
- mkdirSync2(hooksDir, { recursive: true });
681
+ mkdirSync3(hooksDir, { recursive: true });
695
682
  const hookFiles = ["guard.mjs", "audit.mjs", "stop.mjs"];
696
683
  let hooksUpdated = 0;
697
684
  let hooksSkipped = 0;
698
685
  for (const filename of hookFiles) {
699
- const hookPath = join(hooksDir, filename);
686
+ const hookPath = join2(hooksDir, filename);
700
687
  const latest = readHookScript(filename);
701
688
  let needsWrite = true;
702
689
  if (existsSync4(hookPath)) {
@@ -716,27 +703,20 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
716
703
  console.log(` Hook files already up to date`);
717
704
  }
718
705
  const allClients = [
719
- { name: "Claude Code", dir: ".claude", key: "claude", agentId: "claude-code", agentName: "Claude Code" },
720
- { name: "Cursor", dir: ".cursor", key: "cursor", agentId: "cursor", agentName: "Cursor" },
706
+ { name: "Claude Code", dir: ".claude", key: "claude-code", agentId: "claude-code", agentName: "Claude Code" },
721
707
  { name: "Gemini CLI", dir: ".gemini", key: "gemini", agentId: "gemini-cli", agentName: "Gemini CLI" },
722
- { name: "Antigravity", dir: ".antigravity", key: "antigravity", agentId: "antigravity", agentName: "Antigravity" },
723
- { name: "OpenClaw", dir: ".openclaw", key: "openclaw", agentId: "openclaw", agentName: "OpenClaw" },
724
- { name: "Perplexity", dir: ".perplexity", key: "perplexity", agentId: "perplexity", agentName: "Perplexity" }
708
+ { name: "OpenClaw", dir: ".openclaw", key: "openclaw", agentId: "openclaw", agentName: "OpenClaw" }
725
709
  ];
726
710
  const clients = selectedTools.length > 0 ? allClients.filter((c3) => selectedTools.includes(c3.key)) : allClients;
727
711
  const activatedNames = [];
728
712
  const skippedNames = [];
729
713
  for (const client of clients) {
730
714
  const clientDir = resolve3(client.dir);
731
- mkdirSync2(clientDir, { recursive: true });
715
+ mkdirSync3(clientDir, { recursive: true });
732
716
  const guardCmd = `node .solongate/hooks/guard.mjs ${client.agentId} "${client.agentName}"`;
733
717
  const auditCmd = `node .solongate/hooks/audit.mjs ${client.agentId} "${client.agentName}"`;
734
718
  const stopCmd = `node .solongate/hooks/stop.mjs ${client.agentId} "${client.agentName}"`;
735
- if (client.key === "cursor") {
736
- const result = installCursorConfig(clientDir, guardCmd, auditCmd, stopCmd, wrappedMcpConfig);
737
- if (result === "skipped") skippedNames.push(client.name);
738
- else activatedNames.push(client.name);
739
- } else if (client.key === "gemini") {
719
+ if (client.key === "gemini") {
740
720
  const result = installGeminiConfig(clientDir, guardCmd, auditCmd, stopCmd, wrappedMcpConfig);
741
721
  if (result === "skipped") skippedNames.push(client.name);
742
722
  else activatedNames.push(client.name);
@@ -763,7 +743,7 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
763
743
  }
764
744
  }
765
745
  function installStandardHookConfig(clientDir, clientName, guardCmd, auditCmd, stopCmd) {
766
- const settingsPath = join(clientDir, "settings.json");
746
+ const settingsPath = join2(clientDir, "settings.json");
767
747
  const hookSettings = {
768
748
  PreToolUse: [
769
749
  { matcher: "", hooks: [{ type: "command", command: guardCmd }] }
@@ -788,74 +768,8 @@ function installStandardHookConfig(clientDir, clientName, guardCmd, auditCmd, st
788
768
  console.log(` ${existingStr ? "Updated" : "Created"} ${settingsPath}`);
789
769
  return "installed";
790
770
  }
791
- function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
792
- let changed = false;
793
- const hookConfig = {
794
- preToolUse: [
795
- { matcher: "", command: guardCmd, failClosed: true }
796
- ],
797
- postToolUse: [
798
- { matcher: "", command: auditCmd }
799
- ]
800
- };
801
- const hooksContent = JSON.stringify({ version: 1, hooks: hookConfig }, null, 2) + "\n";
802
- const userCursorDir = join(homedir(), ".cursor");
803
- if (existsSync4(userCursorDir)) {
804
- const userHooksPath = join(userCursorDir, "hooks.json");
805
- const existingUserHooks = existsSync4(userHooksPath) ? readFileSync4(userHooksPath, "utf-8") : "";
806
- if (hooksContent !== existingUserHooks) {
807
- writeFileSync2(userHooksPath, hooksContent);
808
- console.log(` ${existingUserHooks ? "Updated" : "Created"} ${userHooksPath} (user-level)`);
809
- changed = true;
810
- }
811
- }
812
- const hooksPath = join(clientDir, "hooks.json");
813
- const existingHooks = existsSync4(hooksPath) ? readFileSync4(hooksPath, "utf-8") : "";
814
- if (hooksContent !== existingHooks) {
815
- writeFileSync2(hooksPath, hooksContent);
816
- console.log(` ${existingHooks ? "Updated" : "Created"} ${hooksPath}`);
817
- changed = true;
818
- }
819
- if (wrappedMcpConfig) {
820
- const cursorMcpConfig = { mcpServers: {} };
821
- for (const [name, server] of Object.entries(wrappedMcpConfig.mcpServers)) {
822
- const args = [...server.args ?? []];
823
- const agentIdx = args.indexOf("--agent-name");
824
- if (agentIdx >= 0 && agentIdx + 1 < args.length) {
825
- args[agentIdx + 1] = "cursor";
826
- } else {
827
- const sepIdx = args.indexOf("--");
828
- if (sepIdx >= 0) {
829
- args.splice(sepIdx, 0, "--agent-name", "cursor");
830
- }
831
- }
832
- cursorMcpConfig.mcpServers[name] = { ...server, args };
833
- }
834
- const mcpPath = join(clientDir, "mcp.json");
835
- const mcpStr = JSON.stringify(cursorMcpConfig, null, 2) + "\n";
836
- const existingMcp = existsSync4(mcpPath) ? readFileSync4(mcpPath, "utf-8") : "";
837
- if (mcpStr !== existingMcp) {
838
- writeFileSync2(mcpPath, mcpStr);
839
- console.log(` ${existingMcp ? "Updated" : "Created"} ${mcpPath}`);
840
- changed = true;
841
- }
842
- }
843
- const staleSettings = join(clientDir, "settings.json");
844
- if (existsSync4(staleSettings)) {
845
- try {
846
- const content = JSON.parse(readFileSync4(staleSettings, "utf-8"));
847
- if (content.hooks?.PreToolUse || content.hooks?.PostToolUse) {
848
- writeFileSync2(staleSettings, JSON.stringify({}, null, 2) + "\n");
849
- console.log(` Cleaned stale ${staleSettings} (had wrong hook format)`);
850
- changed = true;
851
- }
852
- } catch {
853
- }
854
- }
855
- return changed ? "installed" : "skipped";
856
- }
857
771
  function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
858
- const settingsPath = join(clientDir, "settings.json");
772
+ const settingsPath = join2(clientDir, "settings.json");
859
773
  let existing = {};
860
774
  try {
861
775
  existing = JSON.parse(readFileSync4(settingsPath, "utf-8"));
@@ -947,11 +861,8 @@ SOLONGATE_API_KEY=sg_live_your_key_here
947
861
  ".mcp.json",
948
862
  ".solongate/**",
949
863
  ".claude/**",
950
- ".cursor/**",
951
864
  ".gemini/**",
952
- ".antigravity/**",
953
- ".openclaw/**",
954
- ".perplexity/**"
865
+ ".openclaw/**"
955
866
  ];
956
867
  if (existsSync4(gitignorePath)) {
957
868
  let gitignore = readFileSync4(gitignorePath, "utf-8");
@@ -1188,8 +1099,7 @@ async function main() {
1188
1099
  console.log(" \u2502 \u2502");
1189
1100
  console.log(" \u2502 MCP servers \u2192 Protected via proxy \u2502");
1190
1101
  console.log(" \u2502 AI tools \u2192 Guarded via hooks \u2502");
1191
- console.log(" \u2502 Claude, Cursor, Gemini, Antigravity, \u2502");
1192
- console.log(" \u2502 OpenClaw, Perplexity \u2502");
1102
+ console.log(" \u2502 Claude Code, Gemini, OpenClaw \u2502");
1193
1103
  console.log(" \u2502 API key \u2192 Set in .env \u2502");
1194
1104
  console.log(" \u2502 \u2502");
1195
1105
  console.log(" \u2502 View logs: https://dashboard.solongate.com \u2502");
@@ -1209,7 +1119,7 @@ var init_init = __esm({
1209
1119
  "mcp.json",
1210
1120
  ".claude/mcp.json"
1211
1121
  ];
1212
- CLAUDE_DESKTOP_PATHS = process.platform === "win32" ? [join(process.env["APPDATA"] ?? "", "Claude", "claude_desktop_config.json")] : process.platform === "darwin" ? [join(process.env["HOME"] ?? "", "Library", "Application Support", "Claude", "claude_desktop_config.json")] : [join(process.env["HOME"] ?? "", ".config", "claude", "claude_desktop_config.json")];
1122
+ CLAUDE_DESKTOP_PATHS = process.platform === "win32" ? [join2(process.env["APPDATA"] ?? "", "Claude", "claude_desktop_config.json")] : process.platform === "darwin" ? [join2(process.env["HOME"] ?? "", "Library", "Application Support", "Claude", "claude_desktop_config.json")] : [join2(process.env["HOME"] ?? "", ".config", "claude", "claude_desktop_config.json")];
1213
1123
  sleep = (ms) => new Promise((r) => setTimeout(r, ms));
1214
1124
  SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1215
1125
  spinnerInterval = null;
@@ -1562,8 +1472,8 @@ var init_inject = __esm({
1562
1472
 
1563
1473
  // src/create.ts
1564
1474
  var create_exports = {};
1565
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
1566
- import { resolve as resolve5, join as join2 } from "path";
1475
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
1476
+ import { resolve as resolve5, join as join3 } from "path";
1567
1477
  import { execSync as execSync2 } from "child_process";
1568
1478
  function withSpinner(message, fn) {
1569
1479
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -1643,7 +1553,7 @@ EXAMPLES
1643
1553
  }
1644
1554
  function createProject(dir, name, _policy) {
1645
1555
  writeFileSync4(
1646
- join2(dir, "package.json"),
1556
+ join3(dir, "package.json"),
1647
1557
  JSON.stringify(
1648
1558
  {
1649
1559
  name,
@@ -1673,7 +1583,7 @@ function createProject(dir, name, _policy) {
1673
1583
  ) + "\n"
1674
1584
  );
1675
1585
  writeFileSync4(
1676
- join2(dir, "tsconfig.json"),
1586
+ join3(dir, "tsconfig.json"),
1677
1587
  JSON.stringify(
1678
1588
  {
1679
1589
  compilerOptions: {
@@ -1693,9 +1603,9 @@ function createProject(dir, name, _policy) {
1693
1603
  2
1694
1604
  ) + "\n"
1695
1605
  );
1696
- mkdirSync3(join2(dir, "src"), { recursive: true });
1606
+ mkdirSync4(join3(dir, "src"), { recursive: true });
1697
1607
  writeFileSync4(
1698
- join2(dir, "src", "index.ts"),
1608
+ join3(dir, "src", "index.ts"),
1699
1609
  `#!/usr/bin/env node
1700
1610
 
1701
1611
  console.log = (...args: unknown[]) => {
@@ -1731,7 +1641,6 @@ console.log('You need an MCP client to connect:');
1731
1641
  console.log('');
1732
1642
  console.log(' Claude Code Open this folder, .mcp.json is auto-detected');
1733
1643
  console.log(' Claude Desktop Add to Settings > MCP Servers');
1734
- console.log(' Cursor Open this folder, .mcp.json is auto-detected');
1735
1644
  console.log(' Windsurf Open this folder, .mcp.json is auto-detected');
1736
1645
  console.log(' Cline VS Code extension, add server in settings');
1737
1646
  console.log(' Zed Add to settings.json under mcp_servers');
@@ -1740,7 +1649,7 @@ console.log('Press Ctrl+C to stop.');
1740
1649
  `
1741
1650
  );
1742
1651
  writeFileSync4(
1743
- join2(dir, ".mcp.json"),
1652
+ join3(dir, ".mcp.json"),
1744
1653
  JSON.stringify(
1745
1654
  {
1746
1655
  mcpServers: {
@@ -1758,12 +1667,12 @@ console.log('Press Ctrl+C to stop.');
1758
1667
  ) + "\n"
1759
1668
  );
1760
1669
  writeFileSync4(
1761
- join2(dir, ".env"),
1670
+ join3(dir, ".env"),
1762
1671
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
1763
1672
  `
1764
1673
  );
1765
1674
  writeFileSync4(
1766
- join2(dir, ".gitignore"),
1675
+ join3(dir, ".gitignore"),
1767
1676
  `node_modules/
1768
1677
  dist/
1769
1678
  *.solongate-backup
@@ -1782,7 +1691,7 @@ async function main3() {
1782
1691
  process.exit(1);
1783
1692
  }
1784
1693
  withSpinner(`Setting up ${opts.name}...`, () => {
1785
- mkdirSync3(dir, { recursive: true });
1694
+ mkdirSync4(dir, { recursive: true });
1786
1695
  createProject(dir, opts.name, opts.policy);
1787
1696
  });
1788
1697
  if (!opts.noInstall) {
@@ -1829,7 +1738,7 @@ async function main3() {
1829
1738
  bEmpty();
1830
1739
  log3(` ${c.dim}\u251C${hr}\u2524${c.reset}`);
1831
1740
  bEmpty();
1832
- bLine(`${c.yellow}Use with Claude Code / Cursor / MCP client:${c.reset}`);
1741
+ bLine(`${c.yellow}Use with Claude Code / MCP client:${c.reset}`);
1833
1742
  bEmpty();
1834
1743
  bLine(` ${c.dim}1.${c.reset} Replace ${c.blue3}sg_live_YOUR_KEY_HERE${c.reset} in .mcp.json`);
1835
1744
  bLine(` ${c.dim}2.${c.reset} Open this folder in your MCP client`);
@@ -2193,8 +2102,8 @@ import {
2193
2102
  ListResourceTemplatesRequestSchema
2194
2103
  } from "@modelcontextprotocol/sdk/types.js";
2195
2104
  import { createServer as createHttpServer } from "http";
2196
- import { resolve as resolve2 } from "path";
2197
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
2105
+ import { resolve as resolve2, join } from "path";
2106
+ import { existsSync as existsSync3, readFileSync as readFileSync3, mkdirSync as mkdirSync2, appendFileSync } from "fs";
2198
2107
 
2199
2108
  // ../core/dist/index.js
2200
2109
  import { z } from "zod";
@@ -6044,12 +5953,9 @@ var SolonGateProxy = class {
6044
5953
  /** Normalize well-known MCP client names to display-friendly agent identities */
6045
5954
  normalizeAgentName(raw) {
6046
5955
  const lower = raw.toLowerCase();
6047
- if (lower.includes("cursor")) return { id: "cursor", name: "Cursor" };
6048
5956
  if (lower.includes("claude-code") || lower === "claude code") return { id: "claude-code", name: "Claude Code" };
6049
5957
  if (lower.includes("claude")) return { id: "claude-desktop", name: "Claude Desktop" };
6050
5958
  if (lower.includes("gemini")) return { id: "gemini-cli", name: "Gemini CLI" };
6051
- if (lower.includes("antigravity")) return { id: "antigravity", name: "Antigravity" };
6052
- if (lower.includes("perplexity")) return { id: "perplexity", name: "Perplexity" };
6053
5959
  return { id: raw.toLowerCase().replace(/\s+/g, "-"), name: raw };
6054
5960
  }
6055
5961
  /** Extract sub-agent identity from MCP _meta field */
@@ -6249,6 +6155,20 @@ var SolonGateProxy = class {
6249
6155
  if (this.server) {
6250
6156
  const clientVersion = this.server.getClientVersion();
6251
6157
  log2(`MCP clientInfo raw: ${JSON.stringify(clientVersion)}`);
6158
+ try {
6159
+ const debugInfo = {
6160
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6161
+ clientInfo: clientVersion,
6162
+ agentNameFlag: this.config.agentName ?? null,
6163
+ resolvedAgentId: this.agentId,
6164
+ resolvedAgentName: this.agentName,
6165
+ pid: process.pid
6166
+ };
6167
+ const debugDir = resolve2(".solongate");
6168
+ mkdirSync2(debugDir, { recursive: true });
6169
+ appendFileSync(join(debugDir, ".debug-proxy"), JSON.stringify(debugInfo) + "\n");
6170
+ } catch {
6171
+ }
6252
6172
  if (clientVersion?.name && !this.config.agentName) {
6253
6173
  const normalized = this.normalizeAgentName(clientVersion.name);
6254
6174
  this.agentId = normalized.id;
@@ -6678,7 +6598,7 @@ ${msg.content.text}`;
6678
6598
  /**
6679
6599
  * Start serving downstream.
6680
6600
  * If --port is set, serves via StreamableHTTP on that port.
6681
- * Otherwise, serves on stdio (default for Claude Code / Cursor / etc).
6601
+ * Otherwise, serves on stdio (default for Claude Code / etc).
6682
6602
  */
6683
6603
  async serve() {
6684
6604
  if (!this.server) throw new Error("Server not created");
package/dist/init.js CHANGED
@@ -3,7 +3,6 @@
3
3
  // src/init.ts
4
4
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
5
  import { resolve, join, dirname } from "path";
6
- import { homedir } from "os";
7
6
  import { fileURLToPath } from "url";
8
7
  import { execFileSync } from "child_process";
9
8
  import { createInterface } from "readline";
@@ -164,24 +163,15 @@ function parseInitArgs(argv) {
164
163
  case "--all":
165
164
  options.all = true;
166
165
  break;
167
- case "--claude":
168
- options.tools.push("claude");
169
- break;
170
- case "--cursor":
171
- options.tools.push("cursor");
166
+ case "--claude-code":
167
+ options.tools.push("claude-code");
172
168
  break;
173
169
  case "--gemini":
174
170
  options.tools.push("gemini");
175
171
  break;
176
- case "--antigravity":
177
- options.tools.push("antigravity");
178
- break;
179
172
  case "--openclaw":
180
173
  options.tools.push("openclaw");
181
174
  break;
182
- case "--perplexity":
183
- options.tools.push("perplexity");
184
- break;
185
175
  case "--help":
186
176
  case "-h":
187
177
  printHelp();
@@ -205,16 +195,13 @@ OPTIONS
205
195
  -h, --help Show this help message
206
196
 
207
197
  AI TOOL HOOKS (default: all)
208
- --claude Install hooks for Claude Code
209
- --cursor Install hooks for Cursor
198
+ --claude-code Install hooks for Claude Code
210
199
  --gemini Install hooks for Gemini CLI
211
- --antigravity Install hooks for Antigravity
212
200
  --openclaw Install hooks for OpenClaw
213
- --perplexity Install hooks for Perplexity
214
201
 
215
202
  EXAMPLES
216
203
  npx @solongate/proxy init --all # Protect everything, all tools
217
- npx @solongate/proxy init --all --claude --cursor # Only Claude + Cursor hooks
204
+ npx @solongate/proxy init --all --claude-code --gemini # Only Claude Code + Gemini hooks
218
205
  npx @solongate/proxy init --all --policy policy.json # With custom policy
219
206
  `;
220
207
  console.log(help);
@@ -225,7 +212,7 @@ function readHookScript(filename) {
225
212
  return readFileSync(join(HOOKS_DIR, filename), "utf-8");
226
213
  }
227
214
  function unlockProtectedDirs() {
228
- const dirs = [".solongate", ".claude", ".cursor", ".gemini", ".antigravity", ".openclaw", ".perplexity"];
215
+ const dirs = [".solongate", ".claude", ".gemini", ".openclaw"];
229
216
  for (const dir of dirs) {
230
217
  const fullDir = resolve(dir);
231
218
  if (!existsSync(fullDir)) continue;
@@ -302,12 +289,9 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
302
289
  console.log(` Hook files already up to date`);
303
290
  }
304
291
  const allClients = [
305
- { name: "Claude Code", dir: ".claude", key: "claude", agentId: "claude-code", agentName: "Claude Code" },
306
- { name: "Cursor", dir: ".cursor", key: "cursor", agentId: "cursor", agentName: "Cursor" },
292
+ { name: "Claude Code", dir: ".claude", key: "claude-code", agentId: "claude-code", agentName: "Claude Code" },
307
293
  { name: "Gemini CLI", dir: ".gemini", key: "gemini", agentId: "gemini-cli", agentName: "Gemini CLI" },
308
- { name: "Antigravity", dir: ".antigravity", key: "antigravity", agentId: "antigravity", agentName: "Antigravity" },
309
- { name: "OpenClaw", dir: ".openclaw", key: "openclaw", agentId: "openclaw", agentName: "OpenClaw" },
310
- { name: "Perplexity", dir: ".perplexity", key: "perplexity", agentId: "perplexity", agentName: "Perplexity" }
294
+ { name: "OpenClaw", dir: ".openclaw", key: "openclaw", agentId: "openclaw", agentName: "OpenClaw" }
311
295
  ];
312
296
  const clients = selectedTools.length > 0 ? allClients.filter((c2) => selectedTools.includes(c2.key)) : allClients;
313
297
  const activatedNames = [];
@@ -318,11 +302,7 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
318
302
  const guardCmd = `node .solongate/hooks/guard.mjs ${client.agentId} "${client.agentName}"`;
319
303
  const auditCmd = `node .solongate/hooks/audit.mjs ${client.agentId} "${client.agentName}"`;
320
304
  const stopCmd = `node .solongate/hooks/stop.mjs ${client.agentId} "${client.agentName}"`;
321
- if (client.key === "cursor") {
322
- const result = installCursorConfig(clientDir, guardCmd, auditCmd, stopCmd, wrappedMcpConfig);
323
- if (result === "skipped") skippedNames.push(client.name);
324
- else activatedNames.push(client.name);
325
- } else if (client.key === "gemini") {
305
+ if (client.key === "gemini") {
326
306
  const result = installGeminiConfig(clientDir, guardCmd, auditCmd, stopCmd, wrappedMcpConfig);
327
307
  if (result === "skipped") skippedNames.push(client.name);
328
308
  else activatedNames.push(client.name);
@@ -374,72 +354,6 @@ function installStandardHookConfig(clientDir, clientName, guardCmd, auditCmd, st
374
354
  console.log(` ${existingStr ? "Updated" : "Created"} ${settingsPath}`);
375
355
  return "installed";
376
356
  }
377
- function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
378
- let changed = false;
379
- const hookConfig = {
380
- preToolUse: [
381
- { matcher: "", command: guardCmd, failClosed: true }
382
- ],
383
- postToolUse: [
384
- { matcher: "", command: auditCmd }
385
- ]
386
- };
387
- const hooksContent = JSON.stringify({ version: 1, hooks: hookConfig }, null, 2) + "\n";
388
- const userCursorDir = join(homedir(), ".cursor");
389
- if (existsSync(userCursorDir)) {
390
- const userHooksPath = join(userCursorDir, "hooks.json");
391
- const existingUserHooks = existsSync(userHooksPath) ? readFileSync(userHooksPath, "utf-8") : "";
392
- if (hooksContent !== existingUserHooks) {
393
- writeFileSync(userHooksPath, hooksContent);
394
- console.log(` ${existingUserHooks ? "Updated" : "Created"} ${userHooksPath} (user-level)`);
395
- changed = true;
396
- }
397
- }
398
- const hooksPath = join(clientDir, "hooks.json");
399
- const existingHooks = existsSync(hooksPath) ? readFileSync(hooksPath, "utf-8") : "";
400
- if (hooksContent !== existingHooks) {
401
- writeFileSync(hooksPath, hooksContent);
402
- console.log(` ${existingHooks ? "Updated" : "Created"} ${hooksPath}`);
403
- changed = true;
404
- }
405
- if (wrappedMcpConfig) {
406
- const cursorMcpConfig = { mcpServers: {} };
407
- for (const [name, server] of Object.entries(wrappedMcpConfig.mcpServers)) {
408
- const args = [...server.args ?? []];
409
- const agentIdx = args.indexOf("--agent-name");
410
- if (agentIdx >= 0 && agentIdx + 1 < args.length) {
411
- args[agentIdx + 1] = "cursor";
412
- } else {
413
- const sepIdx = args.indexOf("--");
414
- if (sepIdx >= 0) {
415
- args.splice(sepIdx, 0, "--agent-name", "cursor");
416
- }
417
- }
418
- cursorMcpConfig.mcpServers[name] = { ...server, args };
419
- }
420
- const mcpPath = join(clientDir, "mcp.json");
421
- const mcpStr = JSON.stringify(cursorMcpConfig, null, 2) + "\n";
422
- const existingMcp = existsSync(mcpPath) ? readFileSync(mcpPath, "utf-8") : "";
423
- if (mcpStr !== existingMcp) {
424
- writeFileSync(mcpPath, mcpStr);
425
- console.log(` ${existingMcp ? "Updated" : "Created"} ${mcpPath}`);
426
- changed = true;
427
- }
428
- }
429
- const staleSettings = join(clientDir, "settings.json");
430
- if (existsSync(staleSettings)) {
431
- try {
432
- const content = JSON.parse(readFileSync(staleSettings, "utf-8"));
433
- if (content.hooks?.PreToolUse || content.hooks?.PostToolUse) {
434
- writeFileSync(staleSettings, JSON.stringify({}, null, 2) + "\n");
435
- console.log(` Cleaned stale ${staleSettings} (had wrong hook format)`);
436
- changed = true;
437
- }
438
- } catch {
439
- }
440
- }
441
- return changed ? "installed" : "skipped";
442
- }
443
357
  function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
444
358
  const settingsPath = join(clientDir, "settings.json");
445
359
  let existing = {};
@@ -533,11 +447,8 @@ SOLONGATE_API_KEY=sg_live_your_key_here
533
447
  ".mcp.json",
534
448
  ".solongate/**",
535
449
  ".claude/**",
536
- ".cursor/**",
537
450
  ".gemini/**",
538
- ".antigravity/**",
539
- ".openclaw/**",
540
- ".perplexity/**"
451
+ ".openclaw/**"
541
452
  ];
542
453
  if (existsSync(gitignorePath)) {
543
454
  let gitignore = readFileSync(gitignorePath, "utf-8");
@@ -774,8 +685,7 @@ async function main() {
774
685
  console.log(" \u2502 \u2502");
775
686
  console.log(" \u2502 MCP servers \u2192 Protected via proxy \u2502");
776
687
  console.log(" \u2502 AI tools \u2192 Guarded via hooks \u2502");
777
- console.log(" \u2502 Claude, Cursor, Gemini, Antigravity, \u2502");
778
- console.log(" \u2502 OpenClaw, Perplexity \u2502");
688
+ console.log(" \u2502 Claude Code, Gemini, OpenClaw \u2502");
779
689
  console.log(" \u2502 API key \u2192 Set in .env \u2502");
780
690
  console.log(" \u2502 \u2502");
781
691
  console.log(" \u2502 View logs: https://dashboard.solongate.com \u2502");
package/dist/lib.js CHANGED
@@ -3612,8 +3612,8 @@ import {
3612
3612
  ListResourceTemplatesRequestSchema
3613
3613
  } from "@modelcontextprotocol/sdk/types.js";
3614
3614
  import { createServer as createHttpServer } from "http";
3615
- import { resolve as resolve2 } from "path";
3616
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
3615
+ import { resolve as resolve2, join } from "path";
3616
+ import { existsSync as existsSync3, readFileSync as readFileSync3, mkdirSync as mkdirSync2, appendFileSync } from "fs";
3617
3617
 
3618
3618
  // src/config.ts
3619
3619
  import { readFileSync, existsSync } from "fs";
@@ -4255,12 +4255,9 @@ var SolonGateProxy = class {
4255
4255
  /** Normalize well-known MCP client names to display-friendly agent identities */
4256
4256
  normalizeAgentName(raw) {
4257
4257
  const lower = raw.toLowerCase();
4258
- if (lower.includes("cursor")) return { id: "cursor", name: "Cursor" };
4259
4258
  if (lower.includes("claude-code") || lower === "claude code") return { id: "claude-code", name: "Claude Code" };
4260
4259
  if (lower.includes("claude")) return { id: "claude-desktop", name: "Claude Desktop" };
4261
4260
  if (lower.includes("gemini")) return { id: "gemini-cli", name: "Gemini CLI" };
4262
- if (lower.includes("antigravity")) return { id: "antigravity", name: "Antigravity" };
4263
- if (lower.includes("perplexity")) return { id: "perplexity", name: "Perplexity" };
4264
4261
  return { id: raw.toLowerCase().replace(/\s+/g, "-"), name: raw };
4265
4262
  }
4266
4263
  /** Extract sub-agent identity from MCP _meta field */
@@ -4460,6 +4457,20 @@ var SolonGateProxy = class {
4460
4457
  if (this.server) {
4461
4458
  const clientVersion = this.server.getClientVersion();
4462
4459
  log2(`MCP clientInfo raw: ${JSON.stringify(clientVersion)}`);
4460
+ try {
4461
+ const debugInfo = {
4462
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4463
+ clientInfo: clientVersion,
4464
+ agentNameFlag: this.config.agentName ?? null,
4465
+ resolvedAgentId: this.agentId,
4466
+ resolvedAgentName: this.agentName,
4467
+ pid: process.pid
4468
+ };
4469
+ const debugDir = resolve2(".solongate");
4470
+ mkdirSync2(debugDir, { recursive: true });
4471
+ appendFileSync(join(debugDir, ".debug-proxy"), JSON.stringify(debugInfo) + "\n");
4472
+ } catch {
4473
+ }
4463
4474
  if (clientVersion?.name && !this.config.agentName) {
4464
4475
  const normalized = this.normalizeAgentName(clientVersion.name);
4465
4476
  this.agentId = normalized.id;
@@ -4889,7 +4900,7 @@ ${msg.content.text}`;
4889
4900
  /**
4890
4901
  * Start serving downstream.
4891
4902
  * If --port is set, serves via StreamableHTTP on that port.
4892
- * Otherwise, serves on stdio (default for Claude Code / Cursor / etc).
4903
+ * Otherwise, serves on stdio (default for Claude Code / etc).
4893
4904
  */
4894
4905
  async serve() {
4895
4906
  if (!this.server) throw new Error("Server not created");
package/hooks/audit.mjs CHANGED
@@ -26,7 +26,7 @@ const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
26
26
  const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
27
27
 
28
28
  // Agent identity from CLI args: node audit.mjs <agent_id> <agent_name>
29
- // Can be overridden at runtime when stdin contains cursor_version or gemini_version
29
+ // Can be overridden at runtime when stdin contains gemini_version
30
30
  let AGENT_ID = process.argv[2] || 'claude-code';
31
31
  let AGENT_NAME = process.argv[3] || 'Claude Code';
32
32
 
@@ -38,21 +38,22 @@ process.stdin.on('end', async () => {
38
38
  try {
39
39
  const data = JSON.parse(input);
40
40
 
41
- // Debug: dump raw stdin to file for agent detection troubleshooting
42
- try { writeFileSync(resolve('.solongate', '.debug-stdin'), JSON.stringify(data, null, 2)); } catch {}
41
+ // Debug: append raw stdin to file for agent detection troubleshooting
42
+ try {
43
+ const debugLine = JSON.stringify({ ts: new Date().toISOString(), argv: process.argv.slice(2), tool_name: data.tool_name || data.toolName, agent_id: AGENT_ID }) + '\n';
44
+ const { appendFileSync: afs } = await import('node:fs');
45
+ afs(resolve('.solongate', '.debug-audit-log'), debugLine);
46
+ } catch {}
43
47
 
44
48
  // Auto-detect agent from stdin data (overrides CLI args if detected)
45
- if (data.cursor_version) {
46
- AGENT_ID = 'cursor';
47
- AGENT_NAME = 'Cursor';
48
- } else if (data.gemini_version) {
49
+ if (data.gemini_version) {
49
50
  AGENT_ID = 'gemini-cli';
50
51
  AGENT_NAME = 'Gemini CLI';
51
52
  }
52
53
 
53
- // Normalize field names across tools (Claude: tool_name, others may use toolName)
54
- const toolName = data.tool_name || data.toolName || 'unknown';
55
- const toolInput = data.tool_input || data.toolInput || data.params || {};
54
+ let toolName = data.tool_name || data.toolName || '';
55
+ let toolInput = data.tool_input || data.toolInput || data.params || {};
56
+ if (!toolName) toolName = 'unknown';
56
57
 
57
58
  if (toolName === 'Bash' && JSON.stringify(toolInput).includes('audit-logs')) {
58
59
  process.exit(0);
@@ -71,14 +72,15 @@ process.stdin.on('end', async () => {
71
72
  }
72
73
  } catch {}
73
74
 
74
- // Cursor uses result_json, Claude uses tool_response
75
75
  const toolResponse = data.tool_response || data.toolResponse || {};
76
+ const toolOutput = data.tool_output || data.toolOutput || '';
76
77
  const resultJson = data.result_json ? (typeof data.result_json === 'string' ? data.result_json : JSON.stringify(data.result_json)) : '';
77
78
 
78
79
  const hasError = guardDenied ||
79
80
  toolResponse.error ||
80
81
  toolResponse.exitCode > 0 ||
81
82
  toolResponse.isError ||
83
+ (toolOutput && typeof toolOutput === 'string' && toolOutput.includes('"error"')) ||
82
84
  (resultJson && resultJson.includes('"error"'));
83
85
 
84
86
  const argsSummary = {};
package/hooks/guard.mjs CHANGED
@@ -41,40 +41,31 @@ const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
41
41
  const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
42
42
 
43
43
  // Agent identity from CLI args: node guard.mjs <agent_id> <agent_name>
44
- // Can be overridden at runtime when stdin contains cursor_version or gemini_version
44
+ // Can be overridden at runtime when stdin contains gemini_version
45
45
  let AGENT_ID = process.argv[2] || 'claude-code';
46
46
  let AGENT_NAME = process.argv[3] || 'Claude Code';
47
47
 
48
48
  // ── Per-tool block/allow output ──
49
- // Claude Code: exit 2 + stderr = BLOCK, exit 0 = ALLOW
50
- // Cursor: JSON stdout {"permission": "deny", "user_message": "..."} = BLOCK
51
- // Gemini CLI: JSON stdout {"decision": "deny", "reason": "..."} = BLOCK
49
+ // Response format depends on the agent:
50
+ // Claude Code: exit 2 + stderr = BLOCK, exit 0 = ALLOW
51
+ // Gemini CLI: {"decision": "deny/allow", "reason": "..."}
52
+
52
53
  function blockTool(reason) {
53
- if (AGENT_ID === 'cursor') {
54
- // Cursor: JSON stdout with permission: "deny", exit 0
55
- process.stdout.write(JSON.stringify({
56
- permission: 'deny',
57
- user_message: `[SolonGate] ${reason}`,
58
- agent_message: `BLOCKED by SolonGate security policy: ${reason}`,
59
- }));
60
- process.exit(0);
61
- } else if (AGENT_ID === 'gemini-cli') {
54
+ if (AGENT_ID === 'gemini-cli') {
62
55
  process.stdout.write(JSON.stringify({
63
56
  decision: 'deny',
64
57
  reason: `[SolonGate] ${reason}`,
65
58
  }));
66
59
  process.exit(0);
67
60
  } else {
68
- // Claude Code, Antigravity, Perplexity — exit code 2
61
+ // Claude Code — exit code 2
69
62
  process.stderr.write(reason);
70
63
  process.exit(2);
71
64
  }
72
65
  }
73
66
 
74
67
  function allowTool() {
75
- if (AGENT_ID === 'cursor') {
76
- process.stdout.write(JSON.stringify({ permission: 'allow' }));
77
- } else if (AGENT_ID === 'gemini-cli') {
68
+ if (AGENT_ID === 'gemini-cli') {
78
69
  process.stdout.write(JSON.stringify({ decision: 'allow' }));
79
70
  }
80
71
  process.exit(0);
@@ -376,22 +367,30 @@ process.stdin.on('end', async () => {
376
367
  const raw = JSON.parse(input);
377
368
 
378
369
  // Auto-detect agent from stdin data (overrides CLI args if detected)
379
- if (raw.cursor_version) {
380
- AGENT_ID = 'cursor';
381
- AGENT_NAME = 'Cursor';
382
- } else if (raw.gemini_version) {
370
+ if (raw.gemini_version) {
383
371
  AGENT_ID = 'gemini-cli';
384
372
  AGENT_NAME = 'Gemini CLI';
385
373
  }
386
374
 
387
- // Normalize field names across tools (Claude: tool_name/tool_input, others may use toolName/toolInput/params)
375
+ // Debug: append guard invocation to debug log
376
+ try {
377
+ const { appendFileSync: afs, mkdirSync: mds } = await import('node:fs');
378
+ mds(resolve('.solongate'), { recursive: true });
379
+ const debugLine = JSON.stringify({ ts: new Date().toISOString(), hook: 'guard', argv: process.argv.slice(2), tool_name: raw.tool_name || raw.toolName || raw.command, agent_id: AGENT_ID }) + '\n';
380
+ afs(resolve('.solongate', '.debug-guard-log'), debugLine);
381
+ } catch {}
382
+
383
+ let mappedToolName = raw.tool_name || raw.toolName || '';
384
+ let mappedToolInput = raw.tool_input || raw.toolInput || raw.params || {};
385
+
386
+ // Normalize field names across tools
388
387
  const data = {
389
388
  ...raw,
390
- tool_name: raw.tool_name || raw.toolName || '',
391
- tool_input: raw.tool_input || raw.toolInput || raw.params || {},
389
+ tool_name: mappedToolName,
390
+ tool_input: mappedToolInput,
392
391
  tool_response: raw.tool_response || raw.toolResponse || {},
393
392
  cwd: raw.cwd || process.cwd(),
394
- session_id: raw.session_id || raw.sessionId || '',
393
+ session_id: raw.session_id || raw.sessionId || raw.conversation_id || '',
395
394
  };
396
395
  const args = data.tool_input;
397
396
 
@@ -399,7 +398,7 @@ process.stdin.on('end', async () => {
399
398
  // Hardcoded, no bypass possible — runs before policy/PI config
400
399
  // Fully protected: block ALL access (read, write, delete, move)
401
400
  const protectedPaths = [
402
- '.solongate', '.claude', '.cursor', '.gemini', '.antigravity', '.openclaw', '.perplexity',
401
+ '.solongate', '.claude', '.gemini', '.openclaw',
403
402
  'policy.json', '.mcp.json',
404
403
  ];
405
404
  // Write-protected: block write/delete/modify, allow read (cat, grep, head, etc.)
package/hooks/stop.mjs CHANGED
@@ -27,7 +27,7 @@ const API_KEY = process.env.SOLONGATE_API_KEY || dotenv.SOLONGATE_API_KEY || '';
27
27
  const API_URL = process.env.SOLONGATE_API_URL || dotenv.SOLONGATE_API_URL || 'https://api.solongate.com';
28
28
 
29
29
  // Agent identity from CLI args: node stop.mjs <agent_id> <agent_name>
30
- // Can be overridden at runtime when stdin contains cursor_version or gemini_version
30
+ // Can be overridden at runtime when stdin contains gemini_version
31
31
  let AGENT_ID = process.argv[2] || 'claude-code';
32
32
  let AGENT_NAME = process.argv[3] || 'Claude Code';
33
33
 
@@ -46,8 +46,7 @@ process.stdin.on('end', async () => {
46
46
  // Auto-detect agent from stdin data
47
47
  try {
48
48
  const raw = JSON.parse(input);
49
- if (raw.cursor_version) { AGENT_ID = 'cursor'; AGENT_NAME = 'Cursor'; }
50
- else if (raw.gemini_version) { AGENT_ID = 'gemini-cli'; AGENT_NAME = 'Gemini CLI'; }
49
+ if (raw.gemini_version) { AGENT_ID = 'gemini-cli'; AGENT_NAME = 'Gemini CLI'; }
51
50
  } catch {}
52
51
 
53
52
  // Check if tool calls were made in this turn
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.31.0",
3
+ "version": "0.33.0",
4
4
  "description": "AI tool security proxy — protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. Zero code changes required.",
5
5
  "type": "module",
6
6
  "bin": {