polydev-ai 1.2.3 → 1.2.5
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/README.md +1 -1
- package/lib/cliManager.ts +144 -2
- package/lib/smartCliCache.ts +189 -0
- package/lib/universalMemoryExtractor.js +607 -0
- package/lib/zeroKnowledgeEncryption.js +289 -0
- package/mcp/README.md +165 -0
- package/mcp/manifest.json +8 -8
- package/mcp/package.json +2 -2
- package/mcp/server.js +43 -20
- package/mcp/stdio-wrapper.js +203 -2
- package/package.json +5 -1
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Zero-Knowledge Client-Side Encryption System
|
|
4
|
+
* All encryption keys never leave the user's browser
|
|
5
|
+
* Enterprise-grade AES-256-GCM encryption with automatic key rotation
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.zkEncryption = exports.ZeroKnowledgeEncryption = void 0;
|
|
9
|
+
class ZeroKnowledgeEncryption {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.db = null;
|
|
12
|
+
this.activeKey = null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Initialize the encryption system
|
|
16
|
+
*/
|
|
17
|
+
async initialize() {
|
|
18
|
+
await this.initializeIndexedDB();
|
|
19
|
+
await this.loadOrCreateActiveKey();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Initialize IndexedDB for local key storage
|
|
23
|
+
*/
|
|
24
|
+
async initializeIndexedDB() {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const request = indexedDB.open(ZeroKnowledgeEncryption.DB_NAME, ZeroKnowledgeEncryption.DB_VERSION);
|
|
27
|
+
request.onerror = () => reject(request.error);
|
|
28
|
+
request.onsuccess = () => {
|
|
29
|
+
this.db = request.result;
|
|
30
|
+
resolve();
|
|
31
|
+
};
|
|
32
|
+
request.onupgradeneeded = (event) => {
|
|
33
|
+
const db = event.target.result;
|
|
34
|
+
// Store for encryption keys (never synchronized)
|
|
35
|
+
if (!db.objectStoreNames.contains('keys')) {
|
|
36
|
+
const keyStore = db.createObjectStore('keys', { keyPath: 'keyId' });
|
|
37
|
+
keyStore.createIndex('created', 'created', { unique: false });
|
|
38
|
+
}
|
|
39
|
+
// Cache for encrypted data
|
|
40
|
+
if (!db.objectStoreNames.contains('cache')) {
|
|
41
|
+
const cacheStore = db.createObjectStore('cache', { keyPath: 'id' });
|
|
42
|
+
cacheStore.createIndex('expires', 'expires', { unique: false });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Generate new AES-256-GCM encryption key
|
|
49
|
+
*/
|
|
50
|
+
async generateKey() {
|
|
51
|
+
const keyId = this.generateKeyId();
|
|
52
|
+
const key = await window.crypto.subtle.generateKey({
|
|
53
|
+
name: ZeroKnowledgeEncryption.ALGORITHM,
|
|
54
|
+
length: ZeroKnowledgeEncryption.KEY_LENGTH,
|
|
55
|
+
}, true, // extractable for storage
|
|
56
|
+
['encrypt', 'decrypt']);
|
|
57
|
+
const now = new Date();
|
|
58
|
+
const rotationDue = new Date(now.getTime() + (ZeroKnowledgeEncryption.KEY_ROTATION_DAYS * 24 * 60 * 60 * 1000));
|
|
59
|
+
return {
|
|
60
|
+
keyId,
|
|
61
|
+
key,
|
|
62
|
+
created: now,
|
|
63
|
+
rotationDue
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Load active key or create new one
|
|
68
|
+
*/
|
|
69
|
+
async loadOrCreateActiveKey() {
|
|
70
|
+
if (!this.db)
|
|
71
|
+
throw new Error('Database not initialized');
|
|
72
|
+
const transaction = this.db.transaction(['keys'], 'readonly');
|
|
73
|
+
const store = transaction.objectStore('keys');
|
|
74
|
+
// Get the most recent key
|
|
75
|
+
const index = store.index('created');
|
|
76
|
+
const request = index.openCursor(null, 'prev');
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
request.onsuccess = async (event) => {
|
|
79
|
+
const cursor = event.target.result;
|
|
80
|
+
if (cursor) {
|
|
81
|
+
const keyData = cursor.value;
|
|
82
|
+
// Check if key needs rotation
|
|
83
|
+
if (new Date() > new Date(keyData.rotationDue)) {
|
|
84
|
+
console.log('[ZK Encryption] Key rotation needed, generating new key');
|
|
85
|
+
await this.createAndStoreKey();
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Import existing key
|
|
89
|
+
const keyBuffer = new Uint8Array(keyData.keyBuffer);
|
|
90
|
+
this.activeKey = {
|
|
91
|
+
keyId: keyData.keyId,
|
|
92
|
+
key: await window.crypto.subtle.importKey('raw', keyBuffer, { name: ZeroKnowledgeEncryption.ALGORITHM }, true, ['encrypt', 'decrypt']),
|
|
93
|
+
created: new Date(keyData.created),
|
|
94
|
+
rotationDue: new Date(keyData.rotationDue)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// No keys exist, create first one
|
|
100
|
+
await this.createAndStoreKey();
|
|
101
|
+
}
|
|
102
|
+
resolve();
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create and store new key
|
|
108
|
+
*/
|
|
109
|
+
async createAndStoreKey() {
|
|
110
|
+
this.activeKey = await this.generateKey();
|
|
111
|
+
await this.storeKey(this.activeKey);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Store key in IndexedDB
|
|
115
|
+
*/
|
|
116
|
+
async storeKey(userKey) {
|
|
117
|
+
if (!this.db)
|
|
118
|
+
throw new Error('Database not initialized');
|
|
119
|
+
const keyBuffer = await window.crypto.subtle.exportKey('raw', userKey.key);
|
|
120
|
+
const transaction = this.db.transaction(['keys'], 'readwrite');
|
|
121
|
+
const store = transaction.objectStore('keys');
|
|
122
|
+
await store.put({
|
|
123
|
+
keyId: userKey.keyId,
|
|
124
|
+
keyBuffer: Array.from(new Uint8Array(keyBuffer)),
|
|
125
|
+
created: userKey.created.toISOString(),
|
|
126
|
+
rotationDue: userKey.rotationDue.toISOString()
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Encrypt sensitive data
|
|
131
|
+
*/
|
|
132
|
+
async encrypt(plaintext) {
|
|
133
|
+
if (!this.activeKey)
|
|
134
|
+
throw new Error('Encryption key not available');
|
|
135
|
+
const encoder = new TextEncoder();
|
|
136
|
+
const data = encoder.encode(plaintext);
|
|
137
|
+
const iv = window.crypto.getRandomValues(new Uint8Array(ZeroKnowledgeEncryption.IV_LENGTH));
|
|
138
|
+
const encrypted = await window.crypto.subtle.encrypt({
|
|
139
|
+
name: ZeroKnowledgeEncryption.ALGORITHM,
|
|
140
|
+
iv: iv,
|
|
141
|
+
}, this.activeKey.key, data);
|
|
142
|
+
const encryptedArray = new Uint8Array(encrypted);
|
|
143
|
+
const ciphertext = encryptedArray.slice(0, -ZeroKnowledgeEncryption.AUTH_TAG_LENGTH);
|
|
144
|
+
const authTag = encryptedArray.slice(-ZeroKnowledgeEncryption.AUTH_TAG_LENGTH);
|
|
145
|
+
return {
|
|
146
|
+
ciphertext: this.arrayBufferToBase64(ciphertext.buffer),
|
|
147
|
+
iv: this.arrayBufferToBase64(iv.buffer),
|
|
148
|
+
authTag: this.arrayBufferToBase64(authTag.buffer),
|
|
149
|
+
keyId: this.activeKey.keyId,
|
|
150
|
+
version: 1
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Decrypt sensitive data
|
|
155
|
+
*/
|
|
156
|
+
async decrypt(encryptedData) {
|
|
157
|
+
const key = await this.getKeyById(encryptedData.keyId);
|
|
158
|
+
if (!key)
|
|
159
|
+
throw new Error(`Encryption key ${encryptedData.keyId} not found`);
|
|
160
|
+
const ciphertext = this.base64ToArrayBuffer(encryptedData.ciphertext);
|
|
161
|
+
const iv = this.base64ToArrayBuffer(encryptedData.iv);
|
|
162
|
+
const authTag = this.base64ToArrayBuffer(encryptedData.authTag);
|
|
163
|
+
// Combine ciphertext and auth tag for Web Crypto API
|
|
164
|
+
const combined = new Uint8Array(ciphertext.byteLength + authTag.byteLength);
|
|
165
|
+
combined.set(new Uint8Array(ciphertext));
|
|
166
|
+
combined.set(new Uint8Array(authTag), ciphertext.byteLength);
|
|
167
|
+
const decrypted = await window.crypto.subtle.decrypt({
|
|
168
|
+
name: ZeroKnowledgeEncryption.ALGORITHM,
|
|
169
|
+
iv: new Uint8Array(iv),
|
|
170
|
+
}, key, combined);
|
|
171
|
+
const decoder = new TextDecoder();
|
|
172
|
+
return decoder.decode(decrypted);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get encryption key by ID
|
|
176
|
+
*/
|
|
177
|
+
async getKeyById(keyId) {
|
|
178
|
+
if (!this.db)
|
|
179
|
+
return null;
|
|
180
|
+
const transaction = this.db.transaction(['keys'], 'readonly');
|
|
181
|
+
const store = transaction.objectStore('keys');
|
|
182
|
+
const request = store.get(keyId);
|
|
183
|
+
return new Promise((resolve) => {
|
|
184
|
+
request.onsuccess = async () => {
|
|
185
|
+
if (request.result) {
|
|
186
|
+
const keyBuffer = new Uint8Array(request.result.keyBuffer);
|
|
187
|
+
const key = await window.crypto.subtle.importKey('raw', keyBuffer, { name: ZeroKnowledgeEncryption.ALGORITHM }, true, ['encrypt', 'decrypt']);
|
|
188
|
+
resolve(key);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
resolve(null);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
request.onerror = () => resolve(null);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Create SHA-256 hash for content deduplication
|
|
199
|
+
*/
|
|
200
|
+
async createContentHash(content) {
|
|
201
|
+
const encoder = new TextEncoder();
|
|
202
|
+
const data = encoder.encode(content);
|
|
203
|
+
const hashBuffer = await window.crypto.subtle.digest('SHA-256', data);
|
|
204
|
+
return this.arrayBufferToHex(hashBuffer);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Check if key rotation is needed
|
|
208
|
+
*/
|
|
209
|
+
isRotationDue() {
|
|
210
|
+
return this.activeKey ? new Date() > this.activeKey.rotationDue : true;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Force key rotation
|
|
214
|
+
*/
|
|
215
|
+
async rotateKeys() {
|
|
216
|
+
await this.createAndStoreKey();
|
|
217
|
+
console.log('[ZK Encryption] Key rotation completed');
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Generate unique key ID
|
|
221
|
+
*/
|
|
222
|
+
generateKeyId() {
|
|
223
|
+
const timestamp = Date.now().toString(36);
|
|
224
|
+
const random = Math.random().toString(36).substr(2, 9);
|
|
225
|
+
return `zk_${timestamp}_${random}`;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Utility: ArrayBuffer to Base64
|
|
229
|
+
*/
|
|
230
|
+
arrayBufferToBase64(buffer) {
|
|
231
|
+
const bytes = new Uint8Array(buffer);
|
|
232
|
+
let binary = '';
|
|
233
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
234
|
+
binary += String.fromCharCode(bytes[i]);
|
|
235
|
+
}
|
|
236
|
+
return btoa(binary);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Utility: Base64 to ArrayBuffer
|
|
240
|
+
*/
|
|
241
|
+
base64ToArrayBuffer(base64) {
|
|
242
|
+
const binary = atob(base64);
|
|
243
|
+
const bytes = new Uint8Array(binary.length);
|
|
244
|
+
for (let i = 0; i < binary.length; i++) {
|
|
245
|
+
bytes[i] = binary.charCodeAt(i);
|
|
246
|
+
}
|
|
247
|
+
return bytes.buffer;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Utility: ArrayBuffer to Hex
|
|
251
|
+
*/
|
|
252
|
+
arrayBufferToHex(buffer) {
|
|
253
|
+
const bytes = new Uint8Array(buffer);
|
|
254
|
+
return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Get encryption status for dashboard
|
|
258
|
+
*/
|
|
259
|
+
getEncryptionStatus() {
|
|
260
|
+
return {
|
|
261
|
+
keyId: this.activeKey?.keyId || null,
|
|
262
|
+
created: this.activeKey?.created || null,
|
|
263
|
+
rotationDue: this.activeKey?.rotationDue || null,
|
|
264
|
+
rotationNeeded: this.isRotationDue()
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Clear all keys (for user logout/reset)
|
|
269
|
+
*/
|
|
270
|
+
async clearAllKeys() {
|
|
271
|
+
if (!this.db)
|
|
272
|
+
return;
|
|
273
|
+
const transaction = this.db.transaction(['keys', 'cache'], 'readwrite');
|
|
274
|
+
await transaction.objectStore('keys').clear();
|
|
275
|
+
await transaction.objectStore('cache').clear();
|
|
276
|
+
this.activeKey = null;
|
|
277
|
+
console.log('[ZK Encryption] All keys cleared');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
exports.ZeroKnowledgeEncryption = ZeroKnowledgeEncryption;
|
|
281
|
+
ZeroKnowledgeEncryption.ALGORITHM = 'AES-GCM';
|
|
282
|
+
ZeroKnowledgeEncryption.KEY_LENGTH = 256;
|
|
283
|
+
ZeroKnowledgeEncryption.IV_LENGTH = 12;
|
|
284
|
+
ZeroKnowledgeEncryption.AUTH_TAG_LENGTH = 16;
|
|
285
|
+
ZeroKnowledgeEncryption.KEY_ROTATION_DAYS = 30;
|
|
286
|
+
ZeroKnowledgeEncryption.DB_NAME = 'PolydevZKMemory';
|
|
287
|
+
ZeroKnowledgeEncryption.DB_VERSION = 1;
|
|
288
|
+
// Export singleton instance
|
|
289
|
+
exports.zkEncryption = new ZeroKnowledgeEncryption();
|
package/mcp/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Polydev AI MCP Server
|
|
2
|
+
|
|
3
|
+
Get diverse AI perspectives from multiple LLMs via Model Context Protocol (MCP). Supports Cline, Claude Code, and other MCP clients with local CLI detection and remote AI perspectives.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🤖 **Multi-Model AI Perspectives**: Get responses from GPT-4, Claude, Gemini, and more
|
|
8
|
+
- 🔧 **Local CLI Detection**: Automatically detect and use Claude Code, Codex CLI, Gemini CLI
|
|
9
|
+
- ⚡ **Smart Caching**: Intelligent refresh intervals based on CLI status
|
|
10
|
+
- 🔄 **Fallback Support**: Local CLI + remote perspectives for comprehensive responses
|
|
11
|
+
- 🔐 **Secure**: Token-based authentication with your Polydev account
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### For Claude Code Users
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Install globally
|
|
19
|
+
npm install -g polydev-ai
|
|
20
|
+
|
|
21
|
+
# Or use directly with npx
|
|
22
|
+
npx polydev-ai
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### For Cline Users
|
|
26
|
+
|
|
27
|
+
Add to your MCP settings (`.cline/mcp_servers.json`):
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"polydev": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["polydev-ai"],
|
|
35
|
+
"env": {
|
|
36
|
+
"POLYDEV_USER_TOKEN": "your_token_here"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
### Get Your Token
|
|
46
|
+
|
|
47
|
+
1. Sign up at [polydev.ai](https://polydev.ai)
|
|
48
|
+
2. Go to your dashboard and copy your MCP token
|
|
49
|
+
3. Set the environment variable:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
export POLYDEV_USER_TOKEN="pd_your_token_here"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Claude Code Integration
|
|
56
|
+
|
|
57
|
+
Add to your Claude Code MCP configuration:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"polydev": {
|
|
63
|
+
"command": "npx",
|
|
64
|
+
"args": ["polydev-ai"],
|
|
65
|
+
"env": {
|
|
66
|
+
"POLYDEV_USER_TOKEN": "pd_your_token_here"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Available Tools
|
|
74
|
+
|
|
75
|
+
- `get_perspectives` - Get AI responses from multiple models
|
|
76
|
+
- `force_cli_detection` - Force detection of local CLI tools
|
|
77
|
+
- `get_cli_status` - Check status of CLI tools (Claude Code, Codex, Gemini)
|
|
78
|
+
- `send_cli_prompt` - Send prompts to local CLI with perspectives fallback
|
|
79
|
+
|
|
80
|
+
## Usage Examples
|
|
81
|
+
|
|
82
|
+
### Get Multi-Model Perspectives
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Use the get_perspectives tool
|
|
86
|
+
{
|
|
87
|
+
"prompt": "How do I optimize React performance?",
|
|
88
|
+
"models": ["gpt-4", "claude-3-sonnet", "gemini-pro"]
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Check CLI Status
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Check all CLI tools
|
|
96
|
+
await get_cli_status({})
|
|
97
|
+
|
|
98
|
+
// Check specific tool
|
|
99
|
+
await get_cli_status({ provider_id: "claude_code" })
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Send CLI Prompt with Fallback
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
await send_cli_prompt({
|
|
106
|
+
provider_id: "claude_code",
|
|
107
|
+
prompt: "Write a Python function to parse JSON",
|
|
108
|
+
mode: "args"
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Supported CLI Tools
|
|
113
|
+
|
|
114
|
+
- **Claude Code** (`claude`) - Anthropic's official CLI
|
|
115
|
+
- **Codex CLI** (`codex`) - OpenAI's code CLI
|
|
116
|
+
- **Gemini CLI** (`gemini`) - Google's Gemini CLI
|
|
117
|
+
|
|
118
|
+
## Smart Refresh System
|
|
119
|
+
|
|
120
|
+
The MCP server automatically detects CLI status changes:
|
|
121
|
+
|
|
122
|
+
- **Unavailable CLIs**: Check every 2 minutes
|
|
123
|
+
- **Unauthenticated CLIs**: Check every 3 minutes
|
|
124
|
+
- **Working CLIs**: Check every 10 minutes
|
|
125
|
+
- **Fallback detection**: Check every 5 minutes
|
|
126
|
+
|
|
127
|
+
## Environment Variables
|
|
128
|
+
|
|
129
|
+
- `POLYDEV_USER_TOKEN` - Your Polydev authentication token (required)
|
|
130
|
+
- `POLYDEV_CLI_DEBUG` - Enable CLI debugging output
|
|
131
|
+
- `CLAUDE_CODE_PATH` - Custom path to Claude Code CLI
|
|
132
|
+
- `CODEX_CLI_PATH` - Custom path to Codex CLI
|
|
133
|
+
- `GEMINI_CLI_PATH` - Custom path to Gemini CLI
|
|
134
|
+
|
|
135
|
+
## Troubleshooting
|
|
136
|
+
|
|
137
|
+
### CLI Not Detected
|
|
138
|
+
|
|
139
|
+
1. Ensure the CLI is installed and in your PATH
|
|
140
|
+
2. Check authentication: `claude auth status` / `codex login status`
|
|
141
|
+
3. Enable debugging: `export POLYDEV_CLI_DEBUG=1`
|
|
142
|
+
|
|
143
|
+
### Token Issues
|
|
144
|
+
|
|
145
|
+
1. Verify your token at [polydev.ai/dashboard](https://polydev.ai/dashboard)
|
|
146
|
+
2. Ensure the token starts with `pd_`
|
|
147
|
+
3. Check environment variable is set correctly
|
|
148
|
+
|
|
149
|
+
### Permission Errors
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Fix npm permissions
|
|
153
|
+
npm config set prefix '~/.npm-global'
|
|
154
|
+
export PATH=~/.npm-global/bin:$PATH
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Support
|
|
158
|
+
|
|
159
|
+
- 📧 Email: [support@polydev.ai](mailto:support@polydev.ai)
|
|
160
|
+
- 📖 Docs: [polydev.ai/docs](https://polydev.ai/docs)
|
|
161
|
+
- 🐛 Issues: [GitHub Issues](https://github.com/backspacevenkat/polydev-website/issues)
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT License - see LICENSE file for details.
|
package/mcp/manifest.json
CHANGED
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
]
|
|
199
199
|
},
|
|
200
200
|
{
|
|
201
|
-
"name": "
|
|
201
|
+
"name": "force_cli_detection",
|
|
202
202
|
"description": "Force detection and status update for CLI tools (Claude Code, Codex CLI, Gemini CLI). Updates local cache and reports status to MCP server via Supabase.",
|
|
203
203
|
"inputSchema": {
|
|
204
204
|
"type": "object",
|
|
@@ -290,7 +290,7 @@
|
|
|
290
290
|
]
|
|
291
291
|
},
|
|
292
292
|
{
|
|
293
|
-
"name": "
|
|
293
|
+
"name": "get_cli_status",
|
|
294
294
|
"description": "Get current CLI status with caching support. Returns cached results if available and fresh, otherwise performs new detection.",
|
|
295
295
|
"inputSchema": {
|
|
296
296
|
"type": "object",
|
|
@@ -347,7 +347,7 @@
|
|
|
347
347
|
}
|
|
348
348
|
},
|
|
349
349
|
{
|
|
350
|
-
"name": "
|
|
350
|
+
"name": "send_cli_prompt",
|
|
351
351
|
"description": "Send a prompt to a CLI provider and get response. Supports both stdin and argument modes for maximum compatibility.",
|
|
352
352
|
"inputSchema": {
|
|
353
353
|
"type": "object",
|
|
@@ -443,7 +443,7 @@
|
|
|
443
443
|
]
|
|
444
444
|
},
|
|
445
445
|
{
|
|
446
|
-
"name": "
|
|
446
|
+
"name": "detect_memory_sources",
|
|
447
447
|
"description": "Detect all available memory sources across CLI tools (Claude Code, Cline, Codex, Cursor, Continue, Aider). Returns file paths for global memory, project memory, and recent conversations.",
|
|
448
448
|
"inputSchema": {
|
|
449
449
|
"type": "object",
|
|
@@ -503,7 +503,7 @@
|
|
|
503
503
|
}
|
|
504
504
|
},
|
|
505
505
|
{
|
|
506
|
-
"name": "
|
|
506
|
+
"name": "extract_memory",
|
|
507
507
|
"description": "Extract and optionally encrypt memory content from detected CLI tool sources. Provides TF-IDF relevance scoring and content analysis.",
|
|
508
508
|
"inputSchema": {
|
|
509
509
|
"type": "object",
|
|
@@ -550,7 +550,7 @@
|
|
|
550
550
|
}
|
|
551
551
|
},
|
|
552
552
|
{
|
|
553
|
-
"name": "
|
|
553
|
+
"name": "get_recent_conversations",
|
|
554
554
|
"description": "Get recent conversations from CLI tools with TF-IDF relevance scoring against query context. Supports encrypted storage and retrieval.",
|
|
555
555
|
"inputSchema": {
|
|
556
556
|
"type": "object",
|
|
@@ -590,7 +590,7 @@
|
|
|
590
590
|
}
|
|
591
591
|
},
|
|
592
592
|
{
|
|
593
|
-
"name": "
|
|
593
|
+
"name": "get_memory_context",
|
|
594
594
|
"description": "Get formatted memory and conversation context for injection into prompts. Automatically handles decryption and relevance scoring.",
|
|
595
595
|
"inputSchema": {
|
|
596
596
|
"type": "object",
|
|
@@ -631,7 +631,7 @@
|
|
|
631
631
|
}
|
|
632
632
|
},
|
|
633
633
|
{
|
|
634
|
-
"name": "
|
|
634
|
+
"name": "manage_memory_preferences",
|
|
635
635
|
"description": "Configure memory extraction and privacy preferences. Control which CLI tools and memory types are enabled, encryption settings, and context injection behavior.",
|
|
636
636
|
"inputSchema": {
|
|
637
637
|
"type": "object",
|
package/mcp/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "polydev-
|
|
2
|
+
"name": "polydev-ai",
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"description": "Get diverse AI perspectives from multiple LLMs via MCP - supports Cline, Claude Code, and other MCP clients",
|
|
5
5
|
"main": "stdio-wrapper.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"polydev-
|
|
7
|
+
"polydev-ai": "./stdio-wrapper.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node server.js",
|
package/mcp/server.js
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Register ts-node for TypeScript support
|
|
4
|
+
try {
|
|
5
|
+
require('ts-node/register');
|
|
6
|
+
} catch (e) {
|
|
7
|
+
// ts-node not available, proceed without it
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
const fs = require('fs');
|
|
4
11
|
const path = require('path');
|
|
5
12
|
const CLIManager = require('../lib/cliManager').default;
|
|
6
|
-
|
|
13
|
+
|
|
14
|
+
let UniversalMemoryExtractor;
|
|
15
|
+
try {
|
|
16
|
+
UniversalMemoryExtractor = require('../src/lib/universalMemoryExtractor').UniversalMemoryExtractor;
|
|
17
|
+
} catch (e) {
|
|
18
|
+
// Fallback if TypeScript module is not available
|
|
19
|
+
console.warn('UniversalMemoryExtractor not available, memory features will be disabled');
|
|
20
|
+
UniversalMemoryExtractor = class {
|
|
21
|
+
async detectMemorySources() { return []; }
|
|
22
|
+
async extractMemory() { return {}; }
|
|
23
|
+
async getRecentConversations() { return []; }
|
|
24
|
+
async getRelevantContext() { return ''; }
|
|
25
|
+
async getPreferences() { return {}; }
|
|
26
|
+
async updatePreferences() { return {}; }
|
|
27
|
+
async resetPreferences() { return {}; }
|
|
28
|
+
};
|
|
29
|
+
}
|
|
7
30
|
|
|
8
31
|
class MCPServer {
|
|
9
32
|
constructor() {
|
|
@@ -126,35 +149,35 @@ class MCPServer {
|
|
|
126
149
|
result = await this.callPerspectivesAPI(args);
|
|
127
150
|
break;
|
|
128
151
|
|
|
129
|
-
case '
|
|
152
|
+
case 'force_cli_detection':
|
|
130
153
|
result = await this.forceCliDetection(args);
|
|
131
154
|
break;
|
|
132
155
|
|
|
133
|
-
case '
|
|
156
|
+
case 'get_cli_status':
|
|
134
157
|
result = await this.getCliStatus(args);
|
|
135
158
|
break;
|
|
136
159
|
|
|
137
|
-
case '
|
|
160
|
+
case 'send_cli_prompt':
|
|
138
161
|
result = await this.sendCliPrompt(args);
|
|
139
162
|
break;
|
|
140
163
|
|
|
141
|
-
case '
|
|
164
|
+
case 'detect_memory_sources':
|
|
142
165
|
result = await this.detectMemorySources(args);
|
|
143
166
|
break;
|
|
144
167
|
|
|
145
|
-
case '
|
|
168
|
+
case 'extract_memory':
|
|
146
169
|
result = await this.extractMemory(args);
|
|
147
170
|
break;
|
|
148
171
|
|
|
149
|
-
case '
|
|
172
|
+
case 'get_recent_conversations':
|
|
150
173
|
result = await this.getRecentConversations(args);
|
|
151
174
|
break;
|
|
152
175
|
|
|
153
|
-
case '
|
|
176
|
+
case 'get_memory_context':
|
|
154
177
|
result = await this.getMemoryContext(args);
|
|
155
178
|
break;
|
|
156
179
|
|
|
157
|
-
case '
|
|
180
|
+
case 'manage_memory_preferences':
|
|
158
181
|
result = await this.manageMemoryPreferences(args);
|
|
159
182
|
break;
|
|
160
183
|
|
|
@@ -298,20 +321,20 @@ class MCPServer {
|
|
|
298
321
|
case 'get_perspectives':
|
|
299
322
|
return this.formatPerspectivesResponse(result);
|
|
300
323
|
|
|
301
|
-
case '
|
|
324
|
+
case 'force_cli_detection':
|
|
302
325
|
return this.formatCliDetectionResponse(result);
|
|
303
|
-
|
|
304
|
-
case '
|
|
326
|
+
|
|
327
|
+
case 'get_cli_status':
|
|
305
328
|
return this.formatCliStatusResponse(result);
|
|
306
|
-
|
|
307
|
-
case '
|
|
329
|
+
|
|
330
|
+
case 'send_cli_prompt':
|
|
308
331
|
return this.formatCliPromptResponse(result);
|
|
309
|
-
|
|
310
|
-
case '
|
|
311
|
-
case '
|
|
312
|
-
case '
|
|
313
|
-
case '
|
|
314
|
-
case '
|
|
332
|
+
|
|
333
|
+
case 'detect_memory_sources':
|
|
334
|
+
case 'extract_memory':
|
|
335
|
+
case 'get_recent_conversations':
|
|
336
|
+
case 'get_memory_context':
|
|
337
|
+
case 'manage_memory_preferences':
|
|
315
338
|
return this.formatMemoryResponse(result);
|
|
316
339
|
|
|
317
340
|
default:
|