polydev-ai 1.8.74 → 1.8.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mcp/login.js CHANGED
@@ -1,169 +1,396 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Polydev CLI Login - Browser-based authentication for Polydev
4
+ * Polydev CLI - Clean, professional onboarding
5
+ * Works in: Claude Code, Cursor, Cline, VS Code, any terminal
5
6
  *
6
- * Usage: npx polydev-ai login
7
- *
8
- * This script:
9
- * 1. Starts a local HTTP server to receive the callback
10
- * 2. Opens the browser to polydev.ai/auth/cli
11
- * 3. After authentication, receives the token via callback
12
- * 4. Saves the token to your shell config (~/.zshrc or ~/.bashrc)
7
+ * Usage:
8
+ * npx polydev-ai # Full setup
9
+ * npx polydev-ai login # Just login
10
+ * npx polydev-ai status # Show status
11
+ * npx polydev-ai models # Open models page
13
12
  */
14
13
 
15
14
  const fs = require('fs');
16
15
  const path = require('path');
17
16
  const os = require('os');
18
17
  const http = require('http');
19
- const { execFile } = require('child_process');
18
+ const { execFile, exec } = require('child_process');
19
+ const { promisify } = require('util');
20
+
21
+ const execAsync = promisify(exec);
22
+
23
+ // CLI tool definitions
24
+ const CLI_TOOLS = [
25
+ { id: 'claude_code', name: 'Claude Code', command: 'claude', desc: 'Anthropic' },
26
+ { id: 'codex_cli', name: 'Codex CLI', command: 'codex', desc: 'OpenAI' },
27
+ { id: 'gemini_cli', name: 'Gemini CLI', command: 'gemini', desc: 'Google' },
28
+ { id: 'aider', name: 'Aider', command: 'aider', desc: 'AI Pair Programming' }
29
+ ];
20
30
 
21
31
  const command = process.argv[2];
22
32
 
33
+ // Route commands
23
34
  if (command === 'status') {
24
- showStatus();
35
+ showFullStatus();
36
+ } else if (command === 'login') {
37
+ runLogin();
38
+ } else if (command === 'setup') {
39
+ runFullSetup();
25
40
  } else if (command === 'help' || command === '--help' || command === '-h') {
26
41
  showHelp();
42
+ } else if (command === 'models') {
43
+ openModelsPage();
27
44
  } else {
28
- runLogin();
45
+ smartOnboarding();
29
46
  }
30
47
 
31
48
  function showHelp() {
32
49
  console.log(`
33
- Polydev AI Login
50
+ Polydev CLI - Multi-model AI perspectives
34
51
 
35
52
  Commands:
36
- login Authenticate with Polydev (opens browser)
37
- status Show current authentication status
38
- help Show this help message
39
-
40
- Usage:
41
- npx polydev-ai login # Opens browser to authenticate
42
- npx polydev-ai status # Check if authenticated
53
+ npx polydev-ai Full setup (login + detect tools)
54
+ npx polydev-ai login Authenticate via browser
55
+ npx polydev-ai status Show current status
56
+ npx polydev-ai models Open models configuration
57
+ npx polydev-ai help Show this help
43
58
 
44
59
  After login, restart your terminal or run: source ~/.zshrc
60
+
61
+ Links:
62
+ Dashboard: https://polydev.ai/dashboard
63
+ Docs: https://polydev.ai/docs
45
64
  `);
46
65
  }
47
66
 
