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.
- package/lib/cliManager.js +682 -475
- package/lib/cliManager.ts +236 -51
- package/lib/zeroKnowledgeEncryption.js +4 -4
- package/mcp/manifest.json +3 -3
- package/mcp/package.json +2 -2
- package/mcp/stdio-wrapper.js +101 -77
- package/package.json +17 -4
package/mcp/stdio-wrapper.js
CHANGED
|
@@ -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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
980
|
+
console.log('Received SIGTERM, shutting down...');
|
|
936
981
|
this.stopSmartRefreshScheduler();
|
|
937
982
|
process.exit(0);
|
|
938
983
|
});
|
|
939
984
|
|
|
940
|
-
console.
|
|
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.
|
|
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
|
}
|