agent-gate-installer 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/install.mjs CHANGED
@@ -9,6 +9,7 @@ import { homedir, platform } from "node:os";
9
9
  const CLAUDE_HOME = join(homedir(), ".claude");
10
10
  const INSTALL_DIR = join(CLAUDE_HOME, "agent-gate");
11
11
  const SETTINGS_PATH = join(CLAUDE_HOME, "settings.json");
12
+ const CLAUDE_JSON_PATH = join(homedir(), ".claude.json");
12
13
  const REPO_URL = "https://github.com/jl-cmd/agent-gate.git";
13
14
  const MINIMUM_PYTHON_VERSION = [3, 11];
14
15
  const IS_WINDOWS = platform() === "win32";
@@ -129,7 +130,35 @@ function registerMcpServer(settings, venvPython) {
129
130
  command: venvPython,
130
131
  args: [join(INSTALL_DIR, "src", "agent_gate", "server.py")],
131
132
  };
132
- log(" + Registered agent-gate MCP server");
133
+ log(" + Registered agent-gate MCP server in settings.json");
134
+ }
135
+
136
+ function readClaudeJson() {
137
+ if (!existsSync(CLAUDE_JSON_PATH)) return {};
138
+ const raw = readFileSync(CLAUDE_JSON_PATH, "utf8");
139
+ try {
140
+ return JSON.parse(raw);
141
+ } catch {
142
+ log(` ! ${CLAUDE_JSON_PATH} contains malformed JSON, skipping MCP registration there.`);
143
+ return null;
144
+ }
145
+ }
146
+
147
+ function writeClaudeJson(data) {
148
+ writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(data, null, 4) + "\n", "utf8");
149
+ }
150
+
151
+ function registerMcpInClaudeJson(venvPython) {
152
+ const data = readClaudeJson();
153
+ if (data === null) return;
154
+ data.mcpServers = data.mcpServers || {};
155
+ data.mcpServers["agent-gate"] = {
156
+ type: "stdio",
157
+ command: venvPython,
158
+ args: [join(INSTALL_DIR, "src", "agent_gate", "server.py")],
159
+ };
160
+ writeClaudeJson(data);
161
+ log(" + Registered agent-gate MCP server in ~/.claude.json");
133
162
  }
134
163
 