48
- function showStatus() {
49
- loadEnvFile(path.join(os.homedir(), '.polydev.env'));
50
- loadEnvFile(path.join(os.homedir(), '.zshrc'));
51
-
52
- const token = process.env.POLYDEV_USER_TOKEN;
53
- if (token && token.startsWith('pd_')) {
54
- console.log('✓ Authenticated with Polydev');
55
- console.log(` Token: ${token.slice(0, 12)}...${token.slice(-4)}`);
67
+ async function smartOnboarding() {
68
+ const token = getExistingToken();
69
+ if (token) {
70
+ await showFullStatus();
56
71
  } else {
57
- console.log('✗ Not authenticated');
58
- console.log(' Run: npx polydev-ai login');
72
+ await runFullSetup();
59
73
  }
60
74
  }
61
75
 
62
- function loadEnvFile(filePath) {
63
- try {
64
- if (!fs.existsSync(filePath)) return;
65
- const content = fs.readFileSync(filePath, 'utf8');
66
- const match = content.match(/POLYDEV_USER_TOKEN[=\s]["']?([^"'\n]+)["']?/);
67
- if (match && !process.env.POLYDEV_USER_TOKEN) {
68
- process.env.POLYDEV_USER_TOKEN = match[1];
76
+ async function runFullSetup() {
77
+ printHeader();
78
+
79
+ // Step 1: Authentication
80
+ console.log('STEP 1 of 3: Authentication');
81
+ console.log('─'.repeat(40));
82
+
83
+ let token = getExistingToken();
84
+
85
+ if (token) {
86
+ console.log('[OK] Already authenticated');
87
+ console.log(` Token: ${token.slice(0, 12)}...${token.slice(-4)}\n`);
88
+ } else {
89
+ console.log('[..] Not authenticated - opening browser...\n');
90
+ token = await runLoginFlow();
91
+
92
+ if (!token) {
93
+ console.log('\n[!] Login cancelled or timed out.');
94
+ console.log(' Run: npx polydev-ai login\n');
95
+ return;
69
96
  }
70
- } catch (e) {
71
- // ignore
72
97
  }
98
+
99
+ // Step 2: Detect CLI tools
100
+ console.log('\nSTEP 2 of 3: Local AI Tools');
101
+ console.log('─'.repeat(40));
102
+
103
+ const cliStatus = await detectAllCLIs();
104
+ printCLIStatus(cliStatus);
105
+
106
+ // Step 3: Verify API
107
+ console.log('\nSTEP 3 of 3: API Connection');
108
+ console.log('─'.repeat(40));
109
+
110
+ const apiStatus = await verifyAPIConnection(token);
111
+ printAPIStatus(apiStatus);
112
+
113
+ // Summary
114
+ printSetupComplete(token, cliStatus, apiStatus);
115
+ }
116
+
117
+ function printHeader() {
118
+ console.log(`
119
+ ___ ____ _ _ ___ ____ _ _
120
+ |__] | | | | | | |__ | |
121
+ | |__| |__ | | |___| \\/
122
+
123
+ Multi-model AI: GPT-5 | Claude | Gemini | Grok
124
+ ${'─'.repeat(50)}
125
+ `);
73
126
  }
74
127
 
75
128
  async function runLogin() {
76
- console.log('\n🔐 Polydev Authentication\n');
77
-
78
- const server = http.createServer((req, res) => {
79
- const url = new URL(req.url, `http://localhost`);
80
-
81
- if (req.method === 'OPTIONS') {
82
- res.writeHead(204, {
83
- 'Access-Control-Allow-Origin': '*',
84
- 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
85
- 'Access-Control-Allow-Headers': 'Content-Type'
86
- });
87
- res.end();
88
- return;
89
- }
129
+ console.log('\nPolydev Authentication\n');
130
+ const token = await runLoginFlow();
90
131
 
91
- if (url.pathname === '/callback') {
92
- const token = url.searchParams.get('token');
132
+ if (token) {
133
+ console.log('\n[OK] Authentication successful!');
134
+ console.log(' Restart your terminal or run: source ~/.zshrc\n');
135
+ }
136
+ }
93
137
 
94
- if (token && token.startsWith('pd_')) {
95
- const savedTo = saveToken(token);
138
+ function runLoginFlow() {
139
+ return new Promise((resolve) => {
140
+ const server = http.createServer((req, res) => {
141
+ const url = new URL(req.url, `http://localhost`);
96
142
 
97
- res.writeHead(200, {
98
- 'Content-Type': 'text/html',
99
- 'Access-Control-Allow-Origin': '*'
143
+ if (req.method === 'OPTIONS') {
144
+ res.writeHead(204, {
145
+ 'Access-Control-Allow-Origin': '*',
146
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
147
+ 'Access-Control-Allow-Headers': 'Content-Type'
100
148
  });
101
- res.end(`
102
- <!DOCTYPE html>
103
- <html>
104
- <head>
105
- <title>Polydev - Success</title>
106
- <style>
107
- body { font-family: system-ui, -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #fff; }
108
- .container { text-align: center; padding: 40px; max-width: 400px; }
109
- h1 { color: #000; margin-bottom: 16px; font-size: 24px; }
110
- p { color: #666; margin: 8px 0; }
111
- .success { color: #16a34a; font-size: 48px; margin-bottom: 16px; }
112
- code { background: #f5f5f5; padding: 2px 8px; border-radius: 4px; font-size: 14px; }
113
- </style>
114
- </head>
115
- <body>
116
- <div class="container">
117
- <div class="success">✓</div>
118
- <h1>Authenticated!</h1>
119
- <p>Token saved to your shell config.</p>
120
- <p>Restart your terminal or run:</p>
121
- <p><code>source ~/.zshrc</code></p>
122
- <p style="margin-top: 24px; font-size: 14px;">You can close this window.</p>
123
- </div>
124
- </body>
125
- </html>
126
- `);
149
+ res.end();
150
+ return;
151
+ }
152
+
153
+ if (url.pathname === '/callback') {
154
+ const token = url.searchParams.get('token');
155
+
156
+ if (token && token.startsWith('pd_')) {
157
+ const savedTo = saveToken(token);
158
+
159
+ res.writeHead(200, {
160
+ 'Content-Type': 'text/html; charset=utf-8',
161
+ 'Access-Control-Allow-Origin': '*'
162
+ });
163
+ res.end(getSuccessHTML());
164
+
165
+ console.log(`[OK] Token saved to ${savedTo}`);
166
+
167
+ setTimeout(() => {
168
+ server.close();
169
+ resolve(token);
170
+ }, 500);
171
+ } else {
172
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
173
+ res.end('Invalid or missing token');
174
+ }
175
+ } else {
176
+ res.writeHead(404);
177
+ res.end('Not found');
178
+ }
179
+ });
180
+
181
+ server.listen(0, 'localhost', () => {
182
+ const port = server.address().port;
183
+ const callbackUrl = `http://localhost:${port}/callback`;
184
+ const authUrl = `https://polydev.ai/auth/cli?callback=${encodeURIComponent(callbackUrl)}&redirect=claude-code`;
185
+
186
+ console.log('Opening browser for authentication...');
187
+ console.log(`If browser doesn\'t open, visit:\n${authUrl}\n`);
127
188
 
128
- console.log('✓ Token saved successfully!');
129
- console.log(` Saved to: ${savedTo}`);
130
- console.log('\n Restart your terminal or run: source ~/.zshrc\n');
189
+ openBrowser(authUrl);
190
+
191
+ setTimeout(() => {
192
+ server.close();
193
+ resolve(null);
194
+ }, 5 * 60 * 1000);
195
+ });
196
+
197
+ server.on('error', (err) => {
198
+ console.error('[!] Server error:', err.message);
199
+ resolve(null);
200
+ });
201
+ });
202
+ }
131
203
 
132
- setTimeout(() => {
133
- server.close();
134
- process.exit(0);
135
- }, 500);
204
+ async function detectAllCLIs() {
205
+ const results = {};
206
+
207
+ for (const tool of CLI_TOOLS) {
208
+ try {
209
+ const { stdout } = await execAsync(`which ${tool.command} 2>/dev/null`);
210
+ const cliPath = stdout.trim();
211
+
212
+ if (cliPath) {
213
+ let version = 'installed';
214
+ try {
215
+ const { stdout: versionOut } = await execAsync(`${tool.command} --version 2>/dev/null || ${tool.command} -v 2>/dev/null`);
216
+ version = versionOut.trim().split('\n')[0].substring(0, 30);
217
+ } catch (e) {}
218
+
219
+ let authenticated = false;
220
+ try {
221
+ if (tool.id === 'claude_code') {
222
+ const { stdout: authOut } = await execAsync(`${tool.command} auth status 2>/dev/null || echo "not"`);
223
+ authenticated = !authOut.toLowerCase().includes('not');
224
+ } else if (tool.id === 'codex_cli') {
225
+ authenticated = !!process.env.OPENAI_API_KEY;
226
+ } else if (tool.id === 'gemini_cli') {
227
+ authenticated = !!process.env.GOOGLE_API_KEY;
228
+ }
229
+ } catch (e) {}
230
+
231
+ results[tool.id] = { available: true, authenticated, version, path: cliPath, tool };
232
+ console.log(`[OK] ${tool.name.padEnd(12)} ${version}`);
136
233
  } else {
137
- res.writeHead(400, { 'Content-Type': 'text/plain' });
138
- res.end('Invalid or missing token');
234
+ throw new Error('Not found');
139
235
  }
236
+ } catch (e) {
237
+ results[tool.id] = { available: false, authenticated: false, tool };
238
+ console.log(`[--] ${tool.name.padEnd(12)} not installed`);
239
+ }
240
+ }
241
+
242
+ return results;
243
+ }
244
+
245
+ function printCLIStatus(cliStatus) {
246
+ const available = Object.values(cliStatus).filter(s => s.available);
247
+ const authenticated = available.filter(s => s.authenticated);
248
+
249
+ if (available.length === 0) {
250
+ console.log('\n No local CLI tools detected.');
251
+ console.log(' Install Claude Code or Codex CLI to use local tools (free).');
252
+ } else {
253
+ console.log(`\n ${available.length} tool(s) found, ${authenticated.length} authenticated`);
254
+ console.log(' Local tools = FREE (no API credits used)');
255
+ }
256
+ }
257
+
258
+ async function verifyAPIConnection(token) {
259
+ try {
260
+ const response = await fetch('https://www.polydev.ai/api/mcp', {
261
+ method: 'POST',
262
+ headers: {
263
+ 'Content-Type': 'application/json',
264
+ 'Authorization': `Bearer ${token}`,
265
+ 'User-Agent': 'polydev-cli/1.0.0'
266
+ },
267
+ body: JSON.stringify({ action: 'check_status' })
268
+ });
269
+
270
+ if (response.ok) {
271
+ const data = await response.json();
272
+ return {
273
+ connected: true,
274
+ email: data.email,
275
+ credits: data.credits_remaining,
276
+ tier: data.subscription_tier || 'Free',
277
+ models: data.enabled_models || ['GPT-5', 'Gemini', 'Grok', 'GLM']
278
+ };
140
279
  } else {
141
- res.writeHead(404);
142
- res.end('Not found');
280
+ return { connected: false, error: 'Invalid token' };
143
281
  }
144
- });
282
+ } catch (error) {
283
+ return { connected: false, error: error.message };
284
+ }
285
+ }
145
286
 
146
- server.listen(0, 'localhost', () => {
147
- const port = server.address().port;
148
- const callbackUrl = `http://localhost:${port}/callback`;
149
- const authUrl = `https://polydev.ai/auth/cli?callback=${encodeURIComponent(callbackUrl)}&redirect=claude-code`;
287
+ function printAPIStatus(apiStatus) {
288
+ if (apiStatus.connected) {
289
+ console.log(`[OK] Connected to Polydev API`);
290
+ console.log(` Account: ${apiStatus.email || 'Connected'}`);
291
+ console.log(` Credits: ${apiStatus.credits?.toLocaleString() || 0} | Tier: ${apiStatus.tier}`);
292
+ } else {
293
+ console.log(`[!] Could not verify API connection`);
294
+ console.log(` Error: ${apiStatus.error}`);
295
+ }
296
+ }
150
297
 
151
- console.log('Opening browser for authentication...');
152
- console.log(`\nIf browser doesn't open, visit:\n${authUrl}\n`);
298
+ function printSetupComplete(token, cliStatus, apiStatus) {
299
+ const availableCLIs = Object.values(cliStatus).filter(s => s.available);
153
300
 
154
- openBrowser(authUrl);
301
+ console.log(`
302
+ ${'═'.repeat(50)}
303
+ SETUP COMPLETE
304
+ ${'═'.repeat(50)}
155
305
 
156
- setTimeout(() => {
157
- console.log('\n✗ Login timed out. Please try again.\n');
158
- server.close();
159
- process.exit(1);
160
- }, 5 * 60 * 1000);
161
- });
306
+ What's ready:`);
162
307
 
163
- server.on('error', (err) => {
164
- console.error('Server error:', err.message);
165
- process.exit(1);
166
- });
308
+ if (availableCLIs.length > 0) {
309
+ console.log('\n Local Tools (FREE):');
310
+ for (const cli of availableCLIs) {
311
+ const status = cli.authenticated ? 'ready' : 'needs auth';
312
+ console.log(` - ${cli.tool.name} [${status}]`);
313
+ }
314
+ }
315
+
316
+ if (apiStatus.connected) {
317
+ console.log(`\n Polydev API (${apiStatus.credits?.toLocaleString() || 0} credits):`);
318
+ console.log(' - GPT-5, Claude, Gemini, Grok in parallel');
319
+ }
320
+
321
+ console.log(`
322
+ Next steps:
323
+
324
+ 1. Restart terminal or run: source ~/.zshrc
325
+ 2. Start your IDE (Claude Code, Cursor, Cline, etc.)
326
+ 3. Use "get_perspectives" tool or /polydev command
327
+
328
+ Links:
329
+ Dashboard: https://polydev.ai/dashboard
330
+ Models: https://polydev.ai/dashboard/models
331
+ `);
332
+ }
333
+
334
+ async function showFullStatus() {
335
+ console.log('\nPolydev Status');
336
+ console.log('─'.repeat(40));
337
+
338
+ const token = getExistingToken();
339
+
340
+ console.log('\nAuthentication:');
341
+ if (token) {
342
+ const apiStatus = await verifyAPIConnection(token);
343
+ if (apiStatus.connected) {
344
+ console.log(` [OK] ${apiStatus.email || 'Authenticated'}`);
345
+ console.log(` Credits: ${apiStatus.credits?.toLocaleString() || 0} | Tier: ${apiStatus.tier}`);
346
+ } else {
347
+ console.log(' [!] Token found but invalid');
348
+ console.log(' Run: npx polydev-ai login');
349
+ }
350
+ } else {
351
+ console.log(' [--] Not authenticated');
352
+ console.log(' Run: npx polydev-ai login');
353
+ }
354
+
355
+ console.log('\nLocal AI Tools (free):');
356
+ await detectAllCLIs();
357
+
358
+ if (token) {
359
+ console.log('\nAPI Models (uses credits):');
360
+ console.log(' GPT-5, Gemini, Grok, GLM - query in parallel');
361
+ console.log(' Configure: https://polydev.ai/dashboard/models');
362
+ }
363
+
364
+ console.log('');
365
+ }
366
+
367
+ function openModelsPage() {
368
+ const url = 'https://polydev.ai/dashboard/models';
369
+ console.log(`\nOpening: ${url}\n`);
370
+ openBrowser(url);
371
+ }
372
+
373
+ // Utility functions
374
+ function getExistingToken() {
375
+ if (process.env.POLYDEV_USER_TOKEN) {
376
+ return process.env.POLYDEV_USER_TOKEN;
377
+ }
378
+
379
+ const locations = [
380
+ path.join(os.homedir(), '.polydev.env'),
381
+ path.join(os.homedir(), '.zshrc'),
382
+ path.join(os.homedir(), '.bashrc')
383
+ ];
384
+
385
+ for (const loc of locations) {
386
+ if (fs.existsSync(loc)) {
387
+ const content = fs.readFileSync(loc, 'utf8');
388
+ const match = content.match(/POLYDEV_USER_TOKEN[=\s]["']?([^"'\n]+)["']?/);
389
+ if (match) return match[1];
390
+ }
391
+ }
392
+
393
+ return null;
167
394
  }
168
395
 
169
396
  function saveToken(token) {
@@ -173,9 +400,7 @@ function saveToken(token) {
173
400
  path.join(os.homedir(), '.profile');
174
401
 
175
402
  let content = '';
176
- try {
177
- content = fs.readFileSync(rcFile, 'utf8');
178
- } catch (e) {}
403
+ try { content = fs.readFileSync(rcFile, 'utf8'); } catch (e) {}
179
404
 
180
405
  const lines = content.split('\n').filter(line =>
181
406
  !line.trim().startsWith('export POLYDEV_USER_TOKEN') &&
@@ -192,7 +417,6 @@ function saveToken(token) {
192
417
  }
193
418
 
194
419
  function openBrowser(url) {
195
- // Use execFile with explicit command and arguments for safety
196
420
  const platform = process.platform;
197
421
  let cmd, args;
198
422
 
@@ -209,8 +433,61 @@ function openBrowser(url) {
209
433
 
210
434
  execFile(cmd, args, (err) => {
211
435
  if (err) {
212
- console.log('Could not open browser automatically.');
213
- console.log(`Please open this URL manually: ${url}`);
436
+ console.log('[!] Could not open browser automatically.');
437
+ console.log(` Please open: ${url}`);
214
438
  }
215
439
  });
216
440
  }
441
+
442
+ function getSuccessHTML() {
443
+ return `<!DOCTYPE html>
444
+ <html>
445
+ <head>
446
+ <meta charset="utf-8">
447
+ <title>Polydev - Success</title>
448
+ <style>
449
+ * { box-sizing: border-box; margin: 0; padding: 0; }
450
+ body {
451
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
452
+ display: flex; justify-content: center; align-items: center;
453
+ height: 100vh; background: #f5f5f5;
454
+ }
455
+ .container {
456
+ text-align: center; padding: 48px; max-width: 400px;
457
+ background: white; border-radius: 8px;
458
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
459
+ }
460
+ .logo { font-size: 24px; font-weight: 700; color: #111; margin-bottom: 24px; letter-spacing: -0.5px; }
461
+ .check {
462
+ width: 48px; height: 48px; border: 3px solid #111; border-radius: 50%;
463
+ display: flex; align-items: center; justify-content: center;
464
+ margin: 0 auto 24px; font-size: 24px;
465
+ }
466
+ h1 { color: #111; font-size: 20px; font-weight: 600; margin-bottom: 12px; }
467
+ p { color: #666; font-size: 14px; line-height: 1.6; margin-bottom: 8px; }
468
+ .steps {
469
+ background: #f9f9f9; border-radius: 6px; padding: 16px;
470
+ margin: 20px 0; text-align: left;
471
+ }
472
+ .step { color: #444; font-size: 13px; margin-bottom: 8px; }
473
+ .step:last-child { margin-bottom: 0; }
474
+ code { background: #eee; padding: 2px 6px; border-radius: 3px; font-size: 12px; }
475
+ .close { color: #999; font-size: 12px; margin-top: 20px; }
476
+ </style>
477
+ </head>
478
+ <body>
479
+ <div class="container">
480
+ <div class="logo">Polydev</div>
481
+ <div class="check">✓</div>
482
+ <h1>Authentication Complete</h1>
483
+ <p>Your token has been saved automatically.</p>
484
+ <div class="steps">
485
+ <div class="step">1. Restart terminal or run <code>source ~/.zshrc</code></div>
486
+ <div class="step">2. Start your IDE (Claude Code, Cursor, etc.)</div>
487
+ <div class="step">3. Use the <code>get_perspectives</code> tool</div>
488
+ </div>
489
+ <p class="close">You can close this window.</p>
490
+ </div>
491
+ </body>
492
+ </html>`;
493
+ }
package/mcp/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-perspectives",
3
- "version": "1.4.0",
3
+ "version": "1.8.76",
4
4
  "description": "Multi-model AI perspectives - query GPT 5.2, Claude Opus 4.5, Gemini 3, and Grok 4.1 simultaneously. Get diverse perspectives when stuck or need enhanced reasoning. Achieved 74.6% on SWE-bench Verified.",
5
5
  "author": "Polydev AI",
6
6
  "license": "MIT",
@@ -248,20 +248,7 @@ function cleanCliResponse(content) {
248
248
  class StdioMCPWrapper {
249
249
  constructor() {
250
250
  this.userToken = process.env.POLYDEV_USER_TOKEN;
251
- if (!this.userToken) {
252
- console.error('\n╔════════════════════════════════════════════════════════════════╗');
253
- console.error('║ POLYDEV_USER_TOKEN is not set ║');
254
- console.error('╠════════════════════════════════════════════════════════════════╣');
255
- console.error('║ Get your free token at: ║');
256
- console.error('║ 👉 https://polydev.ai/dashboard/mcp-tokens ║');
257
- console.error('║ ║');
258
- console.error('║ Then add to ~/.zshrc or ~/.bashrc: ║');
259
- console.error('║ export POLYDEV_USER_TOKEN="pd_your_token_here" ║');
260
- console.error('║ ║');
261
- console.error('║ Restart your terminal and Claude Code after adding the token ║');
262
- console.error('╚════════════════════════════════════════════════════════════════╝\n');
263
- process.exit(1);
264
- }
251
+ this.isAuthenticated = !!this.userToken;
265
252
 
266
253
  // Server URL for API calls
267
254
  this.serverUrl = 'https://www.polydev.ai/api/mcp';
@@ -272,6 +259,14 @@ class StdioMCPWrapper {
272
259
  // Load manifest for tool definitions
273
260
  this.loadManifest();
274
261
 
262
+ // Log startup status
263
+ if (this.userToken) {
264
+ console.error('[Polydev] Starting with authentication token');
265
+ } else {
266
+ console.error('[Polydev] Starting without token - login required');
267
+ console.error('[Polydev] Use the "login" tool or run: npx polydev-ai');
268
+ }
269
+
275
270
  // Smart refresh scheduler (will be started after initialization)
276
271
  this.refreshScheduler = null;
277
272
 
@@ -287,6 +282,9 @@ class StdioMCPWrapper {
287
282
  this.perspectivesPerMessage = 2; // Default to 2 perspectives
288
283
  this.modelPreferencesCacheTime = null;
289
284
  this.MODEL_PREFERENCES_CACHE_TTL = 5 * 60 * 1000; // 5 minutes cache
285
+
286
+ // Login server reference (for cleanup)
287
+ this._loginServer = null;
290
288
  }
291
289
 
292
290
  loadManifest() {
@@ -332,9 +330,36 @@ class StdioMCPWrapper {
332
330
  };
333
331
 
334
332
  case 'tools/call':
335
- // Handle get_perspectives with local CLIs + remote perspectives
336
333
  const toolName = params.name;
337
334
 
335
+ // These tools work without authentication
336
+ const noAuthTools = ['login', 'get_auth_status', 'polydev.login', 'polydev.get_auth_status'];
337
+
338
+ if (noAuthTools.includes(toolName)) {
339
+ if (toolName === 'login' || toolName === 'polydev.login') {
340
+ return await this.handleLoginTool(params, id);
341
+ }
342
+ if (toolName === 'get_auth_status' || toolName === 'polydev.get_auth_status') {
343
+ return await this.handleGetAuthStatus(params, id);
344
+ }
345
+ }
346
+
347
+ // All other tools require authentication
348
+ if (!this.isAuthenticated) {
349
+ return {
350
+ jsonrpc: '2.0',
351
+ id,
352
+ result: {
353
+ content: [{
354
+ type: 'text',
355
+ text: `Authentication required.\n\nTo login, either:\n1. Use the "login" tool (opens browser)\n2. Run in terminal: npx polydev-ai\n\nAfter login, restart your IDE.`
356
+ }],
357
+ isError: true
358
+ }
359
+ };
360
+ }
361
+
362
+ // Handle get_perspectives with local CLIs + remote perspectives
338
363
  if (toolName === 'get_perspectives' || toolName === 'polydev.get_perspectives') {
339
364
  return await this.handleGetPerspectivesWithCLIs(params, id);
340
365
  }
@@ -370,6 +395,277 @@ class StdioMCPWrapper {
370
395
  }
371
396
  }
372
397
 
398
+ /**
399
+ * Handle login tool - opens browser for authentication
400
+ */
401
+ async handleLoginTool(params, id) {
402
+ const http = require('http');
403
+ const { execFile } = require('child_process');
404
+
405
+ return new Promise((resolve) => {
406
+ // Check if already authenticated
407
+ if (this.isAuthenticated && this.userToken) {
408
+ resolve({
409
+ jsonrpc: '2.0',
410
+ id,
411
+ result: {
412
+ content: [{
413
+ type: 'text',
414
+ text: `Already authenticated.\n\nYour token is configured. Use get_perspectives to query multiple AI models.\n\nTo re-login, run in terminal: npx polydev-ai login`
415
+ }]
416
+ }
417
+ });
418
+ return;
419
+ }
420
+
421
+ const server = http.createServer((req, res) => {
422
+ const url = new URL(req.url, `http://localhost`);
423
+
424
+ if (req.method === 'OPTIONS') {
425
+ res.writeHead(204, {
426
+ 'Access-Control-Allow-Origin': '*',
427
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
428
+ 'Access-Control-Allow-Headers': 'Content-Type'
429
+ });
430
+ res.end();
431
+ return;
432
+ }
433
+
434
+ if (url.pathname === '/callback') {
435
+ const token = url.searchParams.get('token');
436
+
437
+ if (token && token.startsWith('pd_')) {
438
+ // Save token
439
+ this.saveTokenToFiles(token);
440
+ this.userToken = token;
441
+ this.isAuthenticated = true;
442
+
443
+ res.writeHead(200, {
444
+ 'Content-Type': 'text/html; charset=utf-8',
445
+ 'Access-Control-Allow-Origin': '*'
446
+ });
447
+ res.end(this.getLoginSuccessHTML());
448
+
449
+ console.error('[Polydev] Login successful, token saved');
450
+
451
+ setTimeout(() => {
452
+ server.close();
453
+ resolve({
454
+ jsonrpc: '2.0',
455
+ id,
456
+ result: {
457
+ content: [{
458
+ type: 'text',
459
+ text: `Login successful!\n\nYour token has been saved to ~/.zshrc and ~/.polydev.env\n\nIMPORTANT: Restart your IDE to use the new token.\n\nAfter restart, use get_perspectives to query GPT-5, Claude, Gemini, and Grok in parallel.`
460
+ }]
461
+ }
462
+ });
463
+ }, 500);
464
+ } else {
465
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
466
+ res.end('Invalid or missing token');
467
+ }
468
+ } else {
469
+ res.writeHead(404);
470
+ res.end('Not found');
471
+ }
472
+ });
473
+
474
+ server.listen(0, 'localhost', () => {
475
+ const port = server.address().port;
476
+ const callbackUrl = `http://localhost:${port}/callback`;
477
+ const authUrl = `https://polydev.ai/auth/cli?callback=${encodeURIComponent(callbackUrl)}&redirect=ide-plugin`;
478
+
479
+ console.error(`[Polydev] Opening browser for authentication: ${authUrl}`);
480
+
481
+ // Open browser
482
+ const platform = process.platform;
483
+ let cmd, args;
484
+ if (platform === 'darwin') {
485
+ cmd = 'open';
486
+ args = [authUrl];
487
+ } else if (platform === 'win32') {
488
+ cmd = 'cmd';
489
+ args = ['/c', 'start', '', authUrl];
490
+ } else {
491
+ cmd = 'xdg-open';
492
+ args = [authUrl];
493
+ }
494
+
495
+ execFile(cmd, args, (err) => {
496
+ if (err) {
497
+ console.error('[Polydev] Could not open browser:', err.message);
498
+ }
499
+ });
500
+
501
+ // Timeout after 5 minutes
502
+ setTimeout(() => {
503
+ server.close();
504
+ resolve({
505
+ jsonrpc: '2.0',
506
+ id,
507
+ result: {
508
+ content: [{
509
+ type: 'text',
510
+ text: `Login timed out.\n\nPlease try again or run in terminal: npx polydev-ai`
511
+ }],
512
+ isError: true
513
+ }
514
+ });
515
+ }, 5 * 60 * 1000);
516
+ });
517
+
518
+ server.on('error', (err) => {
519
+ console.error('[Polydev] Login server error:', err.message);
520
+ resolve({
521
+ jsonrpc: '2.0',
522
+ id,
523
+ result: {
524
+ content: [{
525
+ type: 'text',
526
+ text: `Login failed: ${err.message}\n\nPlease run in terminal: npx polydev-ai`
527
+ }],
528
+ isError: true
529
+ }
530
+ });
531
+ });
532
+ });
533
+ }
534
+
535
+ /**
536
+ * Handle get_auth_status tool
537
+ */
538
+ async handleGetAuthStatus(params, id) {
539
+ if (!this.isAuthenticated || !this.userToken) {
540
+ return {
541
+ jsonrpc: '2.0',
542
+ id,
543
+ result: {
544
+ content: [{
545
+ type: 'text',
546
+ text: `Not authenticated.\n\nTo login:\n1. Use the "login" tool (opens browser)\n2. Or run in terminal: npx polydev-ai\n\nAfter login, restart your IDE.`
547
+ }]
548
+ }
549
+ };
550
+ }
551
+
552
+ try {
553
+ const response = await fetch('https://www.polydev.ai/api/mcp', {
554
+ method: 'POST',
555
+ headers: {
556
+ 'Content-Type': 'application/json',
557
+ 'Authorization': `Bearer ${this.userToken}`,
558
+ 'User-Agent': 'polydev-stdio-wrapper/1.0.0'
559
+ },
560
+ body: JSON.stringify({ action: 'check_status' })
561
+ });
562
+
563
+ if (response.ok) {
564
+ const data = await response.json();
565
+ const credits = data.credits_remaining?.toLocaleString() || 0;
566
+ const tier = data.subscription_tier || 'Free';
567
+
568
+ return {
569
+ jsonrpc: '2.0',
570
+ id,
571
+ result: {
572
+ content: [{
573
+ type: 'text',
574
+ text: `Polydev Status\n${'─'.repeat(40)}\n\nAuthenticated: Yes\nAccount: ${data.email || 'Connected'}\nCredits: ${credits}\nTier: ${tier}\n\nAvailable tools:\n- get_perspectives (query multiple AI models)\n- get_cli_status (check local CLI tools)\n- send_cli_prompt (use local CLIs)\n\nDashboard: https://polydev.ai/dashboard`
575
+ }]
576
+ }
577
+ };
578
+ } else {
579
+ return {
580
+ jsonrpc: '2.0',
581
+ id,
582
+ result: {
583
+ content: [{
584
+ type: 'text',
585
+ text: `Token invalid or expired.\n\nPlease re-login:\n1. Use the "login" tool\n2. Or run: npx polydev-ai login`
586
+ }],
587
+ isError: true
588
+ }
589
+ };
590
+ }
591
+ } catch (error) {
592
+ return {
593
+ jsonrpc: '2.0',
594
+ id,
595
+ result: {
596
+ content: [{
597
+ type: 'text',
598
+ text: `Could not verify status (offline?)\n\nError: ${error.message}`
599
+ }],
600
+ isError: true
601
+ }
602
+ };
603
+ }
604
+ }
605
+
606
+ /**
607
+ * Save token to shell config files
608
+ */
609
+ saveTokenToFiles(token) {
610
+ const shell = process.env.SHELL || '/bin/zsh';
611
+ const rcFile = shell.includes('zsh') ? path.join(os.homedir(), '.zshrc') :
612
+ shell.includes('bash') ? path.join(os.homedir(), '.bashrc') :
613
+ path.join(os.homedir(), '.profile');
614
+
615
+ let content = '';
616
+ try { content = fs.readFileSync(rcFile, 'utf8'); } catch (e) {}
617
+
618
+ const lines = content.split('\n').filter(line =>
619
+ !line.trim().startsWith('export POLYDEV_USER_TOKEN') &&
620
+ !line.trim().startsWith('POLYDEV_USER_TOKEN')
621
+ );
622
+
623
+ lines.push(`export POLYDEV_USER_TOKEN="${token}"`);
624
+ fs.writeFileSync(rcFile, lines.join('\n').replace(/\n+$/, '') + '\n');
625
+
626
+ // Also save to .polydev.env
627
+ const envFile = path.join(os.homedir(), '.polydev.env');
628
+ fs.writeFileSync(envFile, `POLYDEV_USER_TOKEN="${token}"\n`);
629
+
630
+ console.error(`[Polydev] Token saved to ${rcFile} and ${envFile}`);
631
+ }
632
+
633
+ /**
634
+ * Get login success HTML page
635
+ */
636
+ getLoginSuccessHTML() {
637
+ return `<!DOCTYPE html>
638
+ <html>
639
+ <head>
640
+ <meta charset="utf-8">
641
+ <title>Polydev - Success</title>
642
+ <style>
643
+ * { box-sizing: border-box; margin: 0; padding: 0; }
644
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f5f5f5; }
645
+ .container { text-align: center; padding: 48px; max-width: 400px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
646
+ .logo { font-size: 24px; font-weight: 700; color: #111; margin-bottom: 24px; }
647
+ .check { width: 48px; height: 48px; border: 3px solid #111; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 24px; font-size: 24px; }
648
+ h1 { color: #111; font-size: 20px; font-weight: 600; margin-bottom: 12px; }
649
+ p { color: #666; font-size: 14px; line-height: 1.6; }
650
+ .important { background: #f0f0f0; padding: 16px; border-radius: 6px; margin: 20px 0; }
651
+ .important strong { color: #111; }
652
+ </style>
653
+ </head>
654
+ <body>
655
+ <div class="container">
656
+ <div class="logo">Polydev</div>
657
+ <div class="check">✓</div>
658
+ <h1>Login Successful</h1>
659
+ <p>Your token has been saved.</p>
660
+ <div class="important">
661
+ <strong>Important:</strong> Restart your IDE to use the new token.
662
+ </div>
663
+ <p>You can close this window.</p>
664
+ </div>
665
+ </body>
666
+ </html>`;
667
+ }
668
+
373
669
  async forwardToRemoteServer(request) {
374
670
  console.error(`[Stdio Wrapper] Forwarding request to remote server`);
375
671
 
@@ -1634,7 +1930,7 @@ class StdioMCPWrapper {
1634
1930
  }
1635
1931
 
1636
1932
  async start() {
1637
- console.error('Starting Polydev Stdio MCP Wrapper...');
1933
+ console.error('Starting Polydev MCP Server...');
1638
1934
 
1639
1935
  process.stdin.setEncoding('utf8');
1640
1936
  let buffer = '';
@@ -1651,7 +1947,7 @@ class StdioMCPWrapper {
1651
1947
  const request = JSON.parse(line);
1652
1948
  // Notifications have no id - don't respond to them (JSON-RPC spec)
1653
1949
  if (request.id === undefined) {
1654
- console.error(`[Stdio Wrapper] Received notification: ${request.method}`);
1950
+ console.error(`[Polydev] Received notification: ${request.method}`);
1655
1951
  continue;
1656
1952
  }
1657
1953
  const response = await this.handleRequest(request);
@@ -1664,7 +1960,7 @@ class StdioMCPWrapper {
1664
1960
  });
1665
1961
 
1666
1962
  process.stdin.on('end', () => {
1667
- console.error('Stdio MCP Wrapper shutting down...');
1963
+ console.error('Polydev MCP Server shutting down...');
1668
1964
  this.stopSmartRefreshScheduler();
1669
1965
  process.exit(0);
1670
1966
  });
@@ -1682,30 +1978,243 @@ class StdioMCPWrapper {
1682
1978
  process.exit(0);
1683
1979
  });
1684
1980
 
1685
- console.error('Stdio MCP Wrapper ready and listening on stdin...');
1981
+ console.error('Polydev MCP Server ready.\n');
1982
+
1983
+ // Run the onboarding/status check
1984
+ await this.runStartupFlow();
1686
1985
 
1687
- // Run initial CLI detection in the background so MCP handshake isn't blocked.
1688
- // The promise was already created in constructor so requests will wait for it
1689
- console.error('[Stdio Wrapper] Running initial CLI detection...');
1986
+ // Run CLI detection in background
1987
+ console.error('[Polydev] Detecting local CLI tools...');
1690
1988
 
1691
1989
  this.localForceCliDetection({})
1692
- .then(() => {
1693
- console.error('[Stdio Wrapper] Initial CLI detection completed');
1990
+ .then(async () => {
1694
1991
  this._cliDetectionComplete = true;
1992
+ // Display CLI status after detection
1993
+ await this.displayCliStatus();
1695
1994
  })
1696
1995
  .catch((error) => {
1697
- console.error('[Stdio Wrapper] Initial CLI detection failed:', error);
1698
- this._cliDetectionComplete = true; // Mark complete even on failure
1996
+ console.error('[Polydev] CLI detection failed:', error.message);
1997
+ this._cliDetectionComplete = true;
1699
1998
  })
1700
1999
  .finally(() => {
1701
- // Resolve the readiness promise so waiting requests can proceed
1702
2000
  if (this._cliDetectionResolver) {
1703
2001
  this._cliDetectionResolver();
1704
2002
  }
1705
- // Start smart refresh scheduler after initial detection attempt
1706
2003
  this.startSmartRefreshScheduler();
1707
2004
  });
1708
2005
  }
2006
+
2007
+ /**
2008
+ * Run startup flow - auto-login if no token, verify if has token
2009
+ */
2010
+ async runStartupFlow() {
2011
+ if (!this.userToken) {
2012
+ // No token - auto-open browser for login
2013
+ console.error('─'.repeat(50));
2014
+ console.error('Polydev - First Time Setup');
2015
+ console.error('─'.repeat(50));
2016
+ console.error('Opening browser for authentication...\n');
2017
+
2018
+ await this.autoLoginOnStartup();
2019
+ } else {
2020
+ // Has token - verify it
2021
+ await this.verifyAndDisplayAuth();
2022
+ }
2023
+ }
2024
+
2025
+ /**
2026
+ * Auto-login on startup (opens browser automatically)
2027
+ */
2028
+ async autoLoginOnStartup() {
2029
+ const http = require('http');
2030
+ const { execFile } = require('child_process');
2031
+
2032
+ return new Promise((resolve) => {
2033
+ const server = http.createServer((req, res) => {
2034
+ const url = new URL(req.url, `http://localhost`);
2035
+
2036
+ if (req.method === 'OPTIONS') {
2037
+ res.writeHead(204, {
2038
+ 'Access-Control-Allow-Origin': '*',
2039
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
2040
+ 'Access-Control-Allow-Headers': 'Content-Type'
2041
+ });
2042
+ res.end();
2043
+ return;
2044
+ }
2045
+
2046
+ if (url.pathname === '/callback') {
2047
+ const token = url.searchParams.get('token');
2048
+
2049
+ if (token && token.startsWith('pd_')) {
2050
+ this.saveTokenToFiles(token);
2051
+ this.userToken = token;
2052
+ this.isAuthenticated = true;
2053
+
2054
+ res.writeHead(200, {
2055
+ 'Content-Type': 'text/html; charset=utf-8',
2056
+ 'Access-Control-Allow-Origin': '*'
2057
+ });
2058
+ res.end(this.getLoginSuccessHTML());
2059
+
2060
+ console.error('[OK] Authentication successful!');
2061
+ console.error(' Token saved to ~/.zshrc and ~/.polydev.env');
2062
+ console.error('');
2063
+ console.error(' IMPORTANT: Restart your IDE to use the new token.');
2064
+ console.error('─'.repeat(50) + '\n');
2065
+
2066
+ setTimeout(() => {
2067
+ server.close();
2068
+ resolve(true);
2069
+ }, 500);
2070
+ } else {
2071
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
2072
+ res.end('Invalid or missing token');
2073
+ }
2074
+ } else {
2075
+ res.writeHead(404);
2076
+ res.end('Not found');
2077
+ }
2078
+ });
2079
+
2080
+ server.listen(0, 'localhost', () => {
2081
+ const port = server.address().port;
2082
+ const callbackUrl = `http://localhost:${port}/callback`;
2083
+ const authUrl = `https://polydev.ai/auth/cli?callback=${encodeURIComponent(callbackUrl)}&redirect=ide-plugin&auto=true`;
2084
+
2085
+ console.error('If browser does not open, visit:');
2086
+ console.error(authUrl);
2087
+ console.error('');
2088
+
2089
+ // Open browser
2090
+ const platform = process.platform;
2091
+ let cmd, args;
2092
+ if (platform === 'darwin') {
2093
+ cmd = 'open';
2094
+ args = [authUrl];
2095
+ } else if (platform === 'win32') {
2096
+ cmd = 'cmd';
2097
+ args = ['/c', 'start', '', authUrl];
2098
+ } else {
2099
+ cmd = 'xdg-open';
2100
+ args = [authUrl];
2101
+ }
2102
+
2103
+ execFile(cmd, args, (err) => {
2104
+ if (err) {
2105
+ console.error('[!] Could not open browser automatically.');
2106
+ }
2107
+ });
2108
+
2109
+ // Don't block forever - resolve after timeout
2110
+ setTimeout(() => {
2111
+ if (!this.isAuthenticated) {
2112
+ console.error('[!] Login timeout - use "login" tool to try again');
2113
+ console.error('─'.repeat(50) + '\n');
2114
+ }
2115
+ server.close();
2116
+ resolve(false);
2117
+ }, 2 * 60 * 1000); // 2 minute timeout for startup
2118
+ });
2119
+
2120
+ server.on('error', (err) => {
2121
+ console.error('[!] Could not start login server:', err.message);
2122
+ console.error(' Use "login" tool or run: npx polydev-ai');
2123
+ console.error('─'.repeat(50) + '\n');
2124
+ resolve(false);
2125
+ });
2126
+ });
2127
+ }
2128
+
2129
+ /**
2130
+ * Verify token and display auth status
2131
+ */
2132
+ async verifyAndDisplayAuth() {
2133
+ try {
2134
+ const response = await fetch('https://www.polydev.ai/api/mcp', {
2135
+ method: 'POST',
2136
+ headers: {
2137
+ 'Content-Type': 'application/json',
2138
+ 'Authorization': `Bearer ${this.userToken}`,
2139
+ 'User-Agent': 'polydev-mcp/1.0.0'
2140
+ },
2141
+ body: JSON.stringify({ action: 'check_status' })
2142
+ });
2143
+
2144
+ if (response.ok) {
2145
+ const data = await response.json();
2146
+ this.isAuthenticated = true;
2147
+ const credits = data.credits_remaining?.toLocaleString() || 0;
2148
+ const tier = data.subscription_tier || 'Free';
2149
+
2150
+ console.error('─'.repeat(50));
2151
+ console.error('Polydev - Authenticated');
2152
+ console.error('─'.repeat(50));
2153
+ console.error(`Account: ${data.email || 'Connected'}`);
2154
+ console.error(`Credits: ${credits} | Tier: ${tier}`);
2155
+ console.error('─'.repeat(50) + '\n');
2156
+ } else {
2157
+ this.isAuthenticated = false;
2158
+ console.error('─'.repeat(50));
2159
+ console.error('Polydev - Token Invalid');
2160
+ console.error('─'.repeat(50));
2161
+ console.error('Your token may have expired.');
2162
+ console.error('Use the "login" tool or run: npx polydev-ai login');
2163
+ console.error('─'.repeat(50) + '\n');
2164
+ }
2165
+ } catch (error) {
2166
+ console.error('[Polydev] Could not verify auth (offline?)');
2167
+ }
2168
+ }
2169
+
2170
+ /**
2171
+ * Display CLI tools status after detection
2172
+ */
2173
+ async displayCliStatus() {
2174
+ try {
2175
+ const status = await this.loadLocalCliStatus();
2176
+ if (!status || Object.keys(status).length === 0) {
2177
+ console.error('[Polydev] No local CLI tools detected.\n');
2178
+ return;
2179
+ }
2180
+
2181
+ const tools = Object.entries(status);
2182
+ const available = tools.filter(([_, s]) => s.available);
2183
+ const authenticated = available.filter(([_, s]) => s.authenticated);
2184
+
2185
+ console.error('─'.repeat(50));
2186
+ console.error('Local AI Tools (FREE - no API credits)');
2187
+ console.error('─'.repeat(50));
2188
+
2189
+ for (const [id, s] of tools) {
2190
+ const name = id.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase());
2191
+ if (s.available) {
2192
+ const authStatus = s.authenticated ? '[ready]' : '[needs auth]';
2193
+ console.error(`[OK] ${name.padEnd(15)} ${authStatus}`);
2194
+ } else {
2195
+ console.error(`[--] ${name.padEnd(15)} not installed`);
2196
+ }
2197
+ }
2198
+
2199
+ if (available.length > 0) {
2200
+ console.error('');
2201
+ console.error(`${available.length} tool(s) installed, ${authenticated.length} ready to use`);
2202
+ console.error('Local tools save API credits!');
2203
+ }
2204
+
2205
+ console.error('─'.repeat(50) + '\n');
2206
+ } catch (error) {
2207
+ // Ignore errors in status display
2208
+ }
2209
+ }
2210
+
2211
+ /**
2212
+ * Check and display authentication status on startup
2213
+ * @deprecated Use verifyAndDisplayAuth instead
2214
+ */
2215
+ async checkAndDisplayAuthStatus() {
2216
+ await this.verifyAndDisplayAuth();
2217
+ }
1709
2218
  }
1710
2219
 
1711
2220
  if (require.main === module) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.8.74",
3
+ "version": "1.8.76",
4
4
  "engines": {
5
5
  "node": ">=20.x <=22.x"
6
6
  },