claude-conversation-memory-mcp 1.3.0 → 1.5.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/README.md +230 -31
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +13 -1
- package/dist/mcp-server.js.map +1 -1
- package/dist/parsers/CodexConversationParser.d.ts +51 -0
- package/dist/parsers/CodexConversationParser.d.ts.map +1 -0
- package/dist/parsers/CodexConversationParser.js +279 -0
- package/dist/parsers/CodexConversationParser.js.map +1 -0
- package/dist/parsers/ConversationParser.d.ts +1 -0
- package/dist/parsers/ConversationParser.d.ts.map +1 -1
- package/dist/parsers/ConversationParser.js.map +1 -1
- package/dist/storage/ConversationStorage.d.ts.map +1 -1
- package/dist/storage/ConversationStorage.js +3 -3
- package/dist/storage/ConversationStorage.js.map +1 -1
- package/dist/storage/GlobalIndex.d.ts +125 -0
- package/dist/storage/GlobalIndex.d.ts.map +1 -0
- package/dist/storage/GlobalIndex.js +243 -0
- package/dist/storage/GlobalIndex.js.map +1 -0
- package/dist/storage/SQLiteManager.d.ts +4 -0
- package/dist/storage/SQLiteManager.d.ts.map +1 -1
- package/dist/storage/SQLiteManager.js +36 -2
- package/dist/storage/SQLiteManager.js.map +1 -1
- package/dist/storage/schema.sql +26 -0
- package/dist/tools/ToolDefinitions.d.ts +150 -0
- package/dist/tools/ToolDefinitions.d.ts.map +1 -1
- package/dist/tools/ToolDefinitions.js +152 -3
- package/dist/tools/ToolDefinitions.js.map +1 -1
- package/dist/tools/ToolHandlers.d.ts +58 -17
- package/dist/tools/ToolHandlers.d.ts.map +1 -1
- package/dist/tools/ToolHandlers.js +422 -40
- package/dist/tools/ToolHandlers.js.map +1 -1
- package/dist/types/ToolTypes.d.ts +95 -0
- package/dist/types/ToolTypes.d.ts.map +1 -1
- package/package.json +2 -1
- package/scripts/changelog-check.sh +62 -0
|
@@ -598,7 +598,7 @@ export class ToolHandlers {
|
|
|
598
598
|
};
|
|
599
599
|
}
|
|
600
600
|
/**
|
|
601
|
-
* Query history of tool uses (bash commands, file edits, reads, etc.).
|
|
601
|
+
* Query history of tool uses (bash commands, file edits, reads, etc.) with pagination and filtering.
|
|
602
602
|
*
|
|
603
603
|
* Shows what tools were used during conversations and their results. Useful
|
|
604
604
|
* for understanding what commands were run, what files were edited, and
|
|
@@ -608,72 +608,152 @@ export class ToolHandlers {
|
|
|
608
608
|
* - `tool_name`: Optional filter by tool name (Bash, Edit, Write, Read)
|
|
609
609
|
* - `file_path`: Optional filter by file path
|
|
610
610
|
* - `limit`: Maximum number of results (default: 20)
|
|
611
|
+
* - `offset`: Skip N results for pagination (default: 0)
|
|
612
|
+
* - `include_content`: Include tool content in response (default: true, false for metadata only)
|
|
613
|
+
* - `max_content_length`: Maximum characters per content field (default: 500)
|
|
614
|
+
* - `date_range`: Filter by timestamp range [start, end]
|
|
615
|
+
* - `conversation_id`: Filter by specific conversation
|
|
616
|
+
* - `errors_only`: Show only failed tool uses (default: false)
|
|
611
617
|
*
|
|
612
618
|
* @returns Tool history containing:
|
|
613
619
|
* - `tool_name`: Tool filter if applied
|
|
614
620
|
* - `file_path`: File filter if applied
|
|
615
|
-
* - `tool_uses`: Array of tool uses
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
* - `content`: Result content
|
|
621
|
-
* - `is_error`: Whether the tool failed
|
|
622
|
-
* - `stdout`: Standard output (for Bash)
|
|
623
|
-
* - `stderr`: Standard error (for Bash)
|
|
624
|
-
* - `timestamp`: When the tool was used
|
|
625
|
-
* - `total_found`: Number of tool uses returned
|
|
621
|
+
* - `tool_uses`: Array of tool uses (may have truncated content)
|
|
622
|
+
* - `total_found`: Number of results returned in this page
|
|
623
|
+
* - `total_in_database`: Total matching records in database
|
|
624
|
+
* - `has_more`: Whether more results exist beyond current page
|
|
625
|
+
* - `offset`: Current offset position
|
|
626
626
|
*
|
|
627
627
|
* @example
|
|
628
628
|
* ```typescript
|
|
629
|
-
*
|
|
629
|
+
* // Get first page of Bash commands
|
|
630
|
+
* const page1 = await handlers.getToolHistory({
|
|
630
631
|
* tool_name: 'Bash',
|
|
631
|
-
* limit:
|
|
632
|
+
* limit: 20,
|
|
633
|
+
* offset: 0
|
|
632
634
|
* });
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
*
|
|
635
|
+
*
|
|
636
|
+
* // Get metadata only (no content)
|
|
637
|
+
* const metadata = await handlers.getToolHistory({
|
|
638
|
+
* include_content: false,
|
|
639
|
+
* limit: 50
|
|
640
|
+
* });
|
|
641
|
+
*
|
|
642
|
+
* // Get errors from last 24 hours
|
|
643
|
+
* const errors = await handlers.getToolHistory({
|
|
644
|
+
* errors_only: true,
|
|
645
|
+
* date_range: [Date.now() - 86400000, Date.now()]
|
|
636
646
|
* });
|
|
637
647
|
* ```
|
|
638
648
|
*/
|
|
639
649
|
async getToolHistory(args) {
|
|
640
650
|
const typedArgs = args;
|
|
641
|
-
const { tool_name, file_path, limit = 20 } = typedArgs;
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
651
|
+
const { tool_name, file_path, limit = 20, offset = 0, include_content = true, max_content_length = 500, date_range, conversation_id, errors_only = false, } = typedArgs;
|
|
652
|
+
// Helper function to truncate text with indicator
|
|
653
|
+
const truncateText = (text, maxLength) => {
|
|
654
|
+
if (!text) {
|
|
655
|
+
return { value: undefined, truncated: false };
|
|
656
|
+
}
|
|
657
|
+
if (text.length <= maxLength) {
|
|
658
|
+
return { value: text, truncated: false };
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
value: text.substring(0, maxLength) + '... (truncated)',
|
|
662
|
+
truncated: true,
|
|
663
|
+
};
|
|
664
|
+
};
|
|
665
|
+
// Build WHERE clause for filters
|
|
666
|
+
let whereClause = "WHERE 1=1";
|
|
648
667
|
const params = [];
|
|
649
668
|
if (tool_name) {
|
|
650
|
-
|
|
669
|
+
whereClause += " AND tu.tool_name = ?";
|
|
651
670
|
params.push(tool_name);
|
|
652
671
|
}
|
|
653
672
|
if (file_path) {
|
|
654
673
|
const sanitized = sanitizeForLike(file_path);
|
|
655
|
-
|
|
674
|
+
whereClause += " AND tu.tool_input LIKE ? ESCAPE '\\'";
|
|
656
675
|
params.push(`%${sanitized}%`);
|
|
657
676
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
677
|
+
if (date_range && date_range.length === 2) {
|
|
678
|
+
whereClause += " AND tu.timestamp BETWEEN ? AND ?";
|
|
679
|
+
params.push(date_range[0], date_range[1]);
|
|
680
|
+
}
|
|
681
|
+
if (conversation_id) {
|
|
682
|
+
whereClause += " AND tu.message_id IN (SELECT id FROM messages WHERE conversation_id = ?)";
|
|
683
|
+
params.push(conversation_id);
|
|
684
|
+
}
|
|
685
|
+
if (errors_only) {
|
|
686
|
+
whereClause += " AND tr.is_error = 1";
|
|
687
|
+
}
|
|
688
|
+
// Get total count of matching records
|
|
689
|
+
const countSql = `
|
|
690
|
+
SELECT COUNT(*) as total
|
|
691
|
+
FROM tool_uses tu
|
|
692
|
+
LEFT JOIN tool_results tr ON tu.id = tr.tool_use_id
|
|
693
|
+
${whereClause}
|
|
694
|
+
`;
|
|
695
|
+
const countResult = this.db.prepare(countSql).get(...params);
|
|
696
|
+
const totalInDatabase = countResult.total;
|
|
697
|
+
// Get paginated results
|
|
698
|
+
const sql = `
|
|
699
|
+
SELECT tu.*, tr.content as result_content, tr.is_error, tr.stdout, tr.stderr
|
|
700
|
+
FROM tool_uses tu
|
|
701
|
+
LEFT JOIN tool_results tr ON tu.id = tr.tool_use_id
|
|
702
|
+
${whereClause}
|
|
703
|
+
ORDER BY tu.timestamp DESC
|
|
704
|
+
LIMIT ? OFFSET ?
|
|
705
|
+
`;
|
|
706
|
+
const queryParams = [...params, limit, offset];
|
|
707
|
+
const toolUses = this.db.prepare(sql).all(...queryParams);
|
|
708
|
+
// Calculate pagination metadata
|
|
709
|
+
const hasMore = offset + toolUses.length < totalInDatabase;
|
|
661
710
|
return {
|
|
662
711
|
tool_name,
|
|
663
712
|
file_path,
|
|
664
|
-
tool_uses: toolUses.map((t) =>
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
result
|
|
669
|
-
content: t.result_content,
|
|
713
|
+
tool_uses: toolUses.map((t) => {
|
|
714
|
+
// Parse tool input
|
|
715
|
+
const toolInput = JSON.parse(t.tool_input || "{}");
|
|
716
|
+
// Build result object based on include_content setting
|
|
717
|
+
const result = {
|
|
670
718
|
is_error: Boolean(t.is_error),
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
719
|
+
};
|
|
720
|
+
if (include_content) {
|
|
721
|
+
// Truncate content fields if they exist
|
|
722
|
+
const contentTrunc = truncateText(t.result_content, max_content_length);
|
|
723
|
+
const stdoutTrunc = truncateText(t.stdout, max_content_length);
|
|
724
|
+
const stderrTrunc = truncateText(t.stderr, max_content_length);
|
|
725
|
+
if (contentTrunc.value !== undefined) {
|
|
726
|
+
result.content = contentTrunc.value;
|
|
727
|
+
if (contentTrunc.truncated) {
|
|
728
|
+
result.content_truncated = true;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (stdoutTrunc.value !== undefined) {
|
|
732
|
+
result.stdout = stdoutTrunc.value;
|
|
733
|
+
if (stdoutTrunc.truncated) {
|
|
734
|
+
result.stdout_truncated = true;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (stderrTrunc.value !== undefined) {
|
|
738
|
+
result.stderr = stderrTrunc.value;
|
|
739
|
+
if (stderrTrunc.truncated) {
|
|
740
|
+
result.stderr_truncated = true;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
// If include_content=false, only return is_error (no content, stdout, stderr)
|
|
745
|
+
return {
|
|
746
|
+
tool_use_id: t.id,
|
|
747
|
+
tool_name: t.tool_name,
|
|
748
|
+
tool_input: toolInput,
|
|
749
|
+
result,
|
|
750
|
+
timestamp: new Date(t.timestamp).toISOString(),
|
|
751
|
+
};
|
|
752
|
+
}),
|
|
676
753
|
total_found: toolUses.length,
|
|
754
|
+
total_in_database: totalInDatabase,
|
|
755
|
+
has_more: hasMore,
|
|
756
|
+
offset,
|
|
677
757
|
};
|
|
678
758
|
}
|
|
679
759
|
/**
|
|
@@ -1329,5 +1409,307 @@ export class ToolHandlers {
|
|
|
1329
1409
|
};
|
|
1330
1410
|
}
|
|
1331
1411
|
}
|
|
1412
|
+
// ==================== Global Cross-Project Tools ====================
|
|
1413
|
+
/**
|
|
1414
|
+
* Index all projects (Claude Code + Codex).
|
|
1415
|
+
*
|
|
1416
|
+
* Discovers and indexes all projects from both Claude Code and Codex,
|
|
1417
|
+
* registering them in a global index for cross-project search.
|
|
1418
|
+
*
|
|
1419
|
+
* @param args - Indexing arguments
|
|
1420
|
+
* @returns Summary of all indexed projects
|
|
1421
|
+
*/
|
|
1422
|
+
async indexAllProjects(args) {
|
|
1423
|
+
const { GlobalIndex } = await import("../storage/GlobalIndex.js");
|
|
1424
|
+
const { homedir } = await import("os");
|
|
1425
|
+
const { join } = await import("path");
|
|
1426
|
+
const { existsSync, readdirSync } = await import("fs");
|
|
1427
|
+
const typedArgs = args;
|
|
1428
|
+
const { include_codex = true, include_claude_code = true, codex_path = join(homedir(), ".codex"), claude_projects_path = join(homedir(), ".claude", "projects"), } = typedArgs;
|
|
1429
|
+
const globalIndex = new GlobalIndex();
|
|
1430
|
+
try {
|
|
1431
|
+
const projects = [];
|
|
1432
|
+
const errors = [];
|
|
1433
|
+
let totalMessages = 0;
|
|
1434
|
+
let totalConversations = 0;
|
|
1435
|
+
let totalDecisions = 0;
|
|
1436
|
+
let totalMistakes = 0;
|
|
1437
|
+
// Index Codex if requested
|
|
1438
|
+
if (include_codex && existsSync(codex_path)) {
|
|
1439
|
+
try {
|
|
1440
|
+
const { CodexConversationParser } = await import("../parsers/CodexConversationParser.js");
|
|
1441
|
+
const { SQLiteManager } = await import("../storage/SQLiteManager.js");
|
|
1442
|
+
const { ConversationStorage } = await import("../storage/ConversationStorage.js");
|
|
1443
|
+
// Create dedicated database for Codex
|
|
1444
|
+
const codexDbPath = join(codex_path, ".codex-conversations-memory.db");
|
|
1445
|
+
const codexDb = new SQLiteManager({ dbPath: codexDbPath });
|
|
1446
|
+
const codexStorage = new ConversationStorage(codexDb);
|
|
1447
|
+
// Parse Codex sessions
|
|
1448
|
+
const parser = new CodexConversationParser();
|
|
1449
|
+
const parseResult = parser.parseSession(codex_path);
|
|
1450
|
+
// Store all parsed data
|
|
1451
|
+
await codexStorage.storeConversations(parseResult.conversations);
|
|
1452
|
+
await codexStorage.storeMessages(parseResult.messages);
|
|
1453
|
+
await codexStorage.storeToolUses(parseResult.tool_uses);
|
|
1454
|
+
await codexStorage.storeToolResults(parseResult.tool_results);
|
|
1455
|
+
await codexStorage.storeFileEdits(parseResult.file_edits);
|
|
1456
|
+
await codexStorage.storeThinkingBlocks(parseResult.thinking_blocks);
|
|
1457
|
+
// Get stats from the database
|
|
1458
|
+
const stats = codexDb.getDatabase()
|
|
1459
|
+
.prepare("SELECT COUNT(*) as count FROM conversations")
|
|
1460
|
+
.get();
|
|
1461
|
+
const messageStats = codexDb.getDatabase()
|
|
1462
|
+
.prepare("SELECT COUNT(*) as count FROM messages")
|
|
1463
|
+
.get();
|
|
1464
|
+
const decisionStats = codexDb.getDatabase()
|
|
1465
|
+
.prepare("SELECT COUNT(*) as count FROM decisions")
|
|
1466
|
+
.get();
|
|
1467
|
+
const mistakeStats = codexDb.getDatabase()
|
|
1468
|
+
.prepare("SELECT COUNT(*) as count FROM mistakes")
|
|
1469
|
+
.get();
|
|
1470
|
+
// Register in global index
|
|
1471
|
+
globalIndex.registerProject({
|
|
1472
|
+
project_path: codex_path,
|
|
1473
|
+
source_type: "codex",
|
|
1474
|
+
db_path: codexDbPath,
|
|
1475
|
+
message_count: messageStats.count,
|
|
1476
|
+
conversation_count: stats.count,
|
|
1477
|
+
decision_count: decisionStats.count,
|
|
1478
|
+
mistake_count: mistakeStats.count,
|
|
1479
|
+
metadata: {
|
|
1480
|
+
indexed_folders: parseResult.indexed_folders || [],
|
|
1481
|
+
},
|
|
1482
|
+
});
|
|
1483
|
+
projects.push({
|
|
1484
|
+
project_path: codex_path,
|
|
1485
|
+
source_type: "codex",
|
|
1486
|
+
message_count: messageStats.count,
|
|
1487
|
+
conversation_count: stats.count,
|
|
1488
|
+
});
|
|
1489
|
+
totalMessages += messageStats.count;
|
|
1490
|
+
totalConversations += stats.count;
|
|
1491
|
+
totalDecisions += decisionStats.count;
|
|
1492
|
+
totalMistakes += mistakeStats.count;
|
|
1493
|
+
// Close the Codex database
|
|
1494
|
+
codexDb.close();
|
|
1495
|
+
}
|
|
1496
|
+
catch (error) {
|
|
1497
|
+
errors.push({
|
|
1498
|
+
project_path: codex_path,
|
|
1499
|
+
error: error.message,
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
// Index Claude Code projects if requested
|
|
1504
|
+
if (include_claude_code && existsSync(claude_projects_path)) {
|
|
1505
|
+
try {
|
|
1506
|
+
const projectFolders = readdirSync(claude_projects_path);
|
|
1507
|
+
for (const folder of projectFolders) {
|
|
1508
|
+
const folderPath = join(claude_projects_path, folder);
|
|
1509
|
+
try {
|
|
1510
|
+
// Try to determine the original project path from folder name
|
|
1511
|
+
const projectPath = folderPath; // Simplified for now
|
|
1512
|
+
// Index this project
|
|
1513
|
+
const indexResult = await this.indexConversations({
|
|
1514
|
+
project_path: projectPath,
|
|
1515
|
+
});
|
|
1516
|
+
if (indexResult.success) {
|
|
1517
|
+
// Register in global index
|
|
1518
|
+
globalIndex.registerProject({
|
|
1519
|
+
project_path: projectPath,
|
|
1520
|
+
source_type: "claude-code",
|
|
1521
|
+
db_path: this.db.getDbPath(),
|
|
1522
|
+
message_count: indexResult.stats.messages.count,
|
|
1523
|
+
conversation_count: indexResult.stats.conversations.count,
|
|
1524
|
+
decision_count: indexResult.stats.decisions.count,
|
|
1525
|
+
mistake_count: indexResult.stats.mistakes.count,
|
|
1526
|
+
});
|
|
1527
|
+
projects.push({
|
|
1528
|
+
project_path: projectPath,
|
|
1529
|
+
source_type: "claude-code",
|
|
1530
|
+
message_count: indexResult.stats.messages.count,
|
|
1531
|
+
conversation_count: indexResult.stats.conversations.count,
|
|
1532
|
+
});
|
|
1533
|
+
totalMessages += indexResult.stats.messages.count;
|
|
1534
|
+
totalConversations += indexResult.stats.conversations.count;
|
|
1535
|
+
totalDecisions += indexResult.stats.decisions.count;
|
|
1536
|
+
totalMistakes += indexResult.stats.mistakes.count;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
catch (error) {
|
|
1540
|
+
errors.push({
|
|
1541
|
+
project_path: folder,
|
|
1542
|
+
error: error.message,
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
catch (error) {
|
|
1548
|
+
errors.push({
|
|
1549
|
+
project_path: claude_projects_path,
|
|
1550
|
+
error: error.message,
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
const stats = globalIndex.getGlobalStats();
|
|
1555
|
+
return {
|
|
1556
|
+
success: true,
|
|
1557
|
+
global_index_path: globalIndex.getDbPath(),
|
|
1558
|
+
projects_indexed: projects.length,
|
|
1559
|
+
claude_code_projects: stats.claude_code_projects,
|
|
1560
|
+
codex_projects: stats.codex_projects,
|
|
1561
|
+
total_messages: totalMessages,
|
|
1562
|
+
total_conversations: totalConversations,
|
|
1563
|
+
total_decisions: totalDecisions,
|
|
1564
|
+
total_mistakes: totalMistakes,
|
|
1565
|
+
projects,
|
|
1566
|
+
errors,
|
|
1567
|
+
message: `Indexed ${projects.length} project(s): ${stats.claude_code_projects} Claude Code + ${stats.codex_projects} Codex`,
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
finally {
|
|
1571
|
+
// Ensure GlobalIndex is always closed
|
|
1572
|
+
globalIndex.close();
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Search across all indexed projects.
|
|
1577
|
+
*
|
|
1578
|
+
* @param args - Search arguments
|
|
1579
|
+
* @returns Search results from all projects
|
|
1580
|
+
*/
|
|
1581
|
+
async searchAllConversations(args) {
|
|
1582
|
+
const { GlobalIndex } = await import("../storage/GlobalIndex.js");
|
|
1583
|
+
const { SQLiteManager } = await import("../storage/SQLiteManager.js");
|
|
1584
|
+
const { SemanticSearch } = await import("../search/SemanticSearch.js");
|
|
1585
|
+
const typedArgs = args;
|
|
1586
|
+
const { query, limit = 20, date_range, source_type = "all" } = typedArgs;
|
|
1587
|
+
const globalIndex = new GlobalIndex();
|
|
1588
|
+
try {
|
|
1589
|
+
const projects = globalIndex.getAllProjects(source_type === "all" ? undefined : source_type);
|
|
1590
|
+
const allResults = [];
|
|
1591
|
+
let claudeCodeResults = 0;
|
|
1592
|
+
let codexResults = 0;
|
|
1593
|
+
for (const project of projects) {
|
|
1594
|
+
let projectDb = null;
|
|
1595
|
+
try {
|
|
1596
|
+
// Open this project's database
|
|
1597
|
+
projectDb = new SQLiteManager({ dbPath: project.db_path, readOnly: true });
|
|
1598
|
+
const semanticSearch = new SemanticSearch(projectDb);
|
|
1599
|
+
// Search this specific project's database
|
|
1600
|
+
const localResults = await semanticSearch.searchConversations(query, limit);
|
|
1601
|
+
// Filter by date range if specified
|
|
1602
|
+
const filteredResults = date_range
|
|
1603
|
+
? localResults.filter((r) => {
|
|
1604
|
+
const timestamp = r.message.timestamp;
|
|
1605
|
+
return timestamp >= date_range[0] && timestamp <= date_range[1];
|
|
1606
|
+
})
|
|
1607
|
+
: localResults;
|
|
1608
|
+
// Enrich results with project info
|
|
1609
|
+
for (const result of filteredResults) {
|
|
1610
|
+
allResults.push({
|
|
1611
|
+
conversation_id: result.conversation.id,
|
|
1612
|
+
message_id: result.message.id,
|
|
1613
|
+
timestamp: new Date(result.message.timestamp).toISOString(),
|
|
1614
|
+
similarity: result.similarity,
|
|
1615
|
+
snippet: result.snippet,
|
|
1616
|
+
git_branch: result.conversation.git_branch,
|
|
1617
|
+
message_type: result.message.message_type,
|
|
1618
|
+
role: result.message.role,
|
|
1619
|
+
project_path: project.project_path,
|
|
1620
|
+
source_type: project.source_type,
|
|
1621
|
+
});
|
|
1622
|
+
if (project.source_type === "claude-code") {
|
|
1623
|
+
claudeCodeResults++;
|
|
1624
|
+
}
|
|
1625
|
+
else {
|
|
1626
|
+
codexResults++;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
catch (_error) {
|
|
1631
|
+
// Skip projects that fail to search
|
|
1632
|
+
continue;
|
|
1633
|
+
}
|
|
1634
|
+
finally {
|
|
1635
|
+
// Close project database
|
|
1636
|
+
if (projectDb) {
|
|
1637
|
+
projectDb.close();
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
// Sort by similarity and limit
|
|
1642
|
+
const sortedResults = allResults.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
1643
|
+
return {
|
|
1644
|
+
query,
|
|
1645
|
+
results: sortedResults,
|
|
1646
|
+
total_found: sortedResults.length,
|
|
1647
|
+
projects_searched: projects.length,
|
|
1648
|
+
search_stats: {
|
|
1649
|
+
claude_code_results: claudeCodeResults,
|
|
1650
|
+
codex_results: codexResults,
|
|
1651
|
+
},
|
|
1652
|
+
message: `Found ${sortedResults.length} result(s) across ${projects.length} project(s)`,
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
finally {
|
|
1656
|
+
// Ensure GlobalIndex is always closed
|
|
1657
|
+
globalIndex.close();
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Get decisions from all indexed projects.
|
|
1662
|
+
*
|
|
1663
|
+
* @param args - Query arguments
|
|
1664
|
+
* @returns Decisions from all projects
|
|
1665
|
+
*/
|
|
1666
|
+
async getAllDecisions(args) {
|
|
1667
|
+
const { GlobalIndex } = await import("../storage/GlobalIndex.js");
|
|
1668
|
+
const typedArgs = args;
|
|
1669
|
+
const { query, file_path: _file_path } = typedArgs;
|
|
1670
|
+
const globalIndex = new GlobalIndex();
|
|
1671
|
+
try {
|
|
1672
|
+
const projects = globalIndex.getAllProjects();
|
|
1673
|
+
// TODO: Implement cross-project decision search
|
|
1674
|
+
// For now, return empty results with proper structure
|
|
1675
|
+
return {
|
|
1676
|
+
query,
|
|
1677
|
+
decisions: [],
|
|
1678
|
+
total_found: 0,
|
|
1679
|
+
projects_searched: projects.length,
|
|
1680
|
+
message: `Cross-project decision search not yet implemented (would search ${projects.length} project(s))`,
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
finally {
|
|
1684
|
+
globalIndex.close();
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
/**
|
|
1688
|
+
* Search mistakes across all indexed projects.
|
|
1689
|
+
*
|
|
1690
|
+
* @param args - Search arguments
|
|
1691
|
+
* @returns Mistakes from all projects
|
|
1692
|
+
*/
|
|
1693
|
+
async searchAllMistakes(args) {
|
|
1694
|
+
const { GlobalIndex } = await import("../storage/GlobalIndex.js");
|
|
1695
|
+
const typedArgs = args;
|
|
1696
|
+
const { query, mistake_type: _mistake_type } = typedArgs;
|
|
1697
|
+
const globalIndex = new GlobalIndex();
|
|
1698
|
+
try {
|
|
1699
|
+
const projects = globalIndex.getAllProjects();
|
|
1700
|
+
// TODO: Implement cross-project mistake search
|
|
1701
|
+
// For now, return empty results with proper structure
|
|
1702
|
+
return {
|
|
1703
|
+
query,
|
|
1704
|
+
mistakes: [],
|
|
1705
|
+
total_found: 0,
|
|
1706
|
+
projects_searched: projects.length,
|
|
1707
|
+
message: `Cross-project mistake search not yet implemented (would search ${projects.length} project(s))`,
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
finally {
|
|
1711
|
+
globalIndex.close();
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1332
1714
|
}
|
|
1333
1715
|
//# sourceMappingURL=ToolHandlers.js.map
|