@insforge/install 0.0.51 → 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,307 +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 coding agent:',
105
- pageSize: 15,
106
- loop: false,
107
- choices: clientNames.map(name => ({
108
- name: clientDisplayNames[name] || name,
109
- value: name
110
- }))
111
- });
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
+ });
112
95
  }
113
-
114
- // Validate client
115
96
  if (!clientNames.includes(selectedClient)) {
116
- logger.error(`Invalid client: ${selectedClient}. Available clients: ${clientNames.join(", ")}`);
117
- return;
97
+ logger.error(`Invalid client: ${selectedClient}. Available clients: ${clientNames.join(", ")}`);
98
+ return;
118
99
  }
119
-
120
- // Update argv.client with selected value for downstream use
121
100
  argv.client = selectedClient;
122
101
  const envVars = {};
123
102
  if (argv.env && argv.env.length > 0) {
124
- for (const envVar of argv.env) {
125
- const [key, ...valueParts] = envVar.split("=");
126
- if (key && valueParts.length > 0) {
127
- envVars[key] = valueParts.join("=");
128
- } else {
129
- 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
+ }
130
111
  }
131
- }
132
112
  }
133
113
  if (!envVars.API_KEY) {
134
- logger.error("API_KEY environment variable is required. Use --env API_KEY=your_key");
135
- return;
114
+ logger.error("API_KEY environment variable is required. Use --env API_KEY=your_key");
115
+ return;
136
116
  }
137
- // Add default base URL if not provided
138
117
  if (!envVars.API_BASE_URL) {
139
- envVars.API_BASE_URL = "http://localhost:7130";
118
+ envVars.API_BASE_URL = "http://localhost:7130";
140
119
  }
141
120
  const name = "insforge";
142
121
  const mcpVersion = argv.dev ? "@insforge/mcp@dev" : "@insforge/mcp@latest";
143
122
  try {
144
- printHeader('InsForge MCP Installer');
145
- logger.info(`Setting up MCP for ${cyan(argv.client)}...`);
146
-
147
- printHeader(`Configuring ${argv.client}`);
148
- const config = readConfig(argv.client);
149
-
150
- // 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);
151
127
  if (argv.client === "claude-code") {
152
- // Claude Code uses mcpServers at root level
153
- if (!config.mcpServers) config.mcpServers = {};
154
- const isWindows = process.platform === 'win32';
155
- config.mcpServers[name] = isWindows ? {
156
- command: "cmd",
157
- args: ["/c", "npx", "-y", mcpVersion],
158
- env: {
159
- API_KEY: envVars.API_KEY,
160
- 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 });
161
152
  }
162
- } : {
163
- command: "npx",
164
- args: ["-y", mcpVersion],
165
- env: {
166
- API_KEY: envVars.API_KEY,
167
- 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
+ }
168
161
  }
