agent-gate-installer 1.4.0 → 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) {
@@ -179,6 +208,12 @@ async function install() {
179
208
 
180
209
  mkdirSync(CLAUDE_HOME, { recursive: true });
181
210
 
211
+ const venvDirectory = join(INSTALL_DIR, ".venv");
212
+ const venvPython = getVenvPython(venvDirectory);
213
+
214
+ writeMcpConfigFile(venvPython);
215
+ registerMcpInClaudeJson(venvPython);
216
+
182
217
  if (existsSync(join(INSTALL_DIR, ".git"))) {
183
218
  log(" ~ agent-gate already cloned, pulling latest...");
184
219
  try {
@@ -199,9 +234,6 @@ async function install() {
199
234
  }
200
235
  }
201
236
 
202
- const venvDirectory = join(INSTALL_DIR, ".venv");
203
- const venvPython = getVenvPython(venvDirectory);
204
-
205
237
  if (existsSync(venvDirectory)) {
206
238
  log(" ~ venv already exists");
207
239
  } else {
@@ -236,8 +268,6 @@ async function install() {
236
268
  registerMcpServer(settings, venvPython);
237
269
  writeSettings(settings);
238
270
 
239
- writeMcpConfigFile(venvPython);
240
-
241
271
  log(" + Verifying installation...");
242
272
  try {
243
273
  run(`"${venvPython}" -c "from agent_gate.server import create_mcp; print('OK')"`);
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Tests for install.mjs MCP registration behavior.
5
+ *
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
9
+ */
10
+
11
+ import { existsSync, readFileSync, rmSync, writeFileSync, mkdirSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { execSync } from "node:child_process";
14
+ import { homedir } from "node:os";
15
+
16
+ const MCP_CONFIG_PATH = "/tmp/agent-gate-mcp-config.json";
17
+ const CLAUDE_JSON_PATH = join(homedir(), ".claude.json");
18
+ let passed = 0;
19
+ let failed = 0;
20
+
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() {
36
+ try { rmSync(MCP_CONFIG_PATH); } catch { /* ok */ }
37
+ }
38
+
39
+ function assert(condition, message) {
40
+ if (condition) {
41
+ console.log(` PASS: ${message}`);
42
+ passed++;
43
+ } else {
44
+ console.log(` FAIL: ${message}`);
45
+ failed++;
46
+ }
47
+ }
48
+
49
+ // --- Test 1: writeMcpConfigFile writes correct content ---
50
+ console.log("\nTest 1: writeMcpConfigFile writes correct JSON to /tmp");
51
+ cleanupMcpConfig();
52
+ {
53
+ const venvPython = "/root/.claude/agent-gate/.venv/bin/python";
54
+ const INSTALL_DIR = "/root/.claude/agent-gate";
55
+ const config = {
56
+ mcpServers: {
57
+ "agent-gate": {
58
+ type: "stdio",
59
+ command: venvPython,
60
+ args: [join(INSTALL_DIR, "src", "agent_gate", "server.py")],
61
+ },
62
+ },
63
+ };
64
+ writeFileSync(MCP_CONFIG_PATH, JSON.stringify(config, null, 4) + "\n", "utf8");
65
+
66
+ assert(existsSync(MCP_CONFIG_PATH), "File exists at /tmp/agent-gate-mcp-config.json");
67
+
68
+ const content = JSON.parse(readFileSync(MCP_CONFIG_PATH, "utf8"));
69
+ assert(content.mcpServers?.["agent-gate"] !== undefined, "Has agent-gate MCP server entry");
70
+ assert(content.mcpServers["agent-gate"].type === "stdio", "Type is stdio");
71
+ assert(content.mcpServers["agent-gate"].command === venvPython, "Command points to venv python");
72
+ assert(
73
+ content.mcpServers["agent-gate"].args[0].endsWith("server.py"),
74
+ "Args point to server.py"
75
+ );
76
+ }
77
+ cleanupMcpConfig();
78
+
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();
82
+ {
83
+ try {
84
+ execSync(
85
+ `GH_TOKEN=bad_token node "${join(import.meta.dirname, "install.mjs")}" --non-interactive`,
86
+ { encoding: "utf8", stdio: "pipe", timeout: 30000 }
87
+ );
88
+ } catch {
89
+ // Expected — install fails at clone step
90
+ }
91
+
92
+ assert(
93
+ existsSync(MCP_CONFIG_PATH),
94
+ "/tmp MCP config exists after failed install (written before clone)"
95
+ );
96
+ }
97
+ cleanupMcpConfig();
98
+
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();
103
+ {
104
+ try {
105
+ execSync(
106
+ `GH_TOKEN=bad_token node "${join(import.meta.dirname, "install.mjs")}" --non-interactive`,
107
+ { encoding: "utf8", stdio: "pipe", timeout: 30000 }
108
+ );
109
+ } catch {
110
+ // Expected — install fails at clone step
111
+ }
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
+
119
+ assert(
120
+ claudeJsonHasMcp,
121
+ "~/.claude.json has agent-gate mcpServers entry after failed install"
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 */ }
154
+
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");
158
+ }
159
+ restoreClaudeJson();
160
+ cleanupMcpConfig();
161
+
162
+ // --- Summary ---
163
+ console.log(`\n${passed} passed, ${failed} failed\n`);
164
+ process.exit(failed > 0 ? 1 : 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-gate-installer",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "One-command installer for agent-gate prompt evaluation gate",
5
5
  "type": "module",
6
6
  "bin": {