@insforge/install 0.0.50 → 0.0.52

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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js CHANGED
@@ -1,33 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- clientNames,
4
- logger,
5
- readConfig,
6
- writeConfig
7
- } from "./utils.js";
8
- import { execSync } from 'child_process';
9
- import os from 'os';
10
- import path from 'path';
11
-
12
- import yargs from "yargs";
13
- import { hideBin } from "yargs/helpers";
14
-
15
- import pc from "picocolors";
16
- import select from "@inquirer/select";
17
- var { green, red, yellow, cyan } = pc;
18
-
19
- // Section header helper
20
- const LINE = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
21
- function printHeader(text) {
2
+ import { clientNames, logger, readConfig, writeConfig } from "./utils.js";
3
+ import { execSync } from 'child_process';
4
+ import os from 'os';
5
+ import path from 'path';
6
+ import yargs from "yargs";
7
+ import { hideBin } from "yargs/helpers";
8
+ import pc from "picocolors";
9
+ import select from "@inquirer/select";
10
+ const { green, red, yellow, cyan } = pc;
11
+ const LINE = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
12
+ function printHeader(text) {
22
13
  console.log();
23
14
  console.log(LINE);
24
15
  console.log(` ${text}`);
25
16
  console.log(LINE);
26
17
  console.log();
27
- }
28
-
29
- // ASCII art logo for InsForge
30
- const INSFORGE_LOGO = `
18
+ }
19
+ const INSFORGE_LOGO = `
31
20
  ██╗███╗ ██╗███████╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗
32
21
  ██║████╗ ██║██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝
33
22
  ██║██╔██╗ ██║███████╗█████╗ ██║ ██║██████╔╝██║ ███╗█████╗
@@ -35,9 +24,7 @@ import {
35
24
  ██║██║ ╚████║███████║██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗
36
25
  ╚═╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
37
26
  `;
38
-
39
- // Map client names to display names for better UX
40
- const clientDisplayNames = {
27
+ const clientDisplayNames = {
41
28
  "claude-code": "Claude Code",
42
29
  "cursor": "Cursor",
43
30
  "windsurf": "Windsurf",
@@ -48,10 +35,10 @@ import {
48
35
  "copilot": "GitHub Copilot",
49
36
  "qoder": "Qoder",
50
37
  "antigravity": "Antigravity",
51
- "kiro": "Kiro"
52
- };
53
-
54
- function printPostInstallMessage(clientName) {
38
+ "kiro": "Kiro",
39
+ "opencode": "OpenCode"
40
+ };
41
+ function printPostInstallMessage(clientName) {
55
42
  const displayName = clientDisplayNames[clientName] || clientName;
56
43
  console.log(INSFORGE_LOGO);
57
44
  console.log(green(`✓ InsForge MCP is now configured for ${cyan(displayName)}!`));
@@ -71,305 +58,301 @@ import {
71
58
  console.log(' 💬 Discord: https://discord.com/invite/MPxwj5xVvW');
72
59
  console.log(' ⭐ GitHub: https://github.com/insforge/insforge');
73
60
  console.log();
74
- }
75
- function builder(yargs2) {
76
- return yargs2.option("client", {
77
- type: "string",
78
- description: "Client to use for installation",
79
- demandOption: false,
80
- choices: clientNames
61
+ }
62
+ function builder(y) {
63
+ return y.option("client", {
64
+ type: "string",
65
+ description: "Client to use for installation",
66
+ demandOption: false,
67
+ choices: clientNames
81
68
  }).option("env", {
82
- type: "string",
83
- description: "Environment variables as key=value pairs (can be used multiple times). API_KEY is required.",
84
- array: true
69
+ type: "string",
70
+ description: "Environment variables as key=value pairs (can be used multiple times). API_KEY is required.",
71
+ array: true
85
72
  }).option("dev", {
86
- type: "boolean",
87
- description: "Install dev version (@insforge/mcp@dev) instead of latest",
88
- default: false
73
+ type: "boolean",
74
+ description: "Install dev version (@insforge/mcp@dev) instead of latest",
75
+ default: false
89
76
  });
90
- }
91
-
92
- async function handler(argv) {
77
+ }
78
+ async function handler(argv) {
93
79
  let selectedClient = argv.client;
94
-
95
- // If no client specified, show interactive selection
96
80
  if (!selectedClient) {
97
- console.log();
98
- console.log(LINE);
99
- console.log(` ${cyan('InsForge MCP Installer')}`);
100
- console.log(LINE);
101
- console.log();
102
-
103
- selectedClient = await select({
104
- message: 'Select your AI coding client:',
105
- choices: clientNames.map(name => ({
106
- name: clientDisplayNames[name] || name,
107
- value: name
108
- }))
109
- });
81
+ console.log();
82
+ console.log(LINE);
83
+ console.log(` ${cyan('InsForge MCP Installer')}`);
84
+ console.log(LINE);
85
+ console.log();
86
+ selectedClient = await select({
87
+ message: 'Select your coding agent:',
88
+ pageSize: 15,
89
+ loop: false,
90
+ choices: clientNames.map(name => ({
91
+ name: clientDisplayNames[name] || name,
92
+ value: name
93
+ }))
94
+ });
110
95
  }
111
-
112
- // Validate client
113
96
  if (!clientNames.includes(selectedClient)) {
114
- logger.error(`Invalid client: ${selectedClient}. Available clients: ${clientNames.join(", ")}`);
115
- return;
97
+ logger.error(`Invalid client: ${selectedClient}. Available clients: ${clientNames.join(", ")}`);
98
+ return;
116
99
  }
117
-
118
- // Update argv.client with selected value for downstream use
119
100
  argv.client = selectedClient;
120
101
  const envVars = {};
121
102
  if (argv.env && argv.env.length > 0) {
122
- for (const envVar of argv.env) {
123
- const [key, ...valueParts] = envVar.split("=");
124
- if (key && valueParts.length > 0) {
125
- envVars[key] = valueParts.join("=");
126
- } else {
127
- logger.warn(`Invalid environment variable format: ${envVar}. Expected KEY=VALUE format.`);
103
+ for (const envVar of argv.env) {
104
+ const [key, ...valueParts] = envVar.split("=");
105
+ if (key && valueParts.length > 0) {
106
+ envVars[key] = valueParts.join("=");
107
+ }
108
+ else {
109
+ logger.warn(`Invalid environment variable format: ${envVar}. Expected KEY=VALUE format.`);
110
+ }
128
111
  }
129
- }
130
112
  }
131
113
  if (!envVars.API_KEY) {
132
- logger.error("API_KEY environment variable is required. Use --env API_KEY=your_key");
133
- return;
114
+ logger.error("API_KEY environment variable is required. Use --env API_KEY=your_key");
115
+ return;
134
116
  }
135
- // Add default base URL if not provided
136
117
  if (!envVars.API_BASE_URL) {
137
- envVars.API_BASE_URL = "http://localhost:7130";
118
+ envVars.API_BASE_URL = "http://localhost:7130";
138
119
  }
139
120
  const name = "insforge";
140
121
  const mcpVersion = argv.dev ? "@insforge/mcp@dev" : "@insforge/mcp@latest";
141
122
  try {
142
- printHeader('InsForge MCP Installer');
143
- logger.info(`Setting up MCP for ${cyan(argv.client)}...`);
144
-
145
- printHeader(`Configuring ${argv.client}`);
146
- const config = readConfig(argv.client);
147
-
148
- // Handle different config structures for different clients
123
+ printHeader('InsForge MCP Installer');
124
+ logger.info(`Setting up MCP for ${cyan(argv.client)}...`);
125
+ printHeader(`Configuring ${argv.client}`);
126
+ const config = readConfig(argv.client);
149
127
  if (argv.client === "claude-code") {
150
- // Claude Code uses mcpServers at root level
151
- if (!config.mcpServers) config.mcpServers = {};
152
- const isWindows = process.platform === 'win32';
153
- config.mcpServers[name] = isWindows ? {
154
- command: "cmd",
155
- args: ["/c", "npx", "-y", mcpVersion],
156
- env: {
157
- API_KEY: envVars.API_KEY,
158
- API_BASE_URL: envVars.API_BASE_URL
128
+ if (!config.mcpServers)
129
+ config.mcpServers = {};
130
+ const isWindows = process.platform === 'win32';
131
+ config.mcpServers[name] = isWindows ? {
132
+ command: "cmd",
133
+ args: ["/c", "npx", "-y", mcpVersion],
134
+ env: {
135
+ API_KEY: envVars.API_KEY,
136
+ API_BASE_URL: envVars.API_BASE_URL
137
+ }
138
+ } : {
139
+ command: "npx",
140
+ args: ["-y", mcpVersion],
141
+ env: {
142
+ API_KEY: envVars.API_KEY,
143
+ API_BASE_URL: envVars.API_BASE_URL
144
+ }
145
+ };
146
+ writeConfig(config, argv.client);
147
+ const fs = await import('fs');
148
+ const claudeDir = path.join(process.cwd(), '.claude');
149
+ const settingsPath = path.join(claudeDir, 'settings.local.json');
150
+ if (!fs.existsSync(claudeDir)) {
151
+ fs.mkdirSync(claudeDir, { recursive: true });
159
152
  }
160
- } : {
161
- command: "npx",
162
- args: ["-y", mcpVersion],
163
- env: {
164
- API_KEY: envVars.API_KEY,
165
- API_BASE_URL: envVars.API_BASE_URL
153
+ let settings = {};
154
+ if (fs.existsSync(settingsPath)) {
155
+ try {
156
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
157
+ }
158
+ catch (error) {
159
+ logger.warn(`Could not parse existing settings.local.json: ${error.message}`);
160
+ }
166
161
  }
167
- };
168
- writeConfig(config, argv.client);
169
-
170
- // Create or update .claude/settings.local.json with enableAllProjectMcpServers
171
- const fs = await import('fs');
172
- const claudeDir = path.join(process.cwd(), '.claude');
173
- const settingsPath = path.join(claudeDir, 'settings.local.json');
174
-
175
- if (!fs.existsSync(claudeDir)) {
176
- fs.mkdirSync(claudeDir, { recursive: true });
177
- }
178
-
179
- let settings = {};
180
- if (fs.existsSync(settingsPath)) {
181
- try {
182
- settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
183
- } catch (error) {
184
- logger.warn(`Could not parse existing settings.local.json: ${error.message}`);
162
+ if (!settings.enabledMcpjsonServers) {
163
+ settings.enabledMcpjsonServers = [];
185
164
  }
186
- }
187
-
188
- // Add "insforge" to enabledMcpjsonServers if not already present
189
- if (!settings.enabledMcpjsonServers) {
190
- settings.enabledMcpjsonServers = [];
191
- }
192
- if (!settings.enabledMcpjsonServers.includes("insforge")) {
193
- settings.enabledMcpjsonServers.push("insforge");
194
- }
195
-
196
- // Remove "insforge" from disabledMcpjsonServers if present
197
- if (settings.disabledMcpjsonServers && Array.isArray(settings.disabledMcpjsonServers)) {
198
- settings.disabledMcpjsonServers = settings.disabledMcpjsonServers.filter(
199
- (server) => server !== "insforge"
200
- );
201
- }
202
-
203
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
204
- logger.info("Added \"insforge\" to enabledMcpjsonServers in .claude/settings.local.json");
205
- } else if (argv.client === "codex") {
206
- // Use Codex CLI for Codex
207
- const homeDir = os.homedir();
208
- const isWindows = process.platform === 'win32';
209
- const codexPath = isWindows
210
- ? path.join(homeDir, 'AppData', 'Roaming', 'npm', 'codex.cmd')
211
- : 'codex';
212
-
213
- const envArgs = Object.entries(envVars)
214
- .filter(([key]) => key !== 'CLIENT_NAME')
215
- .map(([key, value]) => `--env ${key}=${value}`)
216
- .join(' ');
217
-
218
- try {
219
- // First try to remove existing insforge MCP if it exists
165
+ if (!settings.enabledMcpjsonServers.includes("insforge")) {
166
+ settings.enabledMcpjsonServers.push("insforge");
167
+ }
168
+ if (settings.disabledMcpjsonServers && Array.isArray(settings.disabledMcpjsonServers)) {
169
+ settings.disabledMcpjsonServers = settings.disabledMcpjsonServers.filter((server) => server !== "insforge");
170
+ }
171
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
172
+ logger.info("Added \"insforge\" to enabledMcpjsonServers in .claude/settings.local.json");
173
+ }
174
+ else if (argv.client === "codex") {
175
+ const homeDir = os.homedir();
176
+ const isWindows = process.platform === 'win32';
177
+ const codexPath = isWindows
178
+ ? path.join(homeDir, 'AppData', 'Roaming', 'npm', 'codex.cmd')
179
+ : 'codex';
180
+ const envArgs = Object.entries(envVars)
181
+ .filter(([key]) => key !== 'CLIENT_NAME')
182
+ .map(([key, value]) => `--env ${key}=${value}`)
183
+ .join(' ');
220
184
  try {
221
- const removeCmd = isWindows ? `"${codexPath}" mcp remove ${name}` : `codex mcp remove ${name}`;
222
- execSync(removeCmd, {
223
- stdio: 'pipe',
224
- shell: true
225
- });
226
- logger.info("Removed existing insforge MCP installation.");
227
- } catch (removeError) {
228
- // It's okay if remove fails - the MCP might not exist
229
- logger.info("No existing insforge MCP found");
185
+ try {
186
+ const removeCmd = isWindows ? `"${codexPath}" mcp remove ${name}` : `codex mcp remove ${name}`;
187
+ execSync(removeCmd, { stdio: 'pipe' });
188
+ logger.info("Removed existing insforge MCP installation.");
189
+ }
190
+ catch {
191
+ logger.info("No existing insforge MCP found");
192
+ }
193
+ const command = isWindows
194
+ ? `"${codexPath}" mcp add ${name} ${envArgs} -- npx -y ${mcpVersion}`
195
+ : `codex mcp add ${name} ${envArgs} -- npx -y ${mcpVersion}`;
196
+ logger.info(`Adding insforge MCP server (${mcpVersion})...`);
197
+ execSync(command, { stdio: 'inherit' });
230
198
  }
231
-
232
- // Now add the MCP server using codex CLI with --env flags
233
- const command = isWindows
234
- ? `"${codexPath}" mcp add ${name} ${envArgs} -- npx -y ${mcpVersion}`
235
- : `codex mcp add ${name} ${envArgs} -- npx -y ${mcpVersion}`;
236
- logger.info(`Adding insforge MCP server (${mcpVersion})...`);
237
- execSync(command, {
238
- stdio: 'inherit',
239
- shell: true
240
- });
241
- } catch (error) {
242
- throw new Error(`Failed to add MCP server via Codex CLI: ${error.message}`);
243
- }
244
- } else if (argv.client === "cursor") {
245
- // Cursor uses mcpServers at root level
246
- if (!config.mcpServers) config.mcpServers = {};
247
- config.mcpServers[name] = {
248
- command: "npx",
249
- args: ["-y", mcpVersion],
250
- env: {
251
- API_KEY: envVars.API_KEY,
252
- API_BASE_URL: envVars.API_BASE_URL
199
+ catch (error) {
200
+ throw new Error(`Failed to add MCP server via Codex CLI: ${error.message}`);
253
201
  }
254
- };
255
- writeConfig(config, argv.client);
256
- } else if (argv.client === "windsurf") {
257
- if (!config.mcpServers) config.mcpServers = {};
258
- config.mcpServers[name] = {
259
- command: "npx",
260
- args: ["-y", mcpVersion],
261
- env: {
262
- API_KEY: envVars.API_KEY,
263
- API_BASE_URL: envVars.API_BASE_URL
202
+ }
203
+ else if (argv.client === "cursor") {
204
+ if (!config.mcpServers)
205
+ config.mcpServers = {};
206
+ config.mcpServers[name] = {
207
+ command: "npx",
208
+ args: ["-y", mcpVersion],
209
+ env: {
210
+ API_KEY: envVars.API_KEY,
211
+ API_BASE_URL: envVars.API_BASE_URL
212
+ }
213
+ };
214
+ writeConfig(config, argv.client);
215
+ }
216
+ else if (argv.client === "windsurf") {
217
+ if (!config.mcpServers)
218
+ config.mcpServers = {};
219
+ config.mcpServers[name] = {
220
+ command: "npx",
221
+ args: ["-y", mcpVersion],
222
+ env: {
223
+ API_KEY: envVars.API_KEY,
224
+ API_BASE_URL: envVars.API_BASE_URL
225
+ }
226
+ };
227
+ writeConfig(config, argv.client);
228
+ }
229
+ else if (argv.client === "cline" || argv.client === "roocode" || argv.client === "trae" || argv.client === "qoder" || argv.client === "kiro") {
230
+ if (!config.mcpServers)
231
+ config.mcpServers = {};
232
+ config.mcpServers[name] = {
233
+ command: "npx",
234
+ args: ["-y", mcpVersion],
235
+ env: {
236
+ API_KEY: envVars.API_KEY,
237
+ API_BASE_URL: envVars.API_BASE_URL
238
+ }
239
+ };
240
+ writeConfig(config, argv.client);
241
+ }
242
+ else if (argv.client === "copilot") {
243
+ const fs = await import('fs');
244
+ const vscodeDir = path.join(process.cwd(), '.vscode');
245
+ const mcpConfigPath = path.join(vscodeDir, 'mcp.json');
246
+ if (!fs.existsSync(vscodeDir)) {
247
+ fs.mkdirSync(vscodeDir, { recursive: true });
264
248
  }
265
- };
266
- writeConfig(config, argv.client);
267
- } else if (argv.client === "cline" || argv.client === "roocode" || argv.client === "trae" || argv.client === "qoder" || argv.client === "kiro") {
268
- if (!config.mcpServers) config.mcpServers = {};
269
- config.mcpServers[name] = {
270
- command: "npx",
271
- args: ["-y", mcpVersion],
272
- env: {
273
- API_KEY: envVars.API_KEY,
274
- API_BASE_URL: envVars.API_BASE_URL
249
+ let copilotConfig = { servers: {} };
250
+ if (fs.existsSync(mcpConfigPath)) {
251
+ try {
252
+ copilotConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
253
+ if (!copilotConfig.servers)
254
+ copilotConfig.servers = {};
255
+ }
256
+ catch (error) {
257
+ logger.warn(`Could not parse existing mcp.json: ${error.message}`);
258
+ copilotConfig = { servers: {} };
259
+ }
275
260
  }
276
- };
277
- writeConfig(config, argv.client);
278
- } else if (argv.client === "copilot") {
279
- // Copilot uses "servers" instead of "mcpServers"
280
- const fs = await import('fs');
281
- const vscodeDir = path.join(process.cwd(), '.vscode');
282
- const mcpConfigPath = path.join(vscodeDir, 'mcp.json');
283
-
284
- if (!fs.existsSync(vscodeDir)) {
285
- fs.mkdirSync(vscodeDir, { recursive: true });
286
- }
287
-
288
- let copilotConfig = { servers: {} };
289
- if (fs.existsSync(mcpConfigPath)) {
290
- try {
291
- copilotConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
292
- if (!copilotConfig.servers) copilotConfig.servers = {};
293
- } catch (error) {
294
- logger.warn(`Could not parse existing mcp.json: ${error.message}`);
295
- copilotConfig = { servers: {} };
261
+ copilotConfig.servers[name] = {
262
+ command: "npx",
263
+ args: ["-y", mcpVersion],
264
+ env: {
265
+ API_KEY: envVars.API_KEY,
266
+ API_BASE_URL: envVars.API_BASE_URL
267
+ }
268
+ };
269
+ fs.writeFileSync(mcpConfigPath, JSON.stringify(copilotConfig, null, 2));
270
+ logger.info(`Configured Copilot MCP at: ${mcpConfigPath}`);
271
+ }
272
+ else if (argv.client === "antigravity") {
273
+ if (!config.mcpServers)
274
+ config.mcpServers = {};
275
+ config.mcpServers[name] = {
276
+ command: "npx",
277
+ args: [
278
+ "-y",
279
+ mcpVersion,
280
+ "--api_key",
281
+ envVars.API_KEY,
282
+ "--api_base_url",
283
+ envVars.API_BASE_URL
284
+ ],
285
+ env: {}
286
+ };
287
+ writeConfig(config, argv.client);
288
+ }
289
+ else if (argv.client === "opencode") {
290
+ const fs = await import('fs');
291
+ const opencodeConfigPath = path.join(process.cwd(), 'opencode.json');
292
+ let opencodeConfig = {};
293
+ if (fs.existsSync(opencodeConfigPath)) {
294
+ try {
295
+ opencodeConfig = JSON.parse(fs.readFileSync(opencodeConfigPath, 'utf8'));
296
+ }
297
+ catch (error) {
298
+ logger.warn(`Could not parse existing opencode.json: ${error.message}`);
299
+ opencodeConfig = {};
300
+ }
296
301
  }
297
- }
298
-
299
- copilotConfig.servers[name] = {
300
- command: "npx",
301
- args: ["-y", mcpVersion],
302
- env: {
303
- API_KEY: envVars.API_KEY,
304
- API_BASE_URL: envVars.API_BASE_URL
302
+ if (!opencodeConfig.mcp)
303
+ opencodeConfig.mcp = {};
304
+ opencodeConfig.mcp[name] = {
305
+ type: "local",
306
+ command: ["npx", "-y", mcpVersion],
307
+ environment: {
308
+ API_KEY: envVars.API_KEY,
309
+ API_BASE_URL: envVars.API_BASE_URL
310
+ }
311
+ };
312
+ fs.writeFileSync(opencodeConfigPath, JSON.stringify(opencodeConfig, null, 2));
313
+ logger.info(`Configured OpenCode MCP at: ${opencodeConfigPath}`);
314
+ }
315
+ // Fetch instructions documentation and save to appropriate files
316
+ let instructionsContent = null;
317
+ try {
318
+ const fetch = (await import('node-fetch')).default;
319
+ const apiBaseUrl = envVars.API_BASE_URL || "http://localhost:7130";
320
+ const response = await fetch(`${apiBaseUrl}/api/docs/instructions`);
321
+ if (response.ok) {
322
+ const result = await response.json();
323
+ if (result && result.content) {
324
+ instructionsContent = result.content;
325
+ }
305
326
  }
306
- };
307
-
308
- fs.writeFileSync(mcpConfigPath, JSON.stringify(copilotConfig, null, 2));
309
- logger.info(`Configured Copilot MCP at: ${mcpConfigPath}`);
310
- } else if (argv.client === "antigravity") {
311
- // Antigravity uses args instead of env vars
312
- if (!config.mcpServers) config.mcpServers = {};
313
- config.mcpServers[name] = {
314
- command: "npx",
315
- args: [
316
- "-y",
317
- mcpVersion,
318
- "--api_key",
319
- envVars.API_KEY,
320
- "--api_base_url",
321
- envVars.API_BASE_URL
322
- ],
323
- env: {}
324
- };
325
- writeConfig(config, argv.client);
326
- }
327
-
328
- // Fetch instructions documentation and save to appropriate files
329
- let instructionsContent = null;
330
- try {
331
- const fetch = (await import('node-fetch')).default;
332
- const fs = await import('fs');
333
- const apiBaseUrl = envVars.API_BASE_URL || "http://localhost:7130";
334
- const response = await fetch(`${apiBaseUrl}/api/docs/instructions`);
335
-
336
- if (response.ok) {
337
- const result = await response.json();
338
- if (result && result.content) {
339
- instructionsContent = result.content;
340
- }
341
327
  }
342
- } catch (fetchError) {
343
- logger.warn(`Could not download instructions: ${fetchError.message}`);
344
- }
345
-
346
- // Save instructions to agent.md for all clients
347
- if (instructionsContent) {
348
- const fs = await import('fs');
349
- const frontmatter = `---
328
+ catch (fetchError) {
329
+ logger.warn(`Could not download instructions: ${fetchError.message}`);
330
+ }
331
+ if (instructionsContent) {
332
+ const fs = await import('fs');
333
+ const frontmatter = `---
350
334
  description: Instructions building apps with MCP
351
335
  globs: *
352
336
  alwaysApply: true
353
337
  ---
354
338
 
355
339
  `;
356
- const contentWithFrontmatter = frontmatter + instructionsContent;
357
- const agentsMdPath = path.join(process.cwd(), 'AGENTS.md');
358
- fs.writeFileSync(agentsMdPath, contentWithFrontmatter, 'utf-8');
359
- logger.info(`Saved instructions to: ${agentsMdPath}`);
360
- }
361
-
362
- printHeader('Setup Complete!');
363
- printPostInstallMessage(argv.client);
364
- } catch (e) {
365
- logger.error(red(e.message));
340
+ const contentWithFrontmatter = frontmatter + instructionsContent;
341
+ const agentsMdPath = path.join(process.cwd(), 'AGENTS.md');
342
+ fs.writeFileSync(agentsMdPath, contentWithFrontmatter, 'utf-8');
343
+ logger.info(`Saved instructions to: ${agentsMdPath}`);
344
+ }
345
+ printHeader('Setup Complete!');
346
+ printPostInstallMessage(argv.client);
347
+ }
348
+ catch (e) {
349
+ logger.error(red(e.message));
366
350
  }