169
- };
170
- writeConfig(config, argv.client);
171
-
172
- // Create or update .claude/settings.local.json with enableAllProjectMcpServers
173
- const fs = await import('fs');
174
- const claudeDir = path.join(process.cwd(), '.claude');
175
- const settingsPath = path.join(claudeDir, 'settings.local.json');
176
-
177
- if (!fs.existsSync(claudeDir)) {
178
- fs.mkdirSync(claudeDir, { recursive: true });
179
- }
180
-
181
- let settings = {};
182
- if (fs.existsSync(settingsPath)) {
183
- try {
184
- settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
185
- } catch (error) {
186
- logger.warn(`Could not parse existing settings.local.json: ${error.message}`);
162
+ if (!settings.enabledMcpjsonServers) {
163
+ settings.enabledMcpjsonServers = [];
187
164
  }
188
- }
189
-
190
- // Add "insforge" to enabledMcpjsonServers if not already present
191
- if (!settings.enabledMcpjsonServers) {
192
- settings.enabledMcpjsonServers = [];
193
- }
194
- if (!settings.enabledMcpjsonServers.includes("insforge")) {
195
- settings.enabledMcpjsonServers.push("insforge");
196
- }
197
-
198
- // Remove "insforge" from disabledMcpjsonServers if present
199
- if (settings.disabledMcpjsonServers && Array.isArray(settings.disabledMcpjsonServers)) {
200
- settings.disabledMcpjsonServers = settings.disabledMcpjsonServers.filter(
201
- (server) => server !== "insforge"
202
- );
203
- }
204
-
205
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
206
- logger.info("Added \"insforge\" to enabledMcpjsonServers in .claude/settings.local.json");
207
- } else if (argv.client === "codex") {
208
- // Use Codex CLI for Codex
209
- const homeDir = os.homedir();
210
- const isWindows = process.platform === 'win32';
211
- const codexPath = isWindows
212
- ? path.join(homeDir, 'AppData', 'Roaming', 'npm', 'codex.cmd')
213
- : 'codex';
214
-
215
- const envArgs = Object.entries(envVars)
216
- .filter(([key]) => key !== 'CLIENT_NAME')
217
- .map(([key, value]) => `--env ${key}=${value}`)
218
- .join(' ');
219
-
220
- try {
221
- // 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(' ');
222
184
  try {
223
- const removeCmd = isWindows ? `"${codexPath}" mcp remove ${name}` : `codex mcp remove ${name}`;
224
- execSync(removeCmd, {
225
- stdio: 'pipe',
226
- shell: true
227
- });
228
- logger.info("Removed existing insforge MCP installation.");
229
- } catch (removeError) {
230
- // It's okay if remove fails - the MCP might not exist
231
- 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' });
232
198
  }
233
-
234
- // Now add the MCP server using codex CLI with --env flags
235
- const command = isWindows
236
- ? `"${codexPath}" mcp add ${name} ${envArgs} -- npx -y ${mcpVersion}`
237
- : `codex mcp add ${name} ${envArgs} -- npx -y ${mcpVersion}`;
238
- logger.info(`Adding insforge MCP server (${mcpVersion})...`);
239
- execSync(command, {
240
- stdio: 'inherit',
241
- shell: true
242
- });
243
- } catch (error) {
244
- throw new Error(`Failed to add MCP server via Codex CLI: ${error.message}`);
245
- }
246
- } else if (argv.client === "cursor") {
247
- // Cursor uses mcpServers at root level
248
- if (!config.mcpServers) config.mcpServers = {};
249
- config.mcpServers[name] = {
250
- command: "npx",
251
- args: ["-y", mcpVersion],
252
- env: {
253
- API_KEY: envVars.API_KEY,
254
- API_BASE_URL: envVars.API_BASE_URL
199
+ catch (error) {
200
+ throw new Error(`Failed to add MCP server via Codex CLI: ${error.message}`);
255
201
  }
256
- };
257
- writeConfig(config, argv.client);
258
- } else if (argv.client === "windsurf") {
259
- if (!config.mcpServers) config.mcpServers = {};
260
- config.mcpServers[name] = {
261
- command: "npx",
262
- args: ["-y", mcpVersion],
263
- env: {
264
- API_KEY: envVars.API_KEY,
265
- 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 });
266
248
  }
267
- };
268
- writeConfig(config, argv.client);
269
- } else if (argv.client === "cline" || argv.client === "roocode" || argv.client === "trae" || argv.client === "qoder" || argv.client === "kiro") {
270
- if (!config.mcpServers) config.mcpServers = {};
271
- config.mcpServers[name] = {
272
- command: "npx",
273
- args: ["-y", mcpVersion],
274
- env: {
275
- API_KEY: envVars.API_KEY,
276
- 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
+ }
277
260
  }
278
- };
279
- writeConfig(config, argv.client);
280
- } else if (argv.client === "copilot") {
281
- // Copilot uses "servers" instead of "mcpServers"
282
- const fs = await import('fs');
283
- const vscodeDir = path.join(process.cwd(), '.vscode');
284
- const mcpConfigPath = path.join(vscodeDir, 'mcp.json');
285
-
286
- if (!fs.existsSync(vscodeDir)) {
287
- fs.mkdirSync(vscodeDir, { recursive: true });
288
- }
289
-
290
- let copilotConfig = { servers: {} };
291
- if (fs.existsSync(mcpConfigPath)) {
292
- try {
293
- copilotConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
294
- if (!copilotConfig.servers) copilotConfig.servers = {};
295
- } catch (error) {
296
- logger.warn(`Could not parse existing mcp.json: ${error.message}`);
297
- 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
+ }
298
301
  }
299
- }
300
-
301
- copilotConfig.servers[name] = {
302
- command: "npx",
303
- args: ["-y", mcpVersion],
304
- env: {
305
- API_KEY: envVars.API_KEY,
306
- 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
+ }
307
326
  }
