polydev-ai 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mcp/manifest.json +226 -0
- package/mcp/server.js +333 -1
- package/mcp/stdio-wrapper.js +182 -32
- package/package.json +2 -1
package/mcp/manifest.json
CHANGED
|
@@ -441,6 +441,232 @@
|
|
|
441
441
|
}
|
|
442
442
|
}
|
|
443
443
|
]
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
"name": "polydev.detect_memory_sources",
|
|
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
|
+
"inputSchema": {
|
|
449
|
+
"type": "object",
|
|
450
|
+
"properties": {
|
|
451
|
+
"project_path": {
|
|
452
|
+
"type": "string",
|
|
453
|
+
"description": "Project directory path to scan for memory sources",
|
|
454
|
+
"default": "."
|
|
455
|
+
},
|
|
456
|
+
"cli_tools": {
|
|
457
|
+
"type": "array",
|
|
458
|
+
"items": {
|
|
459
|
+
"type": "string",
|
|
460
|
+
"enum": ["claude_code", "cline", "codex_cli", "cursor", "continue", "aider", "generic", "all"]
|
|
461
|
+
},
|
|
462
|
+
"description": "CLI tools to detect memory for",
|
|
463
|
+
"default": ["all"]
|
|
464
|
+
},
|
|
465
|
+
"memory_types": {
|
|
466
|
+
"type": "array",
|
|
467
|
+
"items": {
|
|
468
|
+
"type": "string",
|
|
469
|
+
"enum": ["global", "project", "conversation", "config"]
|
|
470
|
+
},
|
|
471
|
+
"description": "Types of memory to detect",
|
|
472
|
+
"default": ["global", "project", "conversation"]
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
"outputSchema": {
|
|
477
|
+
"type": "object",
|
|
478
|
+
"properties": {
|
|
479
|
+
"success": { "type": "boolean" },
|
|
480
|
+
"sources": {
|
|
481
|
+
"type": "array",
|
|
482
|
+
"items": {
|
|
483
|
+
"type": "object",
|
|
484
|
+
"properties": {
|
|
485
|
+
"cli_tool": { "type": "string" },
|
|
486
|
+
"memory_type": { "type": "string" },
|
|
487
|
+
"file_path": { "type": "string" },
|
|
488
|
+
"exists": { "type": "boolean" },
|
|
489
|
+
"size_bytes": { "type": "number" },
|
|
490
|
+
"last_modified": { "type": "string" }
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
"summary": {
|
|
495
|
+
"type": "object",
|
|
496
|
+
"properties": {
|
|
497
|
+
"total_sources": { "type": "number" },
|
|
498
|
+
"sources_by_tool": { "type": "object" },
|
|
499
|
+
"sources_by_type": { "type": "object" }
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
"name": "polydev.extract_memory",
|
|
507
|
+
"description": "Extract and optionally encrypt memory content from detected CLI tool sources. Provides TF-IDF relevance scoring and content analysis.",
|
|
508
|
+
"inputSchema": {
|
|
509
|
+
"type": "object",
|
|
510
|
+
"properties": {
|
|
511
|
+
"user_token": {
|
|
512
|
+
"type": "string",
|
|
513
|
+
"description": "Polydev user authentication token for server storage",
|
|
514
|
+
"minLength": 1
|
|
515
|
+
},
|
|
516
|
+
"project_path": {
|
|
517
|
+
"type": "string",
|
|
518
|
+
"description": "Project directory path",
|
|
519
|
+
"default": "."
|
|
520
|
+
},
|
|
521
|
+
"cli_tools": {
|
|
522
|
+
"type": "array",
|
|
523
|
+
"items": { "type": "string" },
|
|
524
|
+
"default": ["all"]
|
|
525
|
+
},
|
|
526
|
+
"memory_types": {
|
|
527
|
+
"type": "array",
|
|
528
|
+
"items": { "type": "string" },
|
|
529
|
+
"default": ["global", "project", "conversation"]
|
|
530
|
+
},
|
|
531
|
+
"encryption_enabled": {
|
|
532
|
+
"type": "boolean",
|
|
533
|
+
"description": "Enable zero-knowledge encryption for extracted content",
|
|
534
|
+
"default": true
|
|
535
|
+
},
|
|
536
|
+
"max_file_size_kb": {
|
|
537
|
+
"type": "integer",
|
|
538
|
+
"minimum": 1,
|
|
539
|
+
"maximum": 1000,
|
|
540
|
+
"default": 500
|
|
541
|
+
},
|
|
542
|
+
"relevance_threshold": {
|
|
543
|
+
"type": "number",
|
|
544
|
+
"minimum": 0,
|
|
545
|
+
"maximum": 1,
|
|
546
|
+
"default": 0.3
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
"required": ["user_token"]
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
"name": "polydev.get_recent_conversations",
|
|
554
|
+
"description": "Get recent conversations from CLI tools with TF-IDF relevance scoring against query context. Supports encrypted storage and retrieval.",
|
|
555
|
+
"inputSchema": {
|
|
556
|
+
"type": "object",
|
|
557
|
+
"properties": {
|
|
558
|
+
"user_token": {
|
|
559
|
+
"type": "string",
|
|
560
|
+
"description": "Polydev user authentication token",
|
|
561
|
+
"minLength": 1
|
|
562
|
+
},
|
|
563
|
+
"query_context": {
|
|
564
|
+
"type": "string",
|
|
565
|
+
"description": "Current query for relevance scoring and context matching"
|
|
566
|
+
},
|
|
567
|
+
"limit": {
|
|
568
|
+
"type": "integer",
|
|
569
|
+
"minimum": 1,
|
|
570
|
+
"maximum": 50,
|
|
571
|
+
"default": 6
|
|
572
|
+
},
|
|
573
|
+
"cli_tools": {
|
|
574
|
+
"type": "array",
|
|
575
|
+
"items": { "type": "string" },
|
|
576
|
+
"default": ["all"]
|
|
577
|
+
},
|
|
578
|
+
"time_range_hours": {
|
|
579
|
+
"type": "integer",
|
|
580
|
+
"minimum": 1,
|
|
581
|
+
"maximum": 168,
|
|
582
|
+
"default": 24
|
|
583
|
+
},
|
|
584
|
+
"project_path": {
|
|
585
|
+
"type": "string",
|
|
586
|
+
"default": "."
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
"required": ["user_token"]
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
"name": "polydev.get_memory_context",
|
|
594
|
+
"description": "Get formatted memory and conversation context for injection into prompts. Automatically handles decryption and relevance scoring.",
|
|
595
|
+
"inputSchema": {
|
|
596
|
+
"type": "object",
|
|
597
|
+
"properties": {
|
|
598
|
+
"user_token": {
|
|
599
|
+
"type": "string",
|
|
600
|
+
"description": "Polydev user authentication token",
|
|
601
|
+
"minLength": 1
|
|
602
|
+
},
|
|
603
|
+
"query_context": {
|
|
604
|
+
"type": "string",
|
|
605
|
+
"description": "Query to find relevant memory and conversations for"
|
|
606
|
+
},
|
|
607
|
+
"max_memory_kb": {
|
|
608
|
+
"type": "integer",
|
|
609
|
+
"minimum": 1,
|
|
610
|
+
"maximum": 200,
|
|
611
|
+
"default": 50
|
|
612
|
+
},
|
|
613
|
+
"max_conversations": {
|
|
614
|
+
"type": "integer",
|
|
615
|
+
"minimum": 1,
|
|
616
|
+
"maximum": 20,
|
|
617
|
+
"default": 6
|
|
618
|
+
},
|
|
619
|
+
"cli_tools": {
|
|
620
|
+
"type": "array",
|
|
621
|
+
"items": { "type": "string" },
|
|
622
|
+
"default": ["all"]
|
|
623
|
+
},
|
|
624
|
+
"memory_types": {
|
|
625
|
+
"type": "array",
|
|
626
|
+
"items": { "type": "string" },
|
|
627
|
+
"default": ["project", "conversation"]
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
"required": ["user_token"]
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
"name": "polydev.manage_memory_preferences",
|
|
635
|
+
"description": "Configure memory extraction and privacy preferences. Control which CLI tools and memory types are enabled, encryption settings, and context injection behavior.",
|
|
636
|
+
"inputSchema": {
|
|
637
|
+
"type": "object",
|
|
638
|
+
"properties": {
|
|
639
|
+
"user_token": {
|
|
640
|
+
"type": "string",
|
|
641
|
+
"description": "Polydev user authentication token",
|
|
642
|
+
"minLength": 1
|
|
643
|
+
},
|
|
644
|
+
"action": {
|
|
645
|
+
"type": "string",
|
|
646
|
+
"enum": ["get", "update"],
|
|
647
|
+
"description": "Action to perform",
|
|
648
|
+
"default": "get"
|
|
649
|
+
},
|
|
650
|
+
"preferences": {
|
|
651
|
+
"type": "object",
|
|
652
|
+
"description": "Memory preferences to update",
|
|
653
|
+
"properties": {
|
|
654
|
+
"memory_enabled": { "type": "boolean" },
|
|
655
|
+
"privacy_mode": { "type": "boolean" },
|
|
656
|
+
"auto_extraction": { "type": "boolean" },
|
|
657
|
+
"cli_tools_enabled": { "type": "object" },
|
|
658
|
+
"memory_types_enabled": { "type": "object" },
|
|
659
|
+
"auto_inject_context": { "type": "boolean" },
|
|
660
|
+
"context_relevance_threshold": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
661
|
+
"max_conversations_inject": { "type": "integer" },
|
|
662
|
+
"max_memory_inject_kb": { "type": "integer" },
|
|
663
|
+
"key_rotation_days": { "type": "integer" },
|
|
664
|
+
"cache_ttl_minutes": { "type": "integer" }
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
"required": ["user_token"]
|
|
669
|
+
}
|
|
444
670
|
}
|
|
445
671
|
],
|
|
446
672
|
"configuration": {
|
package/mcp/server.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const CLIManager = require('../lib/cliManager').default;
|
|
6
|
+
const UniversalMemoryExtractor = require('../src/lib/universalMemoryExtractor').UniversalMemoryExtractor;
|
|
6
7
|
|
|
7
8
|
class MCPServer {
|
|
8
9
|
constructor() {
|
|
@@ -14,6 +15,7 @@ class MCPServer {
|
|
|
14
15
|
|
|
15
16
|
this.tools = new Map();
|
|
16
17
|
this.cliManager = new CLIManager();
|
|
18
|
+
this.memoryExtractor = new UniversalMemoryExtractor();
|
|
17
19
|
this.loadManifest();
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -136,6 +138,26 @@ class MCPServer {
|
|
|
136
138
|
result = await this.sendCliPrompt(args);
|
|
137
139
|
break;
|
|
138
140
|
|
|
141
|
+
case 'polydev.detect_memory_sources':
|
|
142
|
+
result = await this.detectMemorySources(args);
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case 'polydev.extract_memory':
|
|
146
|
+
result = await this.extractMemory(args);
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case 'polydev.get_recent_conversations':
|
|
150
|
+
result = await this.getRecentConversations(args);
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case 'polydev.get_memory_context':
|
|
154
|
+
result = await this.getMemoryContext(args);
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
case 'polydev.manage_memory_preferences':
|
|
158
|
+
result = await this.manageMemoryPreferences(args);
|
|
159
|
+
break;
|
|
160
|
+
|
|
139
161
|
default:
|
|
140
162
|
throw new Error(`Tool ${name} not implemented`);
|
|
141
163
|
}
|
|
@@ -172,6 +194,30 @@ class MCPServer {
|
|
|
172
194
|
throw new Error('prompt is required and must be a string');
|
|
173
195
|
}
|
|
174
196
|
|
|
197
|
+
// Auto-inject memory context if enabled and not already provided
|
|
198
|
+
let enhancedPrompt = args.prompt;
|
|
199
|
+
if (!args.project_memory && args.auto_inject_memory !== false) {
|
|
200
|
+
try {
|
|
201
|
+
console.error('[Polydev MCP] Attempting to inject memory context...');
|
|
202
|
+
const memoryContext = await this.getMemoryContext({
|
|
203
|
+
prompt: args.prompt,
|
|
204
|
+
cli_tools: ['all'],
|
|
205
|
+
max_entries: 3
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (memoryContext.success && memoryContext.context.entries.length > 0) {
|
|
209
|
+
const contextText = memoryContext.context.entries
|
|
210
|
+
.map(entry => `[${entry.source}] ${entry.content.substring(0, 500)}`)
|
|
211
|
+
.join('\n\n');
|
|
212
|
+
|
|
213
|
+
enhancedPrompt = `Context from previous work:\n${contextText}\n\nCurrent request:\n${args.prompt}`;
|
|
214
|
+
console.error(`[Polydev MCP] Injected ${memoryContext.context.entries.length} memory entries`);
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error('[Polydev MCP] Memory injection failed, continuing without:', error.message);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
175
221
|
// Support both parameter token (Claude Code) and environment token (Cline)
|
|
176
222
|
const userToken = args.user_token || process.env.POLYDEV_USER_TOKEN;
|
|
177
223
|
|
|
@@ -204,7 +250,11 @@ class MCPServer {
|
|
|
204
250
|
method: 'tools/call',
|
|
205
251
|
params: {
|
|
206
252
|
name: 'get_perspectives',
|
|
207
|
-
arguments:
|
|
253
|
+
arguments: {
|
|
254
|
+
...args,
|
|
255
|
+
prompt: enhancedPrompt,
|
|
256
|
+
project_memory: enhancedPrompt !== args.prompt ? 'auto-injected' : args.project_memory
|
|
257
|
+
}
|
|
208
258
|
},
|
|
209
259
|
id: 1
|
|
210
260
|
})
|
|
@@ -257,6 +307,13 @@ class MCPServer {
|
|
|
257
307
|
case 'polydev.send_cli_prompt':
|
|
258
308
|
return this.formatCliPromptResponse(result);
|
|
259
309
|
|
|
310
|
+
case 'polydev.detect_memory_sources':
|
|
311
|
+
case 'polydev.extract_memory':
|
|
312
|
+
case 'polydev.get_recent_conversations':
|
|
313
|
+
case 'polydev.get_memory_context':
|
|
314
|
+
case 'polydev.manage_memory_preferences':
|
|
315
|
+
return this.formatMemoryResponse(result);
|
|
316
|
+
|
|
260
317
|
default:
|
|
261
318
|
return JSON.stringify(result, null, 2);
|
|
262
319
|
}
|
|
@@ -536,6 +593,281 @@ class MCPServer {
|
|
|
536
593
|
}
|
|
537
594
|
}
|
|
538
595
|
|
|
596
|
+
/**
|
|
597
|
+
* Detect available memory sources across CLI tools
|
|
598
|
+
*/
|
|
599
|
+
async detectMemorySources(args) {
|
|
600
|
+
console.log('[MCP Server] Detect memory sources requested');
|
|
601
|
+
|
|
602
|
+
try {
|
|
603
|
+
const { cli_tools = ['all'] } = args;
|
|
604
|
+
|
|
605
|
+
const sources = await this.memoryExtractor.detectMemorySources(cli_tools);
|
|
606
|
+
|
|
607
|
+
return {
|
|
608
|
+
success: true,
|
|
609
|
+
sources,
|
|
610
|
+
message: `Detected ${Object.keys(sources).length} memory sources`,
|
|
611
|
+
timestamp: new Date().toISOString()
|
|
612
|
+
};
|
|
613
|
+
} catch (error) {
|
|
614
|
+
console.error('[MCP Server] Memory detection error:', error);
|
|
615
|
+
return {
|
|
616
|
+
success: false,
|
|
617
|
+
error: error.message,
|
|
618
|
+
timestamp: new Date().toISOString()
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Extract memory from detected sources
|
|
625
|
+
*/
|
|
626
|
+
async extractMemory(args) {
|
|
627
|
+
console.log('[MCP Server] Extract memory requested');
|
|
628
|
+
|
|
629
|
+
try {
|
|
630
|
+
const { cli_tools = ['all'], memory_types = ['all'], project_path } = args;
|
|
631
|
+
|
|
632
|
+
const extractedMemory = await this.memoryExtractor.extractMemory({
|
|
633
|
+
cliTools: cli_tools,
|
|
634
|
+
memoryTypes: memory_types,
|
|
635
|
+
projectPath: project_path
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
return {
|
|
639
|
+
success: true,
|
|
640
|
+
memory: extractedMemory,
|
|
641
|
+
message: `Extracted ${extractedMemory.totalEntries} memory entries`,
|
|
642
|
+
timestamp: new Date().toISOString()
|
|
643
|
+
};
|
|
644
|
+
} catch (error) {
|
|
645
|
+
console.error('[MCP Server] Memory extraction error:', error);
|
|
646
|
+
return {
|
|
647
|
+
success: false,
|
|
648
|
+
error: error.message,
|
|
649
|
+
timestamp: new Date().toISOString()
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Get recent conversations from CLI tools
|
|
656
|
+
*/
|
|
657
|
+
async getRecentConversations(args) {
|
|
658
|
+
console.log('[MCP Server] Get recent conversations requested');
|
|
659
|
+
|
|
660
|
+
try {
|
|
661
|
+
const { cli_tools = ['all'], limit = 10, project_path } = args;
|
|
662
|
+
|
|
663
|
+
const conversations = await this.memoryExtractor.getRecentConversations({
|
|
664
|
+
cliTools: cli_tools,
|
|
665
|
+
limit,
|
|
666
|
+
projectPath: project_path
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
return {
|
|
670
|
+
success: true,
|
|
671
|
+
conversations,
|
|
672
|
+
message: `Retrieved ${conversations.length} recent conversations`,
|
|
673
|
+
timestamp: new Date().toISOString()
|
|
674
|
+
};
|
|
675
|
+
} catch (error) {
|
|
676
|
+
console.error('[MCP Server] Conversation retrieval error:', error);
|
|
677
|
+
return {
|
|
678
|
+
success: false,
|
|
679
|
+
error: error.message,
|
|
680
|
+
timestamp: new Date().toISOString()
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Get relevant memory context for current prompt
|
|
687
|
+
*/
|
|
688
|
+
async getMemoryContext(args) {
|
|
689
|
+
console.log('[MCP Server] Get memory context requested');
|
|
690
|
+
|
|
691
|
+
try {
|
|
692
|
+
const { prompt, cli_tools = ['all'], max_entries = 5, project_path } = args;
|
|
693
|
+
|
|
694
|
+
if (!prompt) {
|
|
695
|
+
throw new Error('prompt is required for context retrieval');
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const context = await this.memoryExtractor.getRelevantContext({
|
|
699
|
+
prompt,
|
|
700
|
+
cliTools: cli_tools,
|
|
701
|
+
maxEntries: max_entries,
|
|
702
|
+
projectPath: project_path
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
success: true,
|
|
707
|
+
context,
|
|
708
|
+
message: `Found ${context.entries.length} relevant context entries`,
|
|
709
|
+
relevanceScore: context.averageRelevance,
|
|
710
|
+
timestamp: new Date().toISOString()
|
|
711
|
+
};
|
|
712
|
+
} catch (error) {
|
|
713
|
+
console.error('[MCP Server] Context retrieval error:', error);
|
|
714
|
+
return {
|
|
715
|
+
success: false,
|
|
716
|
+
error: error.message,
|
|
717
|
+
timestamp: new Date().toISOString()
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Manage memory preferences and settings
|
|
724
|
+
*/
|
|
725
|
+
async manageMemoryPreferences(args) {
|
|
726
|
+
console.log('[MCP Server] Manage memory preferences requested');
|
|
727
|
+
|
|
728
|
+
try {
|
|
729
|
+
const { action = 'get', preferences = {} } = args;
|
|
730
|
+
|
|
731
|
+
let result;
|
|
732
|
+
|
|
733
|
+
switch (action) {
|
|
734
|
+
case 'get':
|
|
735
|
+
result = await this.memoryExtractor.getPreferences();
|
|
736
|
+
break;
|
|
737
|
+
|
|
738
|
+
case 'set':
|
|
739
|
+
result = await this.memoryExtractor.updatePreferences(preferences);
|
|
740
|
+
break;
|
|
741
|
+
|
|
742
|
+
case 'reset':
|
|
743
|
+
result = await this.memoryExtractor.resetPreferences();
|
|
744
|
+
break;
|
|
745
|
+
|
|
746
|
+
default:
|
|
747
|
+
throw new Error(`Unknown action: ${action}`);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
return {
|
|
751
|
+
success: true,
|
|
752
|
+
preferences: result,
|
|
753
|
+
message: `Memory preferences ${action} completed`,
|
|
754
|
+
timestamp: new Date().toISOString()
|
|
755
|
+
};
|
|
756
|
+
} catch (error) {
|
|
757
|
+
console.error('[MCP Server] Preferences management error:', error);
|
|
758
|
+
return {
|
|
759
|
+
success: false,
|
|
760
|
+
error: error.message,
|
|
761
|
+
timestamp: new Date().toISOString()
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Format memory tool responses
|
|
768
|
+
*/
|
|
769
|
+
formatMemoryResponse(result) {
|
|
770
|
+
if (!result.success) {
|
|
771
|
+
return `❌ Memory Operation Failed: ${result.error}`;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
let formatted = `# Memory Operation Results\n\n`;
|
|
775
|
+
formatted += `✅ ${result.message}\n`;
|
|
776
|
+
formatted += `⏰ ${result.timestamp}\n\n`;
|
|
777
|
+
|
|
778
|
+
// Format specific results based on operation type
|
|
779
|
+
if (result.sources) {
|
|
780
|
+
formatted += this.formatMemorySourcesResult(result.sources);
|
|
781
|
+
} else if (result.memory) {
|
|
782
|
+
formatted += this.formatExtractedMemoryResult(result.memory);
|
|
783
|
+
} else if (result.conversations) {
|
|
784
|
+
formatted += this.formatConversationsResult(result.conversations);
|
|
785
|
+
} else if (result.context) {
|
|
786
|
+
formatted += this.formatContextResult(result.context, result.relevanceScore);
|
|
787
|
+
} else if (result.preferences) {
|
|
788
|
+
formatted += this.formatPreferencesResult(result.preferences);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
return formatted;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
formatMemorySourcesResult(sources) {
|
|
795
|
+
let formatted = `## 📁 Memory Sources Detected\n\n`;
|
|
796
|
+
|
|
797
|
+
Object.entries(sources).forEach(([cliTool, toolSources]) => {
|
|
798
|
+
formatted += `### ${cliTool.toUpperCase().replace('_', ' ')}\n`;
|
|
799
|
+
|
|
800
|
+
Object.entries(toolSources).forEach(([memoryType, files]) => {
|
|
801
|
+
const icon = memoryType === 'global' ? '🌍' : memoryType === 'project' ? '📂' : '💬';
|
|
802
|
+
formatted += `${icon} **${memoryType}**: ${files.length} sources\n`;
|
|
803
|
+
|
|
804
|
+
files.forEach(file => {
|
|
805
|
+
const status = file.exists ? '✅' : '❌';
|
|
806
|
+
formatted += ` ${status} \`${file.path}\`\n`;
|
|
807
|
+
});
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
formatted += `\n`;
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
return formatted;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
formatExtractedMemoryResult(memory) {
|
|
817
|
+
let formatted = `## 🧠 Extracted Memory\n\n`;
|
|
818
|
+
formatted += `📊 **Total Entries**: ${memory.totalEntries}\n`;
|
|
819
|
+
formatted += `💾 **Total Size**: ${(memory.totalSize / 1024).toFixed(1)}KB\n\n`;
|
|
820
|
+
|
|
821
|
+
Object.entries(memory.byCliTool).forEach(([cliTool, toolMemory]) => {
|
|
822
|
+
formatted += `### ${cliTool.toUpperCase().replace('_', ' ')}\n`;
|
|
823
|
+
formatted += `- Global: ${toolMemory.global.length} entries\n`;
|
|
824
|
+
formatted += `- Project: ${toolMemory.project.length} entries\n`;
|
|
825
|
+
formatted += `- Conversations: ${toolMemory.conversations.length} entries\n\n`;
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
return formatted;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
formatConversationsResult(conversations) {
|
|
832
|
+
let formatted = `## 💬 Recent Conversations\n\n`;
|
|
833
|
+
|
|
834
|
+
conversations.forEach((conv, index) => {
|
|
835
|
+
formatted += `### ${index + 1}. ${conv.cliTool.toUpperCase().replace('_', ' ')}\n`;
|
|
836
|
+
formatted += `📅 ${new Date(conv.timestamp).toLocaleString()}\n`;
|
|
837
|
+
formatted += `💭 ${conv.messageCount} messages, ${(conv.size / 1024).toFixed(1)}KB\n`;
|
|
838
|
+
|
|
839
|
+
if (conv.topics && conv.topics.length > 0) {
|
|
840
|
+
formatted += `🏷️ Topics: ${conv.topics.join(', ')}\n`;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
formatted += `\n`;
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
return formatted;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
formatContextResult(context, relevanceScore) {
|
|
850
|
+
let formatted = `## 🎯 Relevant Context\n\n`;
|
|
851
|
+
formatted += `📊 **Average Relevance**: ${(relevanceScore * 100).toFixed(1)}%\n\n`;
|
|
852
|
+
|
|
853
|
+
context.entries.forEach((entry, index) => {
|
|
854
|
+
formatted += `### ${index + 1}. ${entry.source} (${(entry.relevance * 100).toFixed(1)}%)\n`;
|
|
855
|
+
formatted += `${entry.content.substring(0, 200)}...\n\n`;
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
return formatted;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
formatPreferencesResult(preferences) {
|
|
862
|
+
let formatted = `## ⚙️ Memory Preferences\n\n`;
|
|
863
|
+
|
|
864
|
+
Object.entries(preferences).forEach(([key, value]) => {
|
|
865
|
+
formatted += `- **${key}**: ${typeof value === 'object' ? JSON.stringify(value) : value}\n`;
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
return formatted;
|
|
869
|
+
}
|
|
870
|
+
|
|
539
871
|
// CLI status and usage tracking is handled locally by CLIManager
|
|
540
872
|
// No database integration needed - this MCP server runs independently
|
|
541
873
|
|
package/mcp/stdio-wrapper.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
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 CLIManager = require('../lib/cliManager')
|
|
6
|
+
const { CLIManager } = require('../lib/cliManager');
|
|
7
7
|
|
|
8
8
|
class StdioMCPWrapper {
|
|
9
9
|
constructor() {
|
|
@@ -204,7 +204,7 @@ class StdioMCPWrapper {
|
|
|
204
204
|
const providerId = args.provider_id; // Optional - detect specific provider
|
|
205
205
|
|
|
206
206
|
// Force detection using CLI Manager (no remote API calls)
|
|
207
|
-
const results = await this.cliManager.forceCliDetection(
|
|
207
|
+
const results = await this.cliManager.forceCliDetection(providerId);
|
|
208
208
|
|
|
209
209
|
// Save status locally to file-based cache
|
|
210
210
|
await this.saveLocalCliStatus(results);
|
|
@@ -241,13 +241,13 @@ class StdioMCPWrapper {
|
|
|
241
241
|
if (providerId) {
|
|
242
242
|
// Get specific provider status
|
|
243
243
|
const status = await this.cliManager.getCliStatus(providerId);
|
|
244
|
-
results
|
|
244
|
+
results = status;
|
|
245
245
|
} else {
|
|
246
246
|
// Get all providers status
|
|
247
|
-
const providers = this.cliManager.
|
|
247
|
+
const providers = this.cliManager.getAvailableProviders();
|
|
248
248
|
for (const provider of providers) {
|
|
249
249
|
const status = await this.cliManager.getCliStatus(provider.id);
|
|
250
|
-
results[provider.id] = status;
|
|
250
|
+
results[provider.id] = status[provider.id];
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
|
|
@@ -271,10 +271,10 @@ class StdioMCPWrapper {
|
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
/**
|
|
274
|
-
* Local CLI prompt sending
|
|
274
|
+
* Local CLI prompt sending with remote perspectives fallback/supplement
|
|
275
275
|
*/
|
|
276
276
|
async localSendCliPrompt(args) {
|
|
277
|
-
console.error(`[Stdio Wrapper] Local CLI prompt sending`);
|
|
277
|
+
console.error(`[Stdio Wrapper] Local CLI prompt sending with perspectives`);
|
|
278
278
|
|
|
279
279
|
try {
|
|
280
280
|
const { provider_id, prompt, mode = 'args', timeout_ms = 30000 } = args;
|
|
@@ -283,29 +283,37 @@ class StdioMCPWrapper {
|
|
|
283
283
|
throw new Error('provider_id and prompt are required');
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
//
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
286
|
+
// Use shorter timeout for faster fallback (5 seconds instead of 30)
|
|
287
|
+
const gracefulTimeout = Math.min(timeout_ms, 5000);
|
|
288
|
+
|
|
289
|
+
// Start both operations concurrently for better performance
|
|
290
|
+
const [localResult, perspectivesResult] = await Promise.allSettled([
|
|
291
|
+
this.cliManager.sendCliPrompt(provider_id, prompt, mode, gracefulTimeout),
|
|
292
|
+
this.callPerspectivesForCli(args, null)
|
|
293
|
+
]);
|
|
294
|
+
|
|
295
|
+
// Process results
|
|
296
|
+
const localResponse = localResult.status === 'fulfilled' ? localResult.value : {
|
|
297
|
+
success: false,
|
|
298
|
+
error: `CLI check failed: ${localResult.reason?.message || 'Unknown error'}`,
|
|
299
|
+
latency_ms: gracefulTimeout,
|
|
300
|
+
timestamp: new Date().toISOString()
|
|
301
|
+
};
|
|
296
302
|
|
|
297
|
-
|
|
298
|
-
success:
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
tokens_used: response.tokensUsed,
|
|
302
|
-
latency_ms: response.latencyMs,
|
|
303
|
-
provider: provider_id,
|
|
304
|
-
mode,
|
|
305
|
-
timestamp: new Date().toISOString(),
|
|
306
|
-
local_only: true
|
|
303
|
+
const perspectivesResponse = perspectivesResult.status === 'fulfilled' ? perspectivesResult.value : {
|
|
304
|
+
success: false,
|
|
305
|
+
error: `Perspectives failed: ${perspectivesResult.reason?.message || 'Unknown error'}`,
|
|
306
|
+
timestamp: new Date().toISOString()
|
|
307
307
|
};
|
|
308
308
|
|
|
309
|
+
// Record usage locally (file-based analytics) - non-blocking
|
|
310
|
+
this.recordLocalUsage(provider_id, prompt, localResponse).catch(err => {
|
|
311
|
+
console.error('[Stdio Wrapper] Usage recording failed (non-critical):', err.message);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Combine results
|
|
315
|
+
return this.combineCliAndPerspectives(localResponse, perspectivesResponse, args);
|
|
316
|
+
|
|
309
317
|
} catch (error) {
|
|
310
318
|
console.error('[Stdio Wrapper] Local CLI prompt error:', error);
|
|
311
319
|
return {
|
|
@@ -317,6 +325,132 @@ class StdioMCPWrapper {
|
|
|
317
325
|
}
|
|
318
326
|
}
|
|
319
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Call remote perspectives for CLI prompts
|
|
330
|
+
*/
|
|
331
|
+
async callPerspectivesForCli(args, localResult) {
|
|
332
|
+
console.error(`[Stdio Wrapper] Calling remote perspectives for CLI prompt`);
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
const perspectivesRequest = {
|
|
336
|
+
jsonrpc: '2.0',
|
|
337
|
+
method: 'tools/call',
|
|
338
|
+
params: {
|
|
339
|
+
name: 'get_perspectives',
|
|
340
|
+
arguments: {
|
|
341
|
+
prompt: args.prompt,
|
|
342
|
+
user_token: this.userToken,
|
|
343
|
+
// Let the remote server use user's configured preferences for models
|
|
344
|
+
// Don't specify models to use dashboard defaults
|
|
345
|
+
project_memory: 'none',
|
|
346
|
+
temperature: 0.7,
|
|
347
|
+
max_tokens: 2000
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
id: `perspectives-${Date.now()}`
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const remoteResponse = await this.forwardToRemoteServer(perspectivesRequest);
|
|
354
|
+
|
|
355
|
+
if (remoteResponse.result && remoteResponse.result.content && remoteResponse.result.content[0]) {
|
|
356
|
+
return {
|
|
357
|
+
success: true,
|
|
358
|
+
content: remoteResponse.result.content[0].text,
|
|
359
|
+
timestamp: new Date().toISOString()
|
|
360
|
+
};
|
|
361
|
+
} else if (remoteResponse.error) {
|
|
362
|
+
return {
|
|
363
|
+
success: false,
|
|
364
|
+
error: remoteResponse.error.message || 'Remote perspectives failed',
|
|
365
|
+
timestamp: new Date().toISOString()
|
|
366
|
+
};
|
|
367
|
+
} else {
|
|
368
|
+
return {
|
|
369
|
+
success: false,
|
|
370
|
+
error: 'Unexpected remote response format',
|
|
371
|
+
timestamp: new Date().toISOString()
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
} catch (error) {
|
|
375
|
+
console.error('[Stdio Wrapper] Perspectives call error:', error);
|
|
376
|
+
return {
|
|
377
|
+
success: false,
|
|
378
|
+
error: `Perspectives request failed: ${error.message}`,
|
|
379
|
+
timestamp: new Date().toISOString()
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Combine local CLI and remote perspectives results
|
|
386
|
+
*/
|
|
387
|
+
combineCliAndPerspectives(localResult, perspectivesResult, args) {
|
|
388
|
+
const combinedResult = {
|
|
389
|
+
success: true,
|
|
390
|
+
timestamp: new Date().toISOString(),
|
|
391
|
+
provider: args.provider_id,
|
|
392
|
+
mode: args.mode,
|
|
393
|
+
sections: {
|
|
394
|
+
local: localResult,
|
|
395
|
+
remote: perspectivesResult
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// Determine overall success and fallback status
|
|
400
|
+
if (localResult.success && perspectivesResult.success) {
|
|
401
|
+
combinedResult.content = this.formatCombinedResponse(localResult, perspectivesResult, false);
|
|
402
|
+
combinedResult.tokens_used = localResult.tokens_used || 0;
|
|
403
|
+
combinedResult.latency_ms = localResult.latency_ms || 0;
|
|
404
|
+
} else if (!localResult.success && perspectivesResult.success) {
|
|
405
|
+
// Fallback case
|
|
406
|
+
combinedResult.content = this.formatCombinedResponse(localResult, perspectivesResult, true);
|
|
407
|
+
combinedResult.fallback_used = true;
|
|
408
|
+
combinedResult.tokens_used = 0; // No local tokens used
|
|
409
|
+
} else if (localResult.success && !perspectivesResult.success) {
|
|
410
|
+
// Local succeeded, remote failed
|
|
411
|
+
combinedResult.content = this.formatCombinedResponse(localResult, perspectivesResult, false);
|
|
412
|
+
combinedResult.tokens_used = localResult.tokens_used || 0;
|
|
413
|
+
combinedResult.latency_ms = localResult.latency_ms || 0;
|
|
414
|
+
} else {
|
|
415
|
+
// Both failed
|
|
416
|
+
combinedResult.success = false;
|
|
417
|
+
combinedResult.error = `Local CLI failed: ${localResult.error}; Perspectives also failed: ${perspectivesResult.error}`;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return combinedResult;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Format combined response text
|
|
425
|
+
*/
|
|
426
|
+
formatCombinedResponse(localResult, perspectivesResult, isFallback) {
|
|
427
|
+
let formatted = '';
|
|
428
|
+
|
|
429
|
+
if (localResult.success) {
|
|
430
|
+
// Local CLI succeeded
|
|
431
|
+
formatted += `🟢 **Local CLI Response** (${localResult.provider} - ${localResult.mode} mode)\n\n`;
|
|
432
|
+
formatted += `${localResult.content}\n\n`;
|
|
433
|
+
formatted += `*Latency: ${localResult.latency_ms || 0}ms | Tokens: ${localResult.tokens_used || 0}*\n\n`;
|
|
434
|
+
formatted += `---\n\n`;
|
|
435
|
+
} else if (isFallback) {
|
|
436
|
+
// Local CLI failed, using fallback
|
|
437
|
+
formatted += `⚠️ **Local CLI unavailable**: ${localResult.error}\n`;
|
|
438
|
+
formatted += `Using perspectives fallback.\n\n`;
|
|
439
|
+
formatted += `---\n\n`;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (perspectivesResult.success) {
|
|
443
|
+
const title = isFallback ? '🧠 **Perspectives Fallback**' : '🧠 **Supplemental Multi-Model Perspectives**';
|
|
444
|
+
formatted += `${title}\n\n`;
|
|
445
|
+
formatted += `${perspectivesResult.content}\n\n`;
|
|
446
|
+
} else if (!isFallback) {
|
|
447
|
+
// Show remote error only if not in fallback mode
|
|
448
|
+
formatted += `❌ **Perspectives request failed**: ${perspectivesResult.error}\n\n`;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return formatted.trim();
|
|
452
|
+
}
|
|
453
|
+
|
|
320
454
|
/**
|
|
321
455
|
* Save CLI status to local file cache
|
|
322
456
|
*/
|
|
@@ -354,10 +488,20 @@ class StdioMCPWrapper {
|
|
|
354
488
|
const polydevevDir = path.join(homeDir, '.polydev');
|
|
355
489
|
const usageFile = path.join(polydevevDir, 'cli-usage.json');
|
|
356
490
|
|
|
491
|
+
// Ensure directory exists
|
|
492
|
+
if (!fs.existsSync(polydevevDir)) {
|
|
493
|
+
fs.mkdirSync(polydevevDir, { recursive: true });
|
|
494
|
+
}
|
|
495
|
+
|
|
357
496
|
// Load existing usage data
|
|
358
497
|
let usageData = [];
|
|
359
498
|
if (fs.existsSync(usageFile)) {
|
|
360
|
-
|
|
499
|
+
try {
|
|
500
|
+
usageData = JSON.parse(fs.readFileSync(usageFile, 'utf8'));
|
|
501
|
+
} catch (parseError) {
|
|
502
|
+
console.error('[Stdio Wrapper] Failed to parse existing usage file, starting fresh:', parseError);
|
|
503
|
+
usageData = [];
|
|
504
|
+
}
|
|
361
505
|
}
|
|
362
506
|
|
|
363
507
|
// Add new usage record
|
|
@@ -366,8 +510,8 @@ class StdioMCPWrapper {
|
|
|
366
510
|
provider: providerId,
|
|
367
511
|
prompt_length: prompt.length,
|
|
368
512
|
success: response.success,
|
|
369
|
-
latency_ms: response.latencyMs,
|
|
370
|
-
tokens_used: response.tokensUsed || 0
|
|
513
|
+
latency_ms: response.latency_ms || response.latencyMs || 0,
|
|
514
|
+
tokens_used: response.tokens_used || response.tokensUsed || 0
|
|
371
515
|
});
|
|
372
516
|
|
|
373
517
|
// Keep only last 1000 records
|
|
@@ -379,7 +523,8 @@ class StdioMCPWrapper {
|
|
|
379
523
|
console.error(`[Stdio Wrapper] Usage recorded locally`);
|
|
380
524
|
|
|
381
525
|
} catch (error) {
|
|
382
|
-
console.error('[Stdio Wrapper] Failed to record local usage:', error);
|
|
526
|
+
console.error('[Stdio Wrapper] Failed to record local usage (non-critical):', error.message);
|
|
527
|
+
// Don't throw - this is non-critical functionality
|
|
383
528
|
}
|
|
384
529
|
}
|
|
385
530
|
|
|
@@ -391,8 +536,13 @@ class StdioMCPWrapper {
|
|
|
391
536
|
return `❌ **CLI Error**\n\n${result.error}\n\n*Timestamp: ${result.timestamp}*`;
|
|
392
537
|
}
|
|
393
538
|
|
|
539
|
+
// Handle combined CLI + perspectives response
|
|
540
|
+
if (result.sections) {
|
|
541
|
+
return result.content;
|
|
542
|
+
}
|
|
543
|
+
|
|
394
544
|
if (result.content) {
|
|
395
|
-
//
|
|
545
|
+
// Standard prompt response
|
|
396
546
|
return `✅ **CLI Response** (${result.provider || 'Unknown'} - ${result.mode || 'unknown'} mode)\n\n${result.content}\n\n*Latency: ${result.latency_ms || 0}ms | Tokens: ${result.tokens_used || 0} | ${result.timestamp}*`;
|
|
397
547
|
} else {
|
|
398
548
|
// Status/detection response
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polydev-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
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",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"lucide-react": "^0.542.0",
|
|
60
60
|
"marked": "^16.2.1",
|
|
61
61
|
"next": "15.0.0",
|
|
62
|
+
"polydev-ai": "^1.0.0",
|
|
62
63
|
"posthog-js": "^1.157.2",
|
|
63
64
|
"react": "^18.3.1",
|
|
64
65
|
"react-dom": "^18.3.1",
|