367
- }
368
-
369
- var parser = yargs(hideBin(process.argv)).scriptName("@insforge/install").command("install", "Install Insforge MCP server", builder, handler).help().alias("h", "help").version().alias("v", "version");
370
- if (!process.argv.slice(2).length || process.argv[2].startsWith("--")) {
351
+ }
352
+ const parser = yargs(hideBin(process.argv)).scriptName("@insforge/install").command("install", "Install Insforge MCP server", builder, handler).help().alias("h", "help").version().alias("v", "version");
353
+ if (!process.argv.slice(2).length || process.argv[2].startsWith("--")) {
371
354
  parser.parse(["install", ...process.argv.slice(2)]);
372
- } else {
355
+ }
356
+ else {
373
357
  parser.parse();
374
- }
375
-
358
+ }
@@ -0,0 +1,9 @@
1
+ interface McpConfig {
2
+ mcpServers?: Record<string, unknown>;
3
+ [key: string]: unknown;
4
+ }
5
+ export declare const logger: import("consola").ConsolaInstance;
6
+ export declare const clientNames: string[];
7
+ export declare function readConfig(client: string, local?: boolean): McpConfig;
8
+ export declare function writeConfig(config: McpConfig, client: string, local?: boolean): void;
9
+ export {};
package/dist/utils.js CHANGED
@@ -1,158 +1,147 @@
1
- // src/client-config.ts
2
1
  import fs from "fs";