135
164
  function writeMcpConfigFile(venvPython) {
@@ -183,6 +212,7 @@ async function install() {
183
212
  const venvPython = getVenvPython(venvDirectory);
184
213
 
185
214
  writeMcpConfigFile(venvPython);
215
+ registerMcpInClaudeJson(venvPython);
186
216
 
187
217
  if (existsSync(join(INSTALL_DIR, ".git"))) {
188
218
  log(" ~ agent-gate already cloned, pulling latest...");
@@ -1,21 +1,38 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Tests for writeMcpConfigFile behavior in install.mjs.
4
+ * Tests for install.mjs MCP registration behavior.
5
5
  *
6
- * Verifies that /tmp/agent-gate-mcp-config.json is written even when
7
- * earlier install steps (clone, venv, pip) fail.
6
+ * Verifies that:
7
+ * - /tmp/agent-gate-mcp-config.json is written even when install fails early
8
+ * - ~/.claude.json gets mcpServers entry for agent-gate
8
9
  */
9
10
 
10
11
  import { existsSync, readFileSync, rmSync, writeFileSync, mkdirSync } from "node:fs";
11
12
  import { join } from "node:path";
12
13
  import { execSync } from "node:child_process";
14
+ import { homedir } from "node:os";
13
15
 
14
16
  const MCP_CONFIG_PATH = "/tmp/agent-gate-mcp-config.json";
17
+ const CLAUDE_JSON_PATH = join(homedir(), ".claude.json");
15
18
  let passed = 0;
16
19
  let failed = 0;
17
20
 
18
- function cleanup() {
21
+ let claudeJsonBackup = null;
22
+
23
+ function backupClaudeJson() {
24
+ if (existsSync(CLAUDE_JSON_PATH)) {
25
+ claudeJsonBackup = readFileSync(CLAUDE_JSON_PATH, "utf8");
26
+ }
27
+ }
28
+
29
+ function restoreClaudeJson() {
30
+ if (claudeJsonBackup !== null) {
31
+ writeFileSync(CLAUDE_JSON_PATH, claudeJsonBackup, "utf8");
32
+ }
33
+ }
34
+
35
+ function cleanupMcpConfig() {
19
36
  try { rmSync(MCP_CONFIG_PATH); } catch { /* ok */ }
20
37
  }
21
38
 
@@ -30,10 +47,9 @@ function assert(condition, message) {
30
47
  }
31
48
 
32
49
  // --- Test 1: writeMcpConfigFile writes correct content ---
33
- console.log("\nTest 1: writeMcpConfigFile writes correct JSON");
34
- cleanup();
50
+ console.log("\nTest 1: writeMcpConfigFile writes correct JSON to /tmp");
51
+ cleanupMcpConfig();
35
52
  {
36
- // Inline the function logic to test it in isolation
37
53
  const venvPython = "/root/.claude/agent-gate/.venv/bin/python";
38
54
  const INSTALL_DIR = "/root/.claude/agent-gate";
39
55
  const config = {
@@ -58,14 +74,12 @@ cleanup();
58
74
  "Args point to server.py"
59
75
  );
60
76
  }
61
- cleanup();
77
+ cleanupMcpConfig();
62
78
 
63
- // --- Test 2: Simulate install failure MCP config should still be written ---
64
- console.log("\nTest 2: MCP config written even when install exits early");
65
- cleanup();
79
+ // --- Test 2: MCP config written even when install exits early ---
80
+ console.log("\nTest 2: /tmp MCP config written even when install exits early");
81
+ cleanupMcpConfig();
66
82
  {
67
- // Run the installer with a bad GH_TOKEN in non-interactive mode.
68
- // The clone step will fail, but we want the MCP config to exist.
69
83
  try {
70
84
  execSync(
71
85
  `GH_TOKEN=bad_token node "${join(import.meta.dirname, "install.mjs")}" --non-interactive`,
@@ -77,38 +91,73 @@ cleanup();
77
91
 
78
92
  assert(
79
93
  existsSync(MCP_CONFIG_PATH),
80
- "MCP config file exists after failed install (written before clone)"
94
+ "/tmp MCP config exists after failed install (written before clone)"
81
95
  );
82
96
  }
83
- cleanup();
97
+ cleanupMcpConfig();
84
98
 
85
- // --- Test 3: Simulate install with existing clone but broken pip ---
86
- console.log("\nTest 3: MCP config written even when pip install fails");
87
- cleanup();
99
+ // --- Test 3: ~/.claude.json gets mcpServers on failed install ---
100
+ console.log("\nTest 3: ~/.claude.json gets mcpServers even when install fails");
101
+ backupClaudeJson();
102
+ cleanupMcpConfig();
88
103
  {
89
- // Create a fake INSTALL_DIR so clone is skipped, but venv/pip will fail
90
- const fakeInstallDir = "/tmp/agent-gate-test-install";
91
- try { rmSync(fakeInstallDir, { recursive: true }); } catch { /* ok */ }
92
- mkdirSync(join(fakeInstallDir, ".git"), { recursive: true });
93
-
94
104
  try {
95
105
  execSync(
96
- `HOME=/tmp/agent-gate-test-home GH_TOKEN=x node "${join(import.meta.dirname, "install.mjs")}" --non-interactive`,
97
- { encoding: "utf8", stdio: "pipe", timeout: 30000, env: { ...process.env, HOME: "/tmp/agent-gate-test-home" } }
106
+ `GH_TOKEN=bad_token node "${join(import.meta.dirname, "install.mjs")}" --non-interactive`,
107
+ { encoding: "utf8", stdio: "pipe", timeout: 30000 }
98
108
  );
99
109
  } catch {
100
- // Expected — install fails at venv/pip step
110
+ // Expected — install fails at clone step
101
111
  }
102
112
 
113
+ let claudeJsonHasMcp = false;
114
+ try {
115
+ const data = JSON.parse(readFileSync(CLAUDE_JSON_PATH, "utf8"));
116
+ claudeJsonHasMcp = data.mcpServers?.["agent-gate"]?.type === "stdio";
117
+ } catch { /* ok */ }
118
+
103
119
  assert(
104
- existsSync(MCP_CONFIG_PATH),
105
- "MCP config file exists after pip failure (written before pip)"
120
+ claudeJsonHasMcp,
121
+ "~/.claude.json has agent-gate mcpServers entry after failed install"
106
122
  );
123
+ }
124
+ restoreClaudeJson();
125
+ cleanupMcpConfig();
126
+
127
+ // --- Test 4: ~/.claude.json preserves existing data ---
128
+ console.log("\nTest 4: ~/.claude.json preserves existing data when adding mcpServers");
129
+ backupClaudeJson();
130
+ cleanupMcpConfig();
131
+ {
132
+ // Write a known state to ~/.claude.json
133
+ const existing = { someExistingKey: "preserved", mcpServers: { "other-server": { type: "http" } } };
134
+ writeFileSync(CLAUDE_JSON_PATH, JSON.stringify(existing, null, 4) + "\n", "utf8");
135
+
136
+ try {
137
+ execSync(
138
+ `GH_TOKEN=bad_token node "${join(import.meta.dirname, "install.mjs")}" --non-interactive`,
139
+ { encoding: "utf8", stdio: "pipe", timeout: 30000 }
140
+ );
141
+ } catch {
142
+ // Expected
143
+ }
144
+
145
+ let preserved = false;
146
+ let hasAgentGate = false;
147
+ let hasOtherServer = false;
148
+ try {
149
+ const data = JSON.parse(readFileSync(CLAUDE_JSON_PATH, "utf8"));
150
+ preserved = data.someExistingKey === "preserved";
151
+ hasAgentGate = data.mcpServers?.["agent-gate"]?.type === "stdio";
152
+ hasOtherServer = data.mcpServers?.["other-server"]?.type === "http";
153
+ } catch { /* ok */ }
107
154
 
108
- try { rmSync(fakeInstallDir, { recursive: true }); } catch { /* ok */ }
109
- try { rmSync("/tmp/agent-gate-test-home", { recursive: true }); } catch { /* ok */ }
155
+ assert(preserved, "Existing keys preserved in ~/.claude.json");
156
+ assert(hasAgentGate, "agent-gate MCP server added");
157
+ assert(hasOtherServer, "Pre-existing MCP server not removed");
110
158
  }
111
- cleanup();
159
+ restoreClaudeJson();
160
+ cleanupMcpConfig();
112
161
 
113
162
  // --- Summary ---
114
163
  console.log(`\n${passed} passed, ${failed} failed\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-gate-installer",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "One-command installer for agent-gate prompt evaluation gate",
5
5
  "type": "module",
6
6
  "bin": {