308
- };
309
-
310
- fs.writeFileSync(mcpConfigPath, JSON.stringify(copilotConfig, null, 2));
311
- logger.info(`Configured Copilot MCP at: ${mcpConfigPath}`);
312
- } else if (argv.client === "antigravity") {
313
- // Antigravity uses args instead of env vars
314
- if (!config.mcpServers) config.mcpServers = {};
315
- config.mcpServers[name] = {
316
- command: "npx",
317
- args: [
318
- "-y",
319
- mcpVersion,
320
- "--api_key",
321
- envVars.API_KEY,
322
- "--api_base_url",
323
- envVars.API_BASE_URL
324
- ],
325
- env: {}
326
- };
327
- writeConfig(config, argv.client);
328
- }
329
-
330
- // Fetch instructions documentation and save to appropriate files
331
- let instructionsContent = null;
332
- try {
333
- const fetch = (await import('node-fetch')).default;
334
- const fs = await import('fs');
335
- const apiBaseUrl = envVars.API_BASE_URL || "http://localhost:7130";
336
- const response = await fetch(`${apiBaseUrl}/api/docs/instructions`);
337
-
338
- if (response.ok) {
339
- const result = await response.json();
340
- if (result && result.content) {
341
- instructionsContent = result.content;
342
- }
343
327
  }
344
- } catch (fetchError) {
345
- logger.warn(`Could not download instructions: ${fetchError.message}`);
346
- }
347
-
348
- // Save instructions to agent.md for all clients
349
- if (instructionsContent) {
350
- const fs = await import('fs');
351
- 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 = `---
352
334
  description: Instructions building apps with MCP
353
335
  globs: *
354
336
  alwaysApply: true
355
337
  ---
356
338
 
357
339
  `;
358
- const contentWithFrontmatter = frontmatter + instructionsContent;
359
- const agentsMdPath = path.join(process.cwd(), 'AGENTS.md');
360
- fs.writeFileSync(agentsMdPath, contentWithFrontmatter, 'utf-8');
361
- logger.info(`Saved instructions to: ${agentsMdPath}`);
362
- }
363
-
364
- printHeader('Setup Complete!');
365
- printPostInstallMessage(argv.client);
366
- } catch (e) {
367
- 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));
368
350
  }
