polydev-ai 1.2.14 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,27 +3,56 @@
3
3
  // Lightweight stdio wrapper with local CLI functionality and remote Polydev MCP server fallback
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const os = require('os');
6
7
  const { CLIManager } = require('../lib/cliManager');
7
8
 
9
+ function ensureWritableTmpDir() {
10
+ const candidates = [
11
+ process.env.POLYDEV_TMPDIR,
12
+ '/tmp/polydev',
13
+ '/tmp',
14
+ path.join(os.tmpdir(), 'polydev'),
15
+ path.join(process.cwd(), '.polydev-tmp')
16
+ ].filter(Boolean);
17
+
18
+ for (const candidate of candidates) {
19
+ try {
20
+ fs.mkdirSync(candidate, { recursive: true });
21
+ fs.accessSync(candidate, fs.constants.W_OK);
22
+ return candidate;
23
+ } catch (error) {
24
+ console.warn(`[Stdio Wrapper] TMP candidate not writable (${candidate}):`, error.message);
25
+ }
26
+ }
27
+
28
+ console.error('[Stdio Wrapper] No writable TMP directory found; Codex CLI will fail.');
29
+ return null;
30
+ }
31
+
32
+ const writableTmp = ensureWritableTmpDir();
33
+ if (writableTmp) {
34
+ process.env.TMPDIR = writableTmp;
35
+ process.env.TMP = writableTmp;
36
+ process.env.TEMP = writableTmp;
37
+ console.error(`[Stdio Wrapper] Using TMP directory: ${writableTmp}`);
38
+ }
39
+
8
40
  class StdioMCPWrapper {
9
41
  constructor() {
10
42
  this.userToken = process.env.POLYDEV_USER_TOKEN;
11
- // Don't exit immediately - wait for initialization to report error properly
12
-
13
- // Initialize CLI Manager for local CLI functionality (only if token exists)
14
- if (this.userToken) {
15
- this.cliManager = new CLIManager();
43
+ if (!this.userToken) {
44
+ console.error('POLYDEV_USER_TOKEN environment variable is required');
45
+ process.exit(1);
16
46
  }
17
47
 
48
+ // Initialize CLI Manager for local CLI functionality
49
+ this.cliManager = new CLIManager();
50
+
18
51
  // Load manifest for tool definitions
19
52
  this.loadManifest();
20
53
 
21
54
  // Smart refresh scheduler (will be started after initialization)
22
55
  this.refreshScheduler = null;
23
-
24
- // Track if initial CLI detection has been done
25
- this.initialDetectionDone = false;
26
- this.initialDetectionPromise = null;
27
56
  }
28
57
 
29
58
  loadManifest() {
@@ -69,20 +98,14 @@ class StdioMCPWrapper {
69
98
  };
70
99
 
71
100
  case 'tools/call':
72
- // Check if token is missing
73
- if (!this.userToken) {
74
- return {
75
- jsonrpc: '2.0',
76
- id,
77
- error: {
78
- code: -32603,
79
- message: 'POLYDEV_USER_TOKEN environment variable is required. Please add it to your MCP configuration.'
80
- }
81
- };
101
+ // Handle get_perspectives with local CLIs + remote perspectives
102
+ const toolName = params.name;
103
+
104
+ if (toolName === 'get_perspectives') {
105
+ return await this.handleGetPerspectivesWithCLIs(params, id);
82
106
  }
83
107
 
84
- // Handle CLI tools locally, forward others to remote server
85
- const toolName = params.name;
108
+ // Handle other CLI tools locally
86
109
  if (toolName.startsWith('polydev.') && this.isCliTool(toolName)) {
87
110
  return await this.handleLocalCliTool(request);
88
111
  } else {
@@ -157,6 +180,45 @@ class StdioMCPWrapper {
157
180
  return cliTools.includes(toolName);
158
181
  }
159
182
 
183
+ /**
184
+ * Handle get_perspectives with local CLIs + remote perspectives
185
+ */
186
+ async handleGetPerspectivesWithCLIs(params, id) {
187
+ console.error(`[Stdio Wrapper] Handling get_perspectives with local CLIs + remote`);
188
+
189
+ try {
190
+ // Use existing localSendCliPrompt logic which already:
191
+ // 1. Checks all local CLIs
192
+ // 2. Calls remote perspectives
193
+ // 3. Combines results
194
+ const result = await this.localSendCliPrompt(params.arguments);
195
+
196
+ return {
197
+ jsonrpc: '2.0',
198
+ id,
199
+ result: {
200
+ content: [
201
+ {
202
+ type: 'text',
203
+ text: result.content || this.formatCliResponse(result)
204
+ }
205
+ ]
206
+ }
207
+ };
208
+
209
+ } catch (error) {
210
+ console.error(`[Stdio Wrapper] get_perspectives error:`, error);
211
+ return {
212
+ jsonrpc: '2.0',
213
+ id,
214
+ error: {
215
+ code: -32603,
216
+ message: error.message
217
+ }
218
+ };
219
+ }
220
+ }
221
+
160
222
  /**
161
223
  * Handle CLI tools locally without remote server calls
162
224
  */
@@ -230,10 +292,6 @@ class StdioMCPWrapper {
230
292
  // Update database with CLI status
231
293
  await this.updateCliStatusInDatabase(results);
232
294
 
233
- // Mark initial detection as done
234
- this.initialDetectionDone = true;
235
- this.initialDetectionPromise = null;
236
-
237
295
  return {
238
296
  success: true,
239
297
  results,
@@ -244,10 +302,6 @@ class StdioMCPWrapper {
244
302
 
245
303
  } catch (error) {
246
304
  console.error('[Stdio Wrapper] Local CLI detection error:', error);
247
- // Mark as done even on error to prevent blocking
248
- this.initialDetectionDone = true;
249
- this.initialDetectionPromise = null;
250
-
251
305
  return {
252
306
  success: false,
253
307
  error: error.message,
@@ -309,16 +363,6 @@ class StdioMCPWrapper {
309
363
  console.error(`[Stdio Wrapper] Local CLI prompt sending with perspectives`);
310
364
 
311
365
  try {
312
- // Ensure initial CLI detection has completed before sending prompts
313
- if (!this.initialDetectionDone && this.initialDetectionPromise) {
314
- console.error('[Stdio Wrapper] Waiting for initial CLI detection to complete...');
315
- await this.initialDetectionPromise;
316
- } else if (!this.initialDetectionDone && !this.initialDetectionPromise) {
317
- console.error('[Stdio Wrapper] Running initial CLI detection before sending prompt...');
318
- this.initialDetectionPromise = this.localForceCliDetection({});
319
- await this.initialDetectionPromise;
320
- }
321
-
322
366
  let { provider_id, prompt, mode = 'args', timeout_ms = 30000 } = args;
323
367
 
324
368
  // Ensure timeout_ms is valid (not undefined, null, Infinity, or negative)
@@ -402,18 +446,7 @@ class StdioMCPWrapper {
402
446
  */
403
447
  async getAllAvailableProviders() {
404
448
  try {
405
- // Try to load from cache first to avoid re-detection
406
- const cachedStatus = await this.loadLocalCliStatus();
407
- let results;
408
-
409
- if (cachedStatus && Object.keys(cachedStatus).length > 0) {
410
- console.error('[Stdio Wrapper] Using cached CLI status');
411
- results = cachedStatus;
412
- } else {
413
- console.error('[Stdio Wrapper] No cached status, running detection');
414
- results = await this.cliManager.forceCliDetection();
415
- }
416
-
449
+ const results = await this.cliManager.forceCliDetection();
417
450
  const availableProviders = [];
418
451
 
419
452
  // Priority order: claude_code > codex_cli > gemini_cli
@@ -894,7 +927,19 @@ class StdioMCPWrapper {
894
927
  }
895
928
 
896
929
  async start() {
897
- console.error('[Stdio Wrapper] Starting Polydev Stdio MCP Wrapper...');
930
+ console.log('Starting Polydev Stdio MCP Wrapper...');
931
+
932
+ // Run initial CLI detection on startup
933
+ console.error('[Stdio Wrapper] Running initial CLI detection...');
934
+ try {
935
+ await this.localForceCliDetection({});
936
+ console.error('[Stdio Wrapper] Initial CLI detection completed');
937
+ } catch (error) {
938
+ console.error('[Stdio Wrapper] Initial CLI detection failed:', error);
939
+ }
940
+
941
+ // Start smart refresh scheduler for automatic updates
942
+ this.startSmartRefreshScheduler();
898
943
 
899
944
  process.stdin.setEncoding('utf8');
900
945
  let buffer = '';
@@ -919,46 +964,25 @@ class StdioMCPWrapper {
919
964
  });
920
965
 
921
966
  process.stdin.on('end', () => {
922
- console.error('[Stdio Wrapper] Shutting down...');
967
+ console.log('Stdio MCP Wrapper shutting down...');
923
968
  this.stopSmartRefreshScheduler();
924
969
  process.exit(0);
925
970
  });
926
971
 
927
972
  // Handle process signals
928
973
  process.on('SIGINT', () => {
929
- console.error('[Stdio Wrapper] Received SIGINT, shutting down...');
974
+ console.log('Received SIGINT, shutting down...');
930
975
  this.stopSmartRefreshScheduler();
931
976
  process.exit(0);
932
977
  });
933
978
 
934
979
  process.on('SIGTERM', () => {
935
- console.error('[Stdio Wrapper] Received SIGTERM, shutting down...');
980
+ console.log('Received SIGTERM, shutting down...');
936
981
  this.stopSmartRefreshScheduler();
937
982
  process.exit(0);
938
983
  });
939
984
 
940
- console.error('[Stdio Wrapper] Ready and listening on stdin...');
941
-
942
- // Run initial CLI detection AFTER stdin handler is set up
943
- if (this.userToken && this.cliManager) {
944
- console.error('[Stdio Wrapper] Scheduling initial CLI detection...');
945
- // Run asynchronously after a short delay to not block initialization
946
- setTimeout(async () => {
947
- console.error('[Stdio Wrapper] Running initial CLI detection...');
948
- try {
949
- this.initialDetectionPromise = this.localForceCliDetection({});
950
- await this.initialDetectionPromise;
951
- console.error('[Stdio Wrapper] Initial CLI detection completed');
952
- } catch (error) {
953
- console.error('[Stdio Wrapper] Initial CLI detection failed:', error);
954
- }
955
-
956
- // Start smart refresh scheduler for automatic updates
957
- this.startSmartRefreshScheduler();
958
- }, 100);
959
- } else {
960
- console.error('[Stdio Wrapper] No token provided - CLI detection disabled');
961
- }
985
+ console.log('Stdio MCP Wrapper ready and listening on stdin...');
962
986
  }
963
987
  }
964
988
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.2.14",
3
+ "version": "1.4.0",
4
4
  "description": "Agentic workflow assistant with CLI integration - get diverse perspectives from multiple LLMs when stuck or need enhanced reasoning",
5
5
  "keywords": [
6
6
  "mcp",
@@ -31,6 +31,12 @@
31
31
  "build": "next build",
32
32
  "start": "next start",
33
33
  "lint": "next lint",
34
+ "test": "jest",
35
+ "test:watch": "jest --watch",
36
+ "test:coverage": "jest --coverage",
37
+ "test:api": "jest __tests__/api",
38
+ "test:vm": "jest __tests__/api/vm",
39
+ "test:admin": "jest __tests__/api/admin",
34
40
  "mcp": "node mcp/server.js",
35
41
  "mcp-stdio": "node mcp/stdio-wrapper.js",
36
42
  "cli-detect": "node -e \"const CLIManager = require('./lib/cliManager').default; const m = new CLIManager(); m.forceCliDetection().then(console.log);\""
@@ -44,8 +50,6 @@
44
50
  "url": "https://github.com/polydev-ai/perspectives-mcp/issues"
45
51
  },
46
52
  "dependencies": {
47
- "which": "^5.0.0",
48
- "shelljs": "^0.8.5",
49
53
  "@hello-pangea/dnd": "^18.0.1",
50
54
  "@radix-ui/react-dialog": "^1.1.15",
51
55
  "@radix-ui/react-progress": "^1.1.7",
@@ -69,21 +73,30 @@
69
73
  "react": "^18.3.1",
70
74
  "react-dom": "^18.3.1",
71
75
  "resend": "^6.0.2",
76
+ "shelljs": "^0.8.5",
72
77
  "sonner": "^2.0.7",
73
78
  "stripe": "^18.5.0",
74
79
  "tailwind-merge": "^3.3.1",
80
+ "tailwindcss-animate": "^1.0.7",
75
81
  "ts-node": "^10.9.2",
76
- "use-debounce": "^10.0.6"
82
+ "use-debounce": "^10.0.6",
83
+ "which": "^5.0.0"
77
84
  },
78
85
  "devDependencies": {
86
+ "@testing-library/jest-dom": "^6.9.1",
87
+ "@testing-library/react": "^16.3.0",
88
+ "@types/jest": "^30.0.0",
79
89
  "@types/node": "^20.16.1",
80
90
  "@types/react": "^18.3.3",
81
91
  "@types/react-dom": "^18.3.0",
82
92
  "autoprefixer": "^10.4.20",
83
93
  "eslint": "^8.57.0",
84
94
  "eslint-config-next": "15.0.0",
95
+ "jest": "^30.2.0",
96
+ "jest-environment-jsdom": "^30.2.0",
85
97
  "postcss": "^8.4.41",
86
98
  "tailwindcss": "^3.4.10",
99
+ "ts-jest": "^29.4.4",
87
100
  "typescript": "^5.5.4"
88
101
  }
89
102
  }