claudmax 3.0.0 → 3.0.1

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.
Files changed (2) hide show
  1. package/index.js +273 -137
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -9,13 +9,22 @@ const os = require('os');
9
9
  const https = require('https');
10
10
  const { execSync } = require('child_process');
11
11
 
12
- // ── Constants ─────────────────────────────────────────────────────────────────
12
+ // ── Constants ──────────────────────────────────────────────────────────────
13
13
  const MCP_PKG = 'claudmax-mcp-server';
14
- const API_BASE = 'https://api.claudmax.pro';
15
-
14
+ const API_BASE = process.env.CLAUDMAX_API_BASE || 'https://api.claudmax.pro';
16
15
  const HOME = os.homedir();
17
16
 
18
- // ── Color helpers ─────────────────────────────────────────────────────────────
17
+ // ── CLI args ──────────────────────────────────────────────────────────────
18
+ const args = process.argv.slice(2);
19
+ const flags = {};
20
+ for (let i = 0; i < args.length; i++) {
21
+ if (args[i].startsWith('--')) {
22
+ const key = args[i].slice(2);
23
+ flags[key] = args[i + 1] !== undefined && !args[i + 1].startsWith('--') ? args[i + 1] : true;
24
+ }
25
+ }
26
+
27
+ // ── Color helpers ─────────────────────────────────────────────────────────
19
28
  const C = {
20
29
  reset: '\x1b[0m',
21
30
  bold: (s) => `\x1b[1m${s}\x1b[0m`,
@@ -32,8 +41,9 @@ const CHECK = C.green('\u2713');
32
41
  const CROSS = C.red('\u2717');
33
42
  const WARN = C.yellow('\u26A0');
34
43
  const INFO = C.cyan('\u2139');
44
+ const ARROW = C.cyan('\u25b6');
35
45
 
36
- // ── Readline helper ────────────────────────────────────────────────────────────
46
+ // ── Readline helper ────────────────────────────────────────────────────────
37
47
  function createRL() {
38
48
  return readline.createInterface({ input: process.stdin, output: process.stdout });
39
49
  }
@@ -42,7 +52,7 @@ function ask(rl, question) {
42
52
  return new Promise((resolve) => rl.question(question, resolve));
43
53
  }
44
54
 
45
- // ── File helpers ──────────────────────────────────────────────────────────────
55
+ // ── File helpers ─────────────────────────────────────────────────────────
46
56
  function readJson(filePath) {
47
57
  try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
48
58
  catch { return null; }
@@ -70,73 +80,112 @@ function ensureDir(dir) {
70
80
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
71
81
  }
72
82
 
73
- // ── API verification ────────────────────────────────────────────────────────
83
+ function fileExists(filePath) {
84
+ try { return fs.existsSync(filePath); } catch { return false; }
85
+ }
86
+
87
+ // ── IDE auto-detector ─────────────────────────────────────────────────────
88
+ function detectIDESilent() {
89
+ const detected = [];
90
+
91
+ if (fileExists(path.join(HOME, '.claude', 'settings.json')) ||
92
+ fileExists(path.join(HOME, '.claude.json'))) {
93
+ detected.push('claude-code');
94
+ }
95
+
96
+ const cursorDir = path.join(HOME, '.cursor', 'mcp.json');
97
+ if (fileExists(cursorDir) ||
98
+ fileExists(path.join(HOME, 'Library', 'Application Support', 'Cursor'))) {
99
+ detected.push('cursor');
100
+ }
101
+
102
+ if (fileExists(path.join(HOME, '.windsurf', 'mcp.json'))) {
103
+ detected.push('windsurf');
104
+ }
105
+
106
+ // VS Code
107
+ const vscodePaths = [
108
+ path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'settings.json'),
109
+ path.join(HOME, '.config', 'Code', 'User', 'settings.json'),
110
+ path.join(process.env.APPDATA || '', 'Code', 'User', 'settings.json'),
111
+ ];
112
+ for (const p of vscodePaths) {
113
+ if (fileExists(p)) { detected.push('vscode'); break; }
114
+ }
115
+
116
+ return [...new Set(detected)];
117
+ }
118
+
119
+ function getVSCodeSettingsPath() {
120
+ switch (process.platform) {
121
+ case 'darwin':
122
+ return path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'settings.json');
123
+ case 'win32':
124
+ return path.join(process.env.APPDATA || '', 'Code', 'User', 'settings.json');
125
+ default:
126
+ return path.join(HOME, '.config', 'Code', 'User', 'settings.json');
127
+ }
128
+ }
129
+
130
+ // ── API verification ─────────────────────────────────────────────────────
74
131
  function verifyConnection(apiKey) {
75
132
  return new Promise((resolve) => {
133
+ const url = new URL(`${API_BASE}/v1/models`);
76
134
  const options = {
77
- hostname: API_BASE.replace('https://', ''),
135
+ hostname: url.hostname,
78
136
  port: 443,
79
- path: '/api/v1/key-check',
80
- method: 'POST',
137
+ path: url.pathname,
138
+ method: 'GET',
81
139
  headers: {
82
- 'Content-Type': 'application/json',
83
- 'User-Agent': 'ClaudMax-CLI/2.0.0',
140
+ 'x-api-key': apiKey,
141
+ 'User-Agent': 'ClaudMax-CLI/3.0.0',
84
142
  },
85
143
  timeout: 15000,
86
144
  };
87
145
 
88
146
  const req = https.request(options, (res) => {
89
147
  let body = '';
90
- res.on('data', (chunk) => { body += chunk; });
148
+ res.on('data', (c) => { body += c; });
91
149
  res.on('end', () => {
92
- try {
93
- // key-check uses POST with { apiKey } field
94
- const data = JSON.parse(body);
95
- resolve({ status: res.statusCode, data, ok: true });
96
- } catch {
97
- resolve({ status: res.statusCode, error: body, ok: false });
98
- }
150
+ if (res.statusCode === 200) resolve({ ok: true, status: res.statusCode });
151
+ else if (res.statusCode === 401) resolve({ ok: false, status: res.statusCode, error: 'invalid_key' });
152
+ else resolve({ ok: false, status: res.statusCode, error: body });
99
153
  });
100
154
  });
101
155
 
102
- req.on('error', (err) => resolve({ status: 0, error: err.message, ok: false }));
103
- req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout', ok: false }); });
104
-
105
- // key-check endpoint expects { apiKey } in body
106
- req.write(JSON.stringify({ apiKey }));
156
+ req.on('error', (err) => resolve({ ok: false, status: 0, error: err.message }));
157
+ req.on('timeout', () => { req.destroy(); resolve({ ok: false, status: 0, error: 'timeout' }); });
107
158
  req.end();
108
159
  });
109
160
  }