3
2
  import os from "os";
4
3
  import path from "path";
5
4
  import process from "process";
6
-
7
- // src/logger.ts
8
5
  import { createConsola } from "consola";
9
- var logger = createConsola({});
10
- var verbose = (msg) => logger.verbose(msg);
11
-
12
- // src/client-config.ts
13
- var homeDir = os.homedir();
14
- var platformPaths = {
15
- win32: {
16
- baseDir: process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
17
- vscodePath: path.join("Code", "User", "globalStorage")
18
- },
19
- darwin: {
20
- baseDir: path.join(homeDir, "Library", "Application Support"),
21
- vscodePath: path.join("Code", "User", "globalStorage")
22
- },
23
- linux: {
24
- baseDir: process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config"),
25
- vscodePath: path.join("Code/User/globalStorage")
26
- }
6
+ export const logger = createConsola({});
7
+ const verbose = (msg) => logger.verbose(msg);
8
+ const homeDir = os.homedir();
9
+ const platformPaths = {
10
+ win32: {
11
+ baseDir: process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
12
+ vscodePath: path.join("Code", "User", "globalStorage")
13
+ },
14
+ darwin: {
15
+ baseDir: path.join(homeDir, "Library", "Application Support"),
16
+ vscodePath: path.join("Code", "User", "globalStorage")
17
+ },
18
+ linux: {
19
+ baseDir: process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config"),
20
+ vscodePath: path.join("Code/User/globalStorage")
21
+ }
27
22
  };
28
- var platform = process.platform;
29
- var { baseDir, vscodePath } = platformPaths[platform];
30
- var clientPaths = {
31
- "claude-code": {
32
- type: "file",
33
- path: path.join(process.cwd(), ".mcp.json")
34
- },
35
- cursor: {
36
- type: "file",
37
- path: path.join(homeDir, ".cursor", "mcp.json"),
38
- localPath: path.join(process.cwd(), ".cursor", "mcp.json")
39
- },
40
- windsurf: {
41
- type: "file",
42
- path: path.join(homeDir, ".codeium", "windsurf", "mcp_config.json")
43
- },
44
- cline: {
45
- type: "file",
46
- path: path.join(baseDir, vscodePath, "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
47
- },
48
- roocode: {
49
- type: "file",
50
- path: path.join(baseDir, vscodePath, "rooveterinaryinc.roo-cline", "settings", "mcp_settings.json")
51
- },
52
- trae: {
53
- type: "file",
54
- path: path.join(baseDir, "Trae", "User", "mcp.json")
55
- },
56
- codex: {
57
- type: "file",
58
- path: path.join(homeDir, ".codex", "mcp_config.json")
59
- },
60
- copilot: {
61
- type: "file",
62
- path: path.join(process.cwd(), ".vscode", "mcp.json")
63
- },
64
- qoder: {
65
- type: "file",
66
- path: path.join(baseDir, "Qoder", "SharedClientCache", "mcp.json")
67
- },
68
- antigravity: {
69
- type: "file",
70
- path: path.join(homeDir, ".gemini", "antigravity", "mcp_config.json")
71
- },
72
- kiro: {
73
- type: "file",
74
- path: path.join(homeDir, ".kiro", "settings", "mcp.json")
75
- }
23
+ const platform = process.platform;
24
+ const { baseDir, vscodePath } = platformPaths[platform] ?? platformPaths.linux;
25
+ const clientPaths = {
26
+ cursor: {
27
+ type: "file",
28
+ path: path.join(homeDir, ".cursor", "mcp.json"),
29
+ localPath: path.join(process.cwd(), ".cursor", "mcp.json")
30
+ },
31
+ "claude-code": {
32
+ type: "file",
33
+ path: path.join(process.cwd(), ".mcp.json")
34
+ },
35
+ windsurf: {
36
+ type: "file",
37
+ path: path.join(homeDir, ".codeium", "windsurf", "mcp_config.json")
38
+ },
39
+ cline: {
40
+ type: "file",
41
+ path: path.join(baseDir, vscodePath, "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
42
+ },
43
+ roocode: {
44
+ type: "file",
45
+ path: path.join(baseDir, vscodePath, "rooveterinaryinc.roo-cline", "settings", "mcp_settings.json")
46
+ },
47
+ trae: {
48
+ type: "file",
49
+ path: path.join(baseDir, "Trae", "User", "mcp.json")
50
+ },
51
+ codex: {
52
+ type: "file",
53
+ path: path.join(homeDir, ".codex", "mcp_config.json")
54
+ },
55
+ copilot: {
56
+ type: "file",
57
+ path: path.join(process.cwd(), ".vscode", "mcp.json")
58
+ },
59
+ qoder: {
60
+ type: "file",
61
+ path: path.join(baseDir, "Qoder", "SharedClientCache", "mcp.json")
62
+ },
63
+ antigravity: {
64
+ type: "file",
65
+ path: path.join(homeDir, ".gemini", "antigravity", "mcp_config.json")
66
+ },
67
+ kiro: {
68
+ type: "file",
69
+ path: path.join(homeDir, ".kiro", "settings", "mcp.json")
70
+ },
71
+ opencode: {
72
+ type: "file",
73
+ path: path.join(process.cwd(), "opencode.json")
74
+ }
76
75
  };
77
- var clientNames = Object.keys(clientPaths);
76
+ export const clientNames = Object.keys(clientPaths);
78
77
  function getConfigPath(client, local) {
79
- const normalizedClient = client?.toLowerCase() || "claude-code";
80
- verbose(`Getting config path for client: ${normalizedClient}${local ? " (local)" : ""}`);
81
- const configTarget = clientPaths[normalizedClient];
82
- if (!configTarget) {
83
- throw new Error(`Unsupported client: ${client}. Supported clients: ${clientNames.join(", ")}`);
84
- }
85
- if (local && configTarget.localPath) {
86
- verbose(`Using local config path for ${normalizedClient}: ${configTarget.localPath}`);
87
- return { ...configTarget, path: configTarget.localPath };
88
- }
89
- verbose(`Using default config path for ${normalizedClient}: ${configTarget.path}`);
90
- return configTarget;
78
+ const normalizedClient = client?.toLowerCase() || "claude-code";
79
+ verbose(`Getting config path for client: ${normalizedClient}${local ? " (local)" : ""}`);
80
+ const configTarget = clientPaths[normalizedClient];
81
+ if (!configTarget) {
82
+ throw new Error(`Unsupported client: ${client}. Supported clients: ${clientNames.join(", ")}`);
83
+ }
84
+ if (local && configTarget.localPath) {
85
+ verbose(`Using local config path for ${normalizedClient}: ${configTarget.localPath}`);
86
+ return { ...configTarget, path: configTarget.localPath };
87
+ }
88
+ verbose(`Using default config path for ${normalizedClient}: ${configTarget.path}`);
89
+ return configTarget;
91
90
  }
92
- function readConfig(client, local) {
93
- verbose(`Reading config for client: ${client}${local ? " (local)" : ""}`);
94
- try {
95
- const configPath = getConfigPath(client, local);
96
- verbose(`Checking if config file exists at: ${configPath.path}`);
97
- if (!fs.existsSync(configPath.path)) {
98
- verbose("Config file not found, returning default empty config");
99
- // Return appropriate default structure for each client
100
- return { mcpServers: {} };
91
+ export function readConfig(client, local) {
92
+ verbose(`Reading config for client: ${client}${local ? " (local)" : ""}`);
93
+ try {
94
+ const configPath = getConfigPath(client, local);
95
+ verbose(`Checking if config file exists at: ${configPath.path}`);
96
+ if (!fs.existsSync(configPath.path)) {
97
+ verbose("Config file not found, returning default empty config");
98
+ return { mcpServers: {} };
99
+ }
100
+ verbose("Reading config file content");
101
+ const rawConfig = JSON.parse(fs.readFileSync(configPath.path, "utf8"));
102
+ verbose(`Config loaded successfully: ${JSON.stringify(rawConfig, null, 2)}`);
103
+ return rawConfig;
104
+ }
105
+ catch (error) {
106
+ verbose(`Error reading config: ${error instanceof Error ? error.stack : JSON.stringify(error)}`);
107
+ return { mcpServers: {} };
101
108
  }
102
- verbose("Reading config file content");
103
- const rawConfig = JSON.parse(fs.readFileSync(configPath.path, "utf8"));
104
- verbose(`Config loaded successfully: ${JSON.stringify(rawConfig, null, 2)}`);
105
- // Return the raw config to preserve existing structure
106
- return rawConfig;
107
- } catch (error) {
108
- verbose(`Error reading config: ${error instanceof Error ? error.stack : JSON.stringify(error)}`);
109
- return { mcpServers: {} };
110
- }
111
109
  }
112
- function writeConfig(config, client, local) {
113
- verbose(`Writing config for client: ${client || "default"}${local ? " (local)" : ""}`);
114
- verbose(`Config data: ${JSON.stringify(config, null, 2)}`);
115
-
116
- // All clients use mcpServers
117
- if (!config.mcpServers || typeof config.mcpServers !== "object") {
118
- verbose("Invalid mcpServers structure in config");
119
- throw new Error("Invalid mcpServers structure");
120
- }
121
-
122
- const configPath = getConfigPath(client, local);
123
- writeConfigFile(config, configPath);
110
+ export function writeConfig(config, client, local) {
111
+ verbose(`Writing config for client: ${client || "default"}${local ? " (local)" : ""}`);
112
+ verbose(`Config data: ${JSON.stringify(config, null, 2)}`);
113
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
114
+ verbose("Invalid mcpServers structure in config");
115
+ throw new Error("Invalid mcpServers structure");
116
+ }
117
+ const configPath = getConfigPath(client, local);
118
+ writeConfigFile(config, configPath);
124
119
  }
125
120
  function writeConfigFile(config, target) {
126
- const configDir = path.dirname(target.path);
127
- verbose(`Ensuring config directory exists: ${configDir}`);
128
- if (!fs.existsSync(configDir)) {
129
- verbose(`Creating directory: ${configDir}`);
130
- fs.mkdirSync(configDir, { recursive: true });
131
- }
132
- let existingConfig = { mcpServers: {} };
133
- try {
134
- if (fs.existsSync(target.path)) {
135
- verbose("Reading existing config file for merging");
136
- existingConfig = JSON.parse(fs.readFileSync(target.path, "utf8"));
137
- verbose(`Existing config loaded: ${JSON.stringify(existingConfig, null, 2)}`);
121
+ const configDir = path.dirname(target.path);
122
+ verbose(`Ensuring config directory exists: ${configDir}`);
123
+ if (!fs.existsSync(configDir)) {
124
+ verbose(`Creating directory: ${configDir}`);
125
+ fs.mkdirSync(configDir, { recursive: true });
126
+ }
127
+ let existingConfig = { mcpServers: {} };
128
+ try {
129
+ if (fs.existsSync(target.path)) {
130
+ verbose("Reading existing config file for merging");
131
+ existingConfig = JSON.parse(fs.readFileSync(target.path, "utf8"));
132
+ verbose(`Existing config loaded: ${JSON.stringify(existingConfig, null, 2)}`);
133
+ }
138
134
  }
139
- } catch (error) {
140
- verbose(`Error reading existing config for merge: ${error instanceof Error ? error.message : String(error)}`);
141
- }
142
- verbose("Merging configs");
143
- const mergedConfig = {
144
- ...existingConfig,
145
- ...config
146
- };
147
- verbose(`Merged config: ${JSON.stringify(mergedConfig, null, 2)}`);
148
- verbose(`Writing config to file: ${target.path}`);
149
- fs.writeFileSync(target.path, JSON.stringify(mergedConfig, null, 2));
150
- verbose("Config successfully written");
135
+ catch (error) {
136
+ verbose(`Error reading existing config for merge: ${error instanceof Error ? error.message : String(error)}`);
137
+ }
138
+ verbose("Merging configs");
139
+ const mergedConfig = {
140
+ ...existingConfig,
141
+ ...config
142
+ };
143
+ verbose(`Merged config: ${JSON.stringify(mergedConfig, null, 2)}`);
144
+ verbose(`Writing config to file: ${target.path}`);
145
+ fs.writeFileSync(target.path, JSON.stringify(mergedConfig, null, 2));
146
+ verbose("Config successfully written");
151
147
  }
152
- export {
153
- logger,
154
- clientNames,
155
- getConfigPath,
156
- readConfig,
157
- writeConfig
158
- };
package/package.json CHANGED
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "name": "@insforge/install",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "description": "CLI tool for installing Insforge MCP servers across different AI clients",
5
5
  "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
6
9
  "main": "dist/index.js",
7
10
  "bin": {
8
11
  "insforge-install": "dist/index.js"
9
12
  },
10
13
  "scripts": {
14
+ "build": "tsc",
15
+ "prepublishOnly": "npm run build",
11
16
  "start": "node dist/index.js"
12
17
  },
13
18
  "dependencies": {
@@ -26,5 +31,10 @@
26
31
  "license": "MIT",
27
32
  "publishConfig": {
28
33
  "access": "public"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.19.30",
37
+ "@types/yargs": "^17.0.35",
38
+ "typescript": "^5.9.3"
29
39
  }
30
40
  }
@@ -1,5 +0,0 @@
1
- {
2
- "enabledMcpjsonServers": [
3
- "insforge"
4
- ]
5
- }
package/.mcp.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "insforge": {
4
- "command": "npx",
5
- "args": [
6
- "-y",
7
- "@insforge/mcp@latest"
8
- ],
9
- "env": {
10
- "API_KEY": "test123",
11
- "API_BASE_URL": "http://localhost:7130"
12
- }
13
- }
14
- }
15
- }