369
- }
370
-
371
- 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");
372
- 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("--")) {
373
354
  parser.parse(["install", ...process.argv.slice(2)]);
374
- } else {
355
+ }
356
+ else {
375
357
  parser.parse();
376
- }
377
-
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
- cursor: {
32
- type: "file",
33
- path: path.join(homeDir, ".cursor", "mcp.json"),
34
- localPath: path.join(process.cwd(), ".cursor", "mcp.json")
35
- },
36
- "claude-code": {
37
- type: "file",
38
- path: path.join(process.cwd(), ".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.51",
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": "ik_478fa0cdd9458ca1e882d918966367a4",
11
- "API_BASE_URL": "https://hgqhh6jq.us-west.insforge.app"
12
- }
13
- }
14
- }
15
- }
package/AGENTS.md DELETED
@@ -1,99 +0,0 @@
1
- ---
2
- description: Instructions building apps with MCP
3
- globs: *
4
- alwaysApply: true
5
- ---
6
-
7
- # InsForge SDK Documentation - Overview
8
-
9
- ## What is InsForge?
10
-
11
- Backend-as-a-service (BaaS) platform providing:
12
-
13
- - **Database**: PostgreSQL with PostgREST API
14
- - **Authentication**: Email/password + OAuth (Google, GitHub)
15
- - **Storage**: File upload/download
16
- - **AI**: Chat completions and image generation (OpenAI-compatible)
17
- - **Functions**: Serverless function deployment
18
-
19
- ## Installation
20
-
21
- ### 🚨 CRITICAL: Follow these steps in order
22
-
23
- ### Step 1: Download Template
24
-
25
- Use the `download-template` MCP tool to create a new project with your backend URL and anon key pre-configured.
26
-
27
- ### Step 2: Install SDK
28
-
29
- ```bash
30
- npm install @insforge/sdk@latest
31
- ```
32
-
33
- ### Step 3: Create SDK Client
34
-
35
- You must create a client instance using `createClient()` with your base URL and anon key:
36
-
37
- ```javascript
38
- import { createClient } from '@insforge/sdk';
39
-
40
- const client = createClient({
41
- baseUrl: 'https://your-app.region.insforge.app', // Your InsForge backend URL
42
- anonKey: 'your-anon-key-here' // Get this from backend metadata
43
- });
44
-
45
- ```
46
-
47
- **API BASE URL**: Your API base URL is `https://your-app.region.insforge.app`.
48
-
49
- ## Getting Detailed Documentation
50
-
51
- ### 🚨 CRITICAL: Always Fetch Documentation Before Writing Code
52
-
53
- Before writing or editing any InsForge integration code, you **MUST** call the `fetch-docs` MCP tool to get the latest SDK documentation. This ensures you have accurate, up-to-date implementation patterns.
54
-
55
- ### Use the InsForge `fetch-docs` MCP tool to get specific SDK documentation:
56
-
57
- Available documentation types:
58
-
59
- - `"instructions"` - Essential backend setup (START HERE)
60
- - `"db-sdk"` - Database operations with SDK
61
- - **Authentication** - Choose based on implementation:
62
- - `"auth-sdk"` - Direct SDK methods for custom auth flows
63
- - `"auth-components-react"` - Pre-built auth UI for React+Vite (singlepage App)
64
- - `"auth-components-react-router"` - Pre-built auth UI for React(Vite+React Router) (Multipage App)
65
- - `"auth-components-nextjs"` - Pre-built auth UI for Nextjs (SSR App)
66
- - `"storage-sdk"` - File storage operations
67
- - `"functions-sdk"` - Serverless functions invocation
68
- - `"ai-integration-sdk"` - AI chat and image generation
69
- - `"real-time"` - Real-time pub/sub (database + client events) via WebSockets
70
- - `"deployment"` - Deploy frontend applications via MCP tool
71
-
72
- ## When to Use SDK vs MCP Tools
73
-
74
- ### Always SDK for Application Logic:
75
-
76
- - Authentication (register, login, logout, profiles)
77
- - Database CRUD (select, insert, update, delete)
78
- - Storage operations (upload, download files)
79
- - AI operations (chat, image generation)
80
- - Serverless function invocation
81
-
82
- ### Use MCP Tools for Infrastructure:
83
-
84
- - Project scaffolding (`download-template`) - Download starter templates with InsForge integration
85
- - Backend setup and metadata (`get-backend-metadata`)
86
- - Database schema management (`run-raw-sql`, `get-table-schema`)
87
- - Storage bucket creation (`create-bucket`, `list-buckets`, `delete-bucket`)
88
- - Serverless function deployment (`create-function`, `update-function`, `delete-function`)
89
- - Frontend deployment (`create-deployment`) - Deploy frontend apps to InsForge hosting
90
-
91
- ## Important Notes
92
-
93
- - For auth: use `auth-sdk` for custom UI, or framework-specific components for pre-built UI
94
- - SDK returns `{data, error}` structure for all operations
95
- - Database inserts require array format: `[{...}]`
96
- - Serverless functions have single endpoint (no subpaths)
97
- - Storage: Upload files to buckets, store URLs in database
98
- - AI operations are OpenAI-compatible
99
- - **EXTRA IMPORTANT**: Use Tailwind CSS 3.4 (do not upgrade to v4). Lock these dependencies in `package.json`