110
161
 
111
- // ── IDE configurators ────────────────────────────────────────────────────────
112
- function configureClaudeCLI(apiKey) {
113
- process.stdout.write(` Configuring Claude Code (CLI)... `);
162
+ // ── IDE configurators ────────────────────────────────────────────────────
163
+ function configureClaudeCode(apiKey) {
164
+ process.stdout.write(` ${ARROW} Claude Code CLI...`);
114
165
 
115
166
  const settingsPath = path.join(HOME, '.claude', 'settings.json');
116
- const dotClaudePath = path.join(HOME, '.claude.json');
117
-
118
167
  ensureDir(path.dirname(settingsPath));
119
-
120
- // settings.json — Claude Code reads ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL from here
121
168
  const settings = readJson(settingsPath) || {};
122
169
  deepMerge(settings, {
123
170
  env: {
124
171
  ANTHROPIC_AUTH_TOKEN: apiKey,
125
- ANTHROPIC_BASE_URL: API_BASE + '/v1',
172
+ ANTHROPIC_BASE_URL: `${API_BASE}/v1`,
126
173
  ANTHROPIC_MODEL: 'claude-opus-4-6',
127
174
  ANTHROPIC_SMALL_FAST_MODEL: 'claude-haiku-4-5',
128
175
  ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4-6',
129
176
  ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-6',
130
177
  ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4-5',
178
+ // Billing bypass: disable non-essential API traffic
131
179
  CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
180
+ CLAUDE_CODE_SKIP_VERSION_CHECK: '1',
132
181
  },
133
182
  hasCompletedOnboarding: true,
183
+ telemetryEnabled: false,
184
+ collectAnalytics: false,
134
185
  });
135
186
  writeJson(settingsPath, settings);
136
- process.stdout.write(`${CHECK} `);
137
- process.stdout.write(`${C.dim(settingsPath + '\n')}`);
138
187
 
139
- // .claude.json — MCP servers
188
+ const dotClaudePath = path.join(HOME, '.claude.json');
140
189
  const dotClaude = readJson(dotClaudePath) || {};
141
190
  if (!dotClaude.mcpServers) dotClaude.mcpServers = {};
142
191
  dotClaude.mcpServers['ClaudMax'] = {
@@ -148,11 +197,47 @@ function configureClaudeCLI(apiKey) {
148
197
  },
149
198
  };
150
199
  writeJson(dotClaudePath, dotClaude);
151
- process.stdout.write(` ${CHECK} ${C.dim(dotClaudePath + '\n')}`);
200
+
201
+ process.stdout.write(`${CHECK}\n`);
202
+ process.stdout.write(` ${C.dim(settingsPath)}\n`);
203
+ process.stdout.write(` ${C.dim(dotClaudePath)}\n`);
204
+ }
205
+
206
+ function configureVSCodeClaude(apiKey) {
207
+ process.stdout.write(` ${ARROW} VS Code Claude Extension...`);
208
+
209
+ // Claude Code settings (extension auto-detects)
210
+ const settingsPath = path.join(HOME, '.claude', 'settings.json');
211
+ ensureDir(path.dirname(settingsPath));
212
+ const settings = readJson(settingsPath) || {};
213
+ deepMerge(settings, {
214
+ env: {
215
+ ANTHROPIC_AUTH_TOKEN: apiKey,
216
+ ANTHROPIC_BASE_URL: `${API_BASE}/v1`,
217
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
218
+ },
219
+ });
220
+ writeJson(settingsPath, settings);
221
+
222
+ // VS Code extension settings
223
+ const vsSettingsPath = getVSCodeSettingsPath();
224
+ ensureDir(path.dirname(vsSettingsPath));
225
+ const vsSettings = readJson(vsSettingsPath) || {};
226
+ deepMerge(vsSettings, {
227
+ 'anthropic.authToken': apiKey,
228
+ 'anthropic.baseUrl': `${API_BASE}/v1`,
229
+ 'claude.codeDisableNonessentialTraffic': true,
230
+ });
231
+ writeJson(vsSettingsPath, vsSettings);
232
+
233
+ process.stdout.write(`${CHECK}\n`);
234
+ process.stdout.write(` ${C.dim(settingsPath)}\n`);
235
+ process.stdout.write(` ${C.dim(vsSettingsPath)}\n`);
152
236
  }
153
237
 
154
238
  function configureCursor(apiKey) {
155
- process.stdout.write(` Configuring Cursor... `);
239
+ process.stdout.write(` ${ARROW} Cursor...`);
240
+
156
241
  const mcpPath = path.join(HOME, '.cursor', 'mcp.json');
157
242
  ensureDir(path.dirname(mcpPath));
158
243
  const existing = readJson(mcpPath) || {};
@@ -160,17 +245,17 @@ function configureCursor(apiKey) {
160
245
  existing.mcpServers['ClaudMax'] = {
161
246
  command: 'npx',
162
247
  args: ['-y', MCP_PKG],
163
- env: {
164
- CLAUDMAX_API_KEY: apiKey,
165
- CLAUDMAX_URL: API_BASE,
166
- },
248
+ env: { CLAUDMAX_API_KEY: apiKey, CLAUDMAX_URL: API_BASE },
167
249
  };
168
250
  writeJson(mcpPath, existing);
169
- console.log(`${CHECK} ${C.dim(mcpPath)}`);
251
+
252
+ process.stdout.write(`${CHECK}\n`);
253
+ process.stdout.write(` ${C.dim(mcpPath)}\n`);
170
254
  }
171
255
 
172
256
  function configureWindsurf(apiKey) {
173
- process.stdout.write(` Configuring Windsurf... `);
257
+ process.stdout.write(` ${ARROW} Windsurf...`);
258
+
174
259
  const mcpPath = path.join(HOME, '.windsurf', 'mcp.json');
175
260
  ensureDir(path.dirname(mcpPath));
176
261
  const existing = readJson(mcpPath) || {};
@@ -178,165 +263,216 @@ function configureWindsurf(apiKey) {
178
263
  existing.mcpServers['ClaudMax'] = {
179
264
  command: 'npx',
180
265
  args: ['-y', MCP_PKG],
181
- env: {
182
- CLAUDMAX_API_KEY: apiKey,
183
- CLAUDMAX_URL: API_BASE,
184
- },
266
+ env: { CLAUDMAX_API_KEY: apiKey, CLAUDMAX_URL: API_BASE },
185
267
  };
186
268
  writeJson(mcpPath, existing);
187
- console.log(`${CHECK} ${C.dim(mcpPath)}`);
269
+
270
+ process.stdout.write(`${CHECK}\n`);
271
+ process.stdout.write(` ${C.dim(mcpPath)}\n`);
188
272
  }
189
273
 
190
274
  function configureCline(apiKey) {
191
- process.stdout.write(` Configuring Cline... `);
192
- const settingsPath = path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'settings.json');
275
+ process.stdout.write(` ${ARROW} Cline...`);
276
+ const settingsPath = getVSCodeSettingsPath();
193
277
  ensureDir(path.dirname(settingsPath));
194
278
  const existing = readJson(settingsPath) || {};
195
279
  deepMerge(existing, {
196
280
  'cline.apiProvider': 'anthropic',
197
- 'cline.anthropicBaseUrl': API_BASE + '/v1',
281
+ 'cline.anthropicBaseUrl': `${API_BASE}/v1`,
198
282
  'cline.apiKey': apiKey,
199
283
  });
200
284
  writeJson(settingsPath, existing);
201
- console.log(`${CHECK} ${C.dim(settingsPath)}`);
285
+ process.stdout.write(`${CHECK}\n`);
286
+ process.stdout.write(` ${C.dim(settingsPath)}\n`);
202
287
  }
203
288
 
204
289
  function configureRooCode(apiKey) {
205
- process.stdout.write(` Configuring Roo Code... `);
206
- const settingsPath = path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'settings.json');
290
+ process.stdout.write(` ${ARROW} Roo Code...`);
291
+ const settingsPath = getVSCodeSettingsPath();
207
292
  ensureDir(path.dirname(settingsPath));
208
293
  const existing = readJson(settingsPath) || {};
209
294
  deepMerge(existing, {
210
295
  'roo-cline.apiProvider': 'anthropic',
211
- 'roo-cline.anthropicBaseUrl': API_BASE + '/v1',
296
+ 'roo-cline.anthropicBaseUrl': `${API_BASE}/v1`,
212
297
  'roo-cline.apiKey': apiKey,
213
298
  });
214
299
  writeJson(settingsPath, existing);
215
- console.log(`${CHECK} ${C.dim(settingsPath)}`);
300
+ process.stdout.write(`${CHECK}\n`);
301
+ process.stdout.write(` ${C.dim(settingsPath)}\n`);
216
302
  }
217
303
 
218
- function configureVSCodeClaude(apiKey) {
219
- configureClaudeCLI(apiKey);
220
- console.log(` ${INFO} Claude extension auto-detects settings. Restart VS Code after setup.`);
304
+ // ── IDE registry ────────────────────────────────────────────────────────────
305
+ const IDES = [
306
+ { id: 'claude-code', name: 'Claude Code (CLI)', configure: configureClaudeCode },
307
+ { id: 'vscode', name: 'VS Code (Claude Extension)', configure: configureVSCodeClaude },
308
+ { id: 'cursor', name: 'Cursor', configure: configureCursor },
309
+ { id: 'windsurf', name: 'Windsurf', configure: configureWindsurf },
310
+ { id: 'cline', name: 'Cline (VS Code Extension)', configure: configureCline },
311
+ { id: 'roo', name: 'Roo Code (VS Code Extension)', configure: configureRooCode },
312
+ ];
313
+
314
+ // ── Banner ────────────────────────────────────────────────────────────────
315
+ function printBanner() {
316
+ console.log('');
317
+ console.log(C.magenta(' \u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e'));
318
+ console.log(C.magenta(' \u2502') + C.bold(' \u2726 ClaudMax Setup ') + C.magenta('\u2502'));
319
+ console.log(C.magenta(' \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510'));
320
+ console.log('');
321
+ }
322
+
323
+ function printHelp() {
324
+ console.log(`
325
+ ${C.bold('Usage:')} npx claudmax [options]
326
+
327
+ ${C.bold('Options:')}
328
+ --api-key <key> Your ClaudMax API key (required in non-interactive mode)
329
+ --ide <ides> Comma-separated IDEs: claude-code,vscode,cursor,windsurf,cline,roo
330
+ Or "all" to configure every supported IDE
331
+ Or "auto" to auto-detect installed IDEs (default)
332
+ --skip-mcp Skip MCP server installation
333
+ --verify Verify API key after configuration
334
+ --help, -h Show this help message
335
+
336
+ ${C.bold('Examples:')}
337
+ npx claudmax Interactive mode
338
+ npx claudmax --api-key sk-ant-... Configure all detected IDEs
339
+ npx claudmax --api-key sk-ant-... --ide all Configure all IDEs
340
+ npx claudmax --api-key sk-ant-... --ide claude-code,cursor
341
+ npx claudmax --api-key sk-ant-... --ide all --verify
342
+
343
+ ${C.bold('Supported IDEs:')}
344
+ claude-code - Claude Code CLI
345
+ vscode - VS Code with Claude extension
346
+ cursor - Cursor AI editor
347
+ windsrf - Windsurf AI editor
348
+ cline - Cline VS Code extension
349
+ roo - Roo Code VS Code extension
350
+ `);
221
351
  }
222
352
 
223
- // ── MCP server install ────────────────────────────────────────────────────────
224
- function installMCPServer() {
225
- process.stdout.write(` Installing ${MCP_PKG} globally... `);
353
+ // ── MCP install ──────────────────────────────────────────────────────────
354
+ function installMCP() {
355
+ if (flags['skip-mcp']) return;
356
+ process.stdout.write(` ${ARROW} Installing ClaudMax MCP server...`);
226
357
  try {
227
- execSync('npm install -g ' + MCP_PKG, { encoding: 'utf8', timeout: 60000 });
228
- console.log(`${CHECK}`);
229
- return true;
358
+ execSync('npm install -g ' + MCP_PKG, { encoding: 'utf8', timeout: 60000, stdio: 'pipe' });
359
+ process.stdout.write(`${CHECK}\n`);
230
360
  } catch (err) {
231
- const msg = err.message || '';
232
- // Check if already installed
233
- if (msg.includes('EACCES') || msg.includes('permission')) {
234
- console.log(`${WARN} Permission denied — try: sudo npm install -g ${MCP_PKG}`);
361
+ const msg = (err.stderr || err.message || '').toLowerCase();
362
+ if (msg.includes('eacces') || msg.includes('permission')) {
363
+ process.stdout.write(`${WARN} Permission denied — run: ${C.bold('sudo npm install -g ' + MCP_PKG)}\n`);
235
364
  } else {
236
- console.log(`${WARN} npm install failed. Run manually: ${C.bold('npm install -g ' + MCP_PKG)}`);
365
+ process.stdout.write(`${WARN} npm install failed. Run manually: ${C.bold('npm install -g ' + MCP_PKG)}\n`);
237
366
  }
238
- return false;
239
367
  }
240
368
  }
241
369
 
242
- // ── Banner ────────────────────────────────────────────────────────────────────
243
- function printBanner() {
244
- console.log('');
245
- console.log(C.magenta(' ╔════════════════════════════════════════════════════════════╗'));
246
- console.log(C.magenta(' ║') + C.bold(' ⚡ ClaudMax Setup ⚡ ') + C.magenta('║'));
247
- console.log(C.magenta(' ╚════════════════════════════════════════════════════════════╝'));
248
- console.log('');
249
- }
250
-
251
- // ── Main ──────────────────────────────────────────────────────────────────────
370
+ // ── Main ──────────────────────────────────────────────────────────────────
252
371
  async function main() {
253
- const rl = createRL();
372
+ if (flags.help || flags.h || args.includes('-h')) {
373
+ printHelp();
374
+ process.exit(0);
375
+ }
376
+
254
377
  printBanner();
255
378
 
256
- // ── API key ───────────────────────────────────────────────────────────────
257
- let apiKey = await ask(rl, ` ${C.bold('Enter your ClaudMax API key')}: `);
258
- apiKey = apiKey.trim();
379
+ const rl = createRL();
380
+ const detected = detectIDESilent();
259
381
 
260
- if (!apiKey) {
261
- console.log(`\n ${CROSS} ${C.red('No API key provided. Run')} ${C.bold('npx claudmax')} ${C.red('to try again.')}`);
262
- rl.close();
263
- process.exit(1);
264
- }
382
+ // ── Parse --ide ──────────────────────────────────────────────────────
383
+ let targetIDEStr = flags.ide || 'auto';
384
+ let selectedIds = [];
265
385
 
266
- // ── IDE selection ────────────────────────────────────────────────────────
267
- const IDES = [
268
- { id: 1, name: 'Claude Code (CLI)', shortName: 'Claude Code', configure: configureClaudeCLI },
269
- { id: 2, name: 'VS Code (Claude Extension)', shortName: 'VS Code', configure: configureVSCodeClaude },
270
- { id: 3, name: 'Cursor', shortName: 'Cursor', configure: configureCursor },
271
- { id: 4, name: 'Windsurf', shortName: 'Windsurf', configure: configureWindsurf },
272
- { id: 5, name: 'Cline (VS Code Extension)', shortName: 'Cline', configure: configureCline },
273
- { id: 6, name: 'Roo Code (VS Code Extension)', shortName: 'Roo Code', configure: configureRooCode },
274
- ];
386
+ if (targetIDEStr === 'auto') {
387
+ selectedIds = detected;
388
+ if (selectedIds.length > 0) {
389
+ console.log(` ${INFO} Auto-detected IDEs: ${selectedIds.join(', ')}`);
390
+ } else {
391
+ console.log(` ${WARN} No IDEs auto-detected. Use ${C.bold('--ide all')} to configure all.`);
392
+ targetIDEStr = 'all';
393
+ }
394
+ }
275
395
 
276
- console.log('');
277
- for (const ide of IDES) {
278
- console.log(` ${C.magenta('[' + ide.id + ']')} ${ide.name}`);
396
+ if (targetIDEStr === 'all') {
397
+ selectedIds = IDES.map((i) => i.id);
398
+ } else if (targetIDEStr !== 'auto') {
399
+ selectedIds = targetIDEStr.split(',').map((s) => s.trim()).filter(Boolean);
279
400
  }
280
- console.log('');
281
- console.log(` ${C.dim("Enter numbers separated by spaces, or 'a' for all:")}`);
282
401
 
283
- const choice = await ask(rl, ` ${C.bold('Your choice')}: `);
284
- let selectedIds = [];
402
+ selectedIds = [...new Set(selectedIds)];
285
403
 
286
- if (choice.trim().toLowerCase() === 'a') {
287
- selectedIds = IDES.map((ide) => ide.id);
288
- } else if (choice.trim()) {
289
- selectedIds = choice.trim().split(/[\s,]+/).map(Number).filter((n) => n >= 1 && n <= IDES.length);
404
+ // ── API key ──────────────────────────────────────────────────────────
405
+ let apiKey = (flags['api-key'] || flags.apiKey || '').trim();
406
+
407
+ if (!apiKey) {
408
+ if (!process.stdin.isTTY) {
409
+ console.log(` ${CROSS} ${C.red('API key required. Use:')} ${C.bold('--api-key sk-ant-...')}\n`);
410
+ rl.close();
411
+ process.exit(1);
412
+ }
413
+ process.stdout.write(` ${C.bold('Enter your ClaudMax API key')}\n`);
414
+ process.stdout.write(` ${C.dim('Get your key at:')} ${C.cyan('https://claudmax.pro/check-usage')}\n\n`);
415
+ while (!apiKey.trim()) {
416
+ apiKey = await ask(rl, ` ${C.bold('API Key:')} `);
417
+ if (!apiKey.trim()) console.log(` ${CROSS} API key cannot be empty.\n`);
418
+ }
290
419
  }
291
420
 
292
- console.log('');
421
+ apiKey = apiKey.trim();
422
+ process.stdout.write(` ${CHECK} API key set: ${C.dim(apiKey.slice(0, 10) + '...' + apiKey.slice(-4))}\n\n`);
293
423
 
294
- // ── Configure IDEs ───────────────────────────────────────────────────────
424
+ // ── Configure IDEs ────────────────────────────────────────────────────
295
425
  if (selectedIds.length > 0) {
426
+ process.stdout.write(` ${C.bold('Configuring:')} ${selectedIds.map(id => {
427
+ const ide = IDES.find(i => i.id === id);
428
+ return ide ? ide.name : id;
429
+ }).join(', ')}\n\n`);
430
+
296
431
  const selectedIDEs = IDES.filter((ide) => selectedIds.includes(ide.id));
297
432
  for (const ide of selectedIDEs) {
298
433
  try {
299
434
  ide.configure(apiKey);
300
435
  } catch (err) {
301
- console.log(` ${CROSS} ${C.red('Failed to configure')} ${ide.name}: ${err.message}`);
436
+ process.stdout.write(` ${CROSS} Failed: ${err.message}\n`);
302
437
  }
303
438
  }
304
- console.log('');
305
439
  } else {
306
- console.log(` ${WARN} ${C.yellow('No IDEs selected — skipping configuration.')}`);
307
- console.log('');
440
+ console.log(` ${WARN} No IDEs selected.\n`);
308
441
  }
309
442
 
310
- // ── Install MCP server ───────────────────────────────────────────────────
311
- installMCPServer();
312
443
  console.log('');
313
444
 
314
- // ── Verify API key ───────────────────────────────────────────────────────
315
- process.stdout.write(` Verifying connection to ClaudMax API... `);
316
- const result = await verifyConnection(apiKey);
445
+ // ── Install MCP ──────────────────────────────────────────────────────
446
+ installMCP();
447
+ console.log('');
317
448
 
318
- if (result.ok && result.status === 200) {
319
- console.log(`${CHECK} ${C.green('API key is valid.')}`);
320
- } else if (result.ok && result.status === 401) {
321
- console.log(`${CROSS} ${C.red('Invalid API key.')}`);
322
- rl.close();
323
- process.exit(1);
324
- } else {
325
- console.log(`${WARN} ${C.yellow('Could not verify — check your network.')}`);
449
+ // ── Verify ──────────────────────────────────────────────────────────
450
+ if (flags.verify || flags.v) {
451
+ process.stdout.write(` ${ARROW} Verifying connection...`);
452
+ const result = await verifyConnection(apiKey);
453
+ if (result.ok) {
454
+ process.stdout.write(`\r ${CHECK} Connected — API key is valid.\n`);
455
+ } else if (result.status === 401) {
456
+ process.stdout.write(`\r ${CROSS} Invalid API key.\n`);
457
+ rl.close();
458
+ process.exit(1);
459
+ } else {
460
+ process.stdout.write(`\r ${WARN} HTTP ${result.status || '?'} — could not verify.\n`);
461
+ }
462
+ console.log('');
326
463
  }
327
464
 
328
- // ── Summary ──────────────────────────────────────────────────────────────
329
- console.log('');
330
- console.log(C.magenta(' ╔════════════════════════════════════════════════════════════╗'));
331
- console.log(C.magenta('') + C.green(' ✓ Setup complete! ') + C.magenta(''));
332
- console.log(C.magenta(' ║') + C.dim(' Restart your IDE(s) to apply. ') + C.magenta(''));
333
- console.log(C.magenta(' ╚════════════════════════════════════════════════════════════╝'));
465
+ // ── Summary ───────────────────────────────────────────────────────────
466
+ console.log(` ${C.magenta('\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510')}`);
467
+ console.log(` ${C.magenta('\u2502')} ${C.green('\u2713')} Setup complete! ${C.magenta('\u2502')}`);
468
+ console.log(` ${C.magenta('\u2502')} Restart your IDE(s) to apply changes. ${C.magenta('\u2502')}`);
469
+ console.log(` ${C.magenta('\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518')}`);
334
470
  console.log('');
335
471
 
336
472
  rl.close();
337
473
  }
338
474
 
339
475
  main().catch((err) => {
340
- console.error('\n' + C.red('\u2717 Fatal error: ' + (err && err.message ? err.message : err)));
476
+ console.error(`\n${C.red('\u2717 Fatal error:')} ${err.message}\n`);
341
477
  process.exit(1);
342
478
  });
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "claudmax",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "ClaudMax CLI — Configure Claude Code, Cursor, Windsurf, Cline, and Roo Code to use ClaudMax API gateway with one command",
5
5
  "main": "index.js",
6
6
  "bin": {
7
- "claudmax": "./index.js"
7
+ "claudmax": "index.js"
8
8
  },
9
9
  "scripts": {
10
10
  "start": "node ./index.js"