snow-ai 0.2.17 → 0.2.18
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/dist/api/systemPrompt.d.ts +1 -1
- package/dist/api/systemPrompt.js +8 -0
- package/dist/mcp/filesystem.d.ts +2 -0
- package/dist/mcp/filesystem.js +7 -1
- package/dist/ui/components/DiffViewer.d.ts +3 -1
- package/dist/ui/components/DiffViewer.js +105 -76
- package/dist/ui/components/MCPInfoPanel.js +84 -49
- package/dist/ui/components/MCPInfoScreen.js +93 -5
- package/dist/ui/components/ToolConfirmation.js +1 -1
- package/dist/ui/pages/ChatScreen.js +1 -1
- package/dist/utils/mcpToolsManager.d.ts +5 -0
- package/dist/utils/mcpToolsManager.js +64 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* System prompt configuration for Snow AI CLI
|
|
3
3
|
*/
|
|
4
|
-
export declare const SYSTEM_PROMPT = "You are Snow AI CLI, an intelligent command-line assistant designed to help users with their tasks efficiently and systematically.\n\n## \uD83C\uDFAF Core Principles\n\n1. **Language Adaptation**: ALWAYS respond in the SAME language as the user's query\n - User asks in Chinese \u2192 Respond in Chinese\n - User asks in English \u2192 Respond in English\n - User asks in Japanese \u2192 Respond in Japanese\n - This applies to ALL responses, explanations, and error messages\n\n2. **Methodology First**: Follow systematic workflows, not ad-hoc solutions\n3. **Quality Assurance**: Always verify code changes by running build/test scripts\n4. **Incremental Progress**: Break complex tasks into manageable steps with TODO tracking\n\n## \uD83D\uDCDA Project Context\n\n**SNOW.md Documentation:**\n- Check if SNOW.md exists in the project root before making changes\n- SNOW.md contains: project overview, architecture, tech stack, development guidelines\n- ALWAYS read SNOW.md first for complex tasks to understand project context\n- If SNOW.md doesn't exist, proceed without it (it's optional)\n\n## \uD83D\uDD04 Standard Workflow\n\n### For Simple Tasks (1-2 steps):\n1. Understand the request\n2. Execute directly using appropriate tools\n3. Verify the result\n\n### For Complex Tasks (3+ steps):\n1. **Plan**: Create a TODO list with clear, actionable tasks\n2. **Read Context**: Check SNOW.md and relevant files\n3. **Execute**: Work through tasks systematically\n4. **Update**: Mark each task as completed IMMEDIATELY after finishing\n5. **Verify**: Run build/test scripts to catch errors\n6. **Report**: Summarize what was done\n\n## \u2705 TODO Management Best Practices\n\n**When to create TODO lists:**\n- Multi-file changes or refactoring\n- Feature implementation with multiple components\n- Bug fixes requiring investigation + changes + testing\n- Any task with 3+ distinct steps\n- Tasks requiring project documentation review\n\n**TODO Update Discipline:**\n- \u2705 Mark task as \"completed\" IMMEDIATELY after finishing it\n- \u2705 Update TODO status in real-time, not at the end\n- \u2705 Keep TODO list synchronized with actual progress\n- \u274C Don't wait until all tasks are done to update statuses\n- \u274C Don't skip TODO updates for \"small\" tasks\n\n**Status Model:**\n- **pending**: Not yet started or in progress\n- **completed**: 100% finished and verified\n\n## \uD83D\uDEE0\uFE0F Tool Selection Strategy\n\n**Filesystem Operations:**\n- Use `filesystem-read` before editing to see exact line numbers\n- Use `filesystem-edit` for precise, small changes (recommended \u226415 lines)\n- Use `filesystem-create` for new files\n- Use `filesystem-search` to find code patterns across files\n\n**Terminal Commands:**\n- Use for build scripts, testing, package management\n- Examples: `npm run build`, `npm test`, `git status`\n\n**Context7 Documentation:**\n- Use `context7-resolve-library-id` first to find library ID\n- Then use `context7-get-library-docs` to fetch documentation\n- Helpful for understanding third-party libraries\n\n## \uD83D\uDD0D Code Quality Assurance\n\n**CRITICAL: Always verify code changes!**\n\nAfter making code changes, you MUST:\n1. Run the project's build script: `npm run build` or `tsc`\n2. Check for TypeScript/compilation errors\n3. If errors occur, fix them immediately\n4. Never leave code in a broken state\n\n**Common verification commands:**\n- TypeScript projects: `npm run build` or `tsc`\n- JavaScript projects: `npm run lint` or `npm test`\n- Python projects: `python -m py_compile <file>`\n- Go projects: `go build`\n\n## \uD83C\uDFA8 Response Quality Guidelines\n\n1. **Be Concise**: Provide clear, actionable information without unnecessary verbosity\n2. **Use Formatting**: Use markdown, emojis, and structure for readability\n3. **Show Progress**: For complex tasks, show TODO progress and updates\n4. **Explain Decisions**: Briefly explain why you chose a particular approach\n5. **Handle Errors Gracefully**: If something fails, explain why and suggest alternatives\n\n## \uD83D\uDEA8 Error Prevention\n\n**Before executing:**\n- Read files completely before editing\n- Verify line numbers are correct\n- Check file paths exist\n\n**During execution:**\n- Make small, incremental changes\n- Test after each significant change\n- Keep backups in mind (user can use git)\n\n**After execution:**\n- Run build/compile scripts\n- Verify no syntax errors\n- Confirm the change works as intended\n\n## \uD83D\uDCA1 Examples of Good Workflow\n\n**Example 1: Adding a new feature**\n```\n1. Create TODO list with tasks\n2. Read SNOW.md to understand architecture\n3. Read relevant source files\n4. Implement changes incrementally\n5. Update TODO after each file\n6. Run npm run build to verify\n7. Report completion\n```\n\n**Example 2: Fixing a bug**\n```\n1. Search for the bug location\n2. Read surrounding code context\n3. Identify root cause\n4. Make minimal fix\n5. Run build/test scripts\n6. Verify fix works\n```\n\n**Example 3: Refactoring code**\n```\n1. Create TODO with affected files\n2. Read all files to understand dependencies\n3. Refactor one file at a time\n4. Update TODO after each file\n5. Run build after each change\n6. Ensure no breaking changes\n```\n\nRemember: Your goal is to be a reliable, systematic, and quality-focused assistant. Always prioritize correctness over speed, and maintain clear communication with the user in their preferred language.";
|
|
4
|
+
export declare const SYSTEM_PROMPT = "You are Snow AI CLI, an intelligent command-line assistant designed to help users with their tasks efficiently and systematically.\n\n## \uD83C\uDFAF Core Principles\n\n1. **Language Adaptation**: ALWAYS respond in the SAME language as the user's query\n - User asks in Chinese \u2192 Respond in Chinese\n - User asks in English \u2192 Respond in English\n - User asks in Japanese \u2192 Respond in Japanese\n - This applies to ALL responses, explanations, and error messages\n\n2. **Methodology First**: Follow systematic workflows, not ad-hoc solutions\n3. **Quality Assurance**: Always verify code changes by running build/test scripts\n4. **Incremental Progress**: Break complex tasks into manageable steps with TODO tracking\n\n## \uD83D\uDCDA Project Context\n\n**SNOW.md Documentation:**\n- Check if SNOW.md exists in the project root before making changes\n- SNOW.md contains: project overview, architecture, tech stack, development guidelines\n- ALWAYS read SNOW.md first for complex tasks to understand project context\n- If SNOW.md doesn't exist, proceed without it (it's optional)\n\n## \uD83D\uDD04 Standard Workflow\n\n### For Simple Tasks (1-2 steps):\n1. Understand the request\n2. Execute directly using appropriate tools\n3. Verify the result\n\n### For Complex Tasks (3+ steps):\n1. **Plan**: Create a TODO list with clear, actionable tasks\n2. **Read Context**: Check SNOW.md and relevant files\n3. **Execute**: Work through tasks systematically\n4. **Update**: Mark each task as completed IMMEDIATELY after finishing\n5. **Verify**: Run build/test scripts to catch errors\n6. **Report**: Summarize what was done\n\n## \u2705 TODO Management Best Practices\n\n**When to create TODO lists:**\n- Multi-file changes or refactoring\n- Feature implementation with multiple components\n- Bug fixes requiring investigation + changes + testing\n- Any task with 3+ distinct steps\n- Tasks requiring project documentation review\n\n**TODO Update Discipline:**\n- \u2705 Mark task as \"completed\" IMMEDIATELY after finishing it\n- \u2705 Update TODO status in real-time, not at the end\n- \u2705 Keep TODO list synchronized with actual progress\n- \u274C Don't wait until all tasks are done to update statuses\n- \u274C Don't skip TODO updates for \"small\" tasks\n\n**Status Model:**\n- **pending**: Not yet started or in progress\n- **completed**: 100% finished and verified\n\n## \uD83D\uDEE0\uFE0F Tool Selection Strategy\n\n**\u26A1 CRITICAL: Autonomous Tool Usage**\n- **ALWAYS decide and use tools autonomously** - DO NOT ask users for permission\n- **Make intelligent decisions** about which tools to use based on the task\n- **Execute immediately** when you have sufficient information\n- Users expect you to act, not to ask \"Should I...?\" or \"Do you want me to...?\"\n- Only ask for clarification when task requirements are genuinely ambiguous\n- When you have access to tools that can solve the task, USE THEM directly\n\n**Filesystem Operations:**\n- Use `filesystem-read` before editing to see exact line numbers\n- Use `filesystem-edit` for precise, small changes (recommended \u226415 lines)\n- Use `filesystem-create` for new files\n- Use `filesystem-search` to find code patterns across files\n\n**Terminal Commands:**\n- Use for build scripts, testing, package management\n- Examples: `npm run build`, `npm test`, `git status`\n\n**Context7 Documentation:**\n- Use `context7-resolve-library-id` first to find library ID\n- Then use `context7-get-library-docs` to fetch documentation\n- Helpful for understanding third-party libraries\n\n## \uD83D\uDD0D Code Quality Assurance\n\n**CRITICAL: Always verify code changes!**\n\nAfter making code changes, you MUST:\n1. Run the project's build script: `npm run build` or `tsc`\n2. Check for TypeScript/compilation errors\n3. If errors occur, fix them immediately\n4. Never leave code in a broken state\n\n**Common verification commands:**\n- TypeScript projects: `npm run build` or `tsc`\n- JavaScript projects: `npm run lint` or `npm test`\n- Python projects: `python -m py_compile <file>`\n- Go projects: `go build`\n\n## \uD83C\uDFA8 Response Quality Guidelines\n\n1. **Be Concise**: Provide clear, actionable information without unnecessary verbosity\n2. **Use Formatting**: Use markdown, emojis, and structure for readability\n3. **Show Progress**: For complex tasks, show TODO progress and updates\n4. **Explain Decisions**: Briefly explain why you chose a particular approach\n5. **Handle Errors Gracefully**: If something fails, explain why and suggest alternatives\n\n## \uD83D\uDEA8 Error Prevention\n\n**Before executing:**\n- Read files completely before editing\n- Verify line numbers are correct\n- Check file paths exist\n\n**During execution:**\n- Make small, incremental changes\n- Test after each significant change\n- Keep backups in mind (user can use git)\n\n**After execution:**\n- Run build/compile scripts\n- Verify no syntax errors\n- Confirm the change works as intended\n\n## \uD83D\uDCA1 Examples of Good Workflow\n\n**Example 1: Adding a new feature**\n```\n1. Create TODO list with tasks\n2. Read SNOW.md to understand architecture\n3. Read relevant source files\n4. Implement changes incrementally\n5. Update TODO after each file\n6. Run npm run build to verify\n7. Report completion\n```\n\n**Example 2: Fixing a bug**\n```\n1. Search for the bug location\n2. Read surrounding code context\n3. Identify root cause\n4. Make minimal fix\n5. Run build/test scripts\n6. Verify fix works\n```\n\n**Example 3: Refactoring code**\n```\n1. Create TODO with affected files\n2. Read all files to understand dependencies\n3. Refactor one file at a time\n4. Update TODO after each file\n5. Run build after each change\n6. Ensure no breaking changes\n```\n\nRemember: Your goal is to be a reliable, systematic, and quality-focused assistant. Always prioritize correctness over speed, and maintain clear communication with the user in their preferred language.";
|
package/dist/api/systemPrompt.js
CHANGED
|
@@ -60,6 +60,14 @@ export const SYSTEM_PROMPT = `You are Snow AI CLI, an intelligent command-line a
|
|
|
60
60
|
|
|
61
61
|
## 🛠️ Tool Selection Strategy
|
|
62
62
|
|
|
63
|
+
**⚡ CRITICAL: Autonomous Tool Usage**
|
|
64
|
+
- **ALWAYS decide and use tools autonomously** - DO NOT ask users for permission
|
|
65
|
+
- **Make intelligent decisions** about which tools to use based on the task
|
|
66
|
+
- **Execute immediately** when you have sufficient information
|
|
67
|
+
- Users expect you to act, not to ask "Should I...?" or "Do you want me to...?"
|
|
68
|
+
- Only ask for clarification when task requirements are genuinely ambiguous
|
|
69
|
+
- When you have access to tools that can solve the task, USE THEM directly
|
|
70
|
+
|
|
63
71
|
**Filesystem Operations:**
|
|
64
72
|
- Use \`filesystem-read\` before editing to see exact line numbers
|
|
65
73
|
- Use \`filesystem-edit\` for precise, small changes (recommended ≤15 lines)
|
package/dist/mcp/filesystem.d.ts
CHANGED
|
@@ -138,6 +138,8 @@ export declare class FilesystemMCPService {
|
|
|
138
138
|
linesModified: number;
|
|
139
139
|
structureAnalysis?: StructureAnalysis;
|
|
140
140
|
diagnostics?: Diagnostic[];
|
|
141
|
+
completeOldContent?: string;
|
|
142
|
+
completeNewContent?: string;
|
|
141
143
|
}>;
|
|
142
144
|
/**
|
|
143
145
|
* Search for code keywords in files within a directory
|
package/dist/mcp/filesystem.js
CHANGED
|
@@ -536,8 +536,11 @@ export class FilesystemMCPService {
|
|
|
536
536
|
catch (error) {
|
|
537
537
|
// Ignore diagnostics errors, they are optional
|
|
538
538
|
}
|
|
539
|
+
// Prepare complete file contents (without line numbers for diff comparison)
|
|
540
|
+
const completeOldContent = lines.join('\n');
|
|
541
|
+
const completeNewContent = finalLines.join('\n');
|
|
539
542
|
const result = {
|
|
540
|
-
message: `✅ File edited successfully: ${filePath}\n` +
|
|
543
|
+
message: `✅ File edited successfully,Please check the edit results and pay attention to code boundary issues to avoid syntax errors caused by missing closed parts: ${filePath}\n` +
|
|
541
544
|
` Replaced: lines ${startLine}-${adjustedEndLine} (${linesToModify} lines)\n` +
|
|
542
545
|
` Result: ${newContentLines.length} new lines` +
|
|
543
546
|
(smartBoundaries.extended
|
|
@@ -551,6 +554,9 @@ export class FilesystemMCPService {
|
|
|
551
554
|
totalLines: finalTotalLines,
|
|
552
555
|
linesModified: linesToModify,
|
|
553
556
|
structureAnalysis,
|
|
557
|
+
// Add complete file contents for intelligent diff display
|
|
558
|
+
completeOldContent,
|
|
559
|
+
completeNewContent,
|
|
554
560
|
};
|
|
555
561
|
// Add diagnostics if any were found
|
|
556
562
|
if (diagnostics.length > 0) {
|
|
@@ -3,6 +3,8 @@ interface Props {
|
|
|
3
3
|
oldContent?: string;
|
|
4
4
|
newContent: string;
|
|
5
5
|
filename?: string;
|
|
6
|
+
completeOldContent?: string;
|
|
7
|
+
completeNewContent?: string;
|
|
6
8
|
}
|
|
7
|
-
export default function DiffViewer({ oldContent, newContent, filename }: Props): React.JSX.Element;
|
|
9
|
+
export default function DiffViewer({ oldContent, newContent, filename, completeOldContent, completeNewContent, }: Props): React.JSX.Element;
|
|
8
10
|
export {};
|
|
@@ -1,120 +1,149 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import * as Diff from 'diff';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
const
|
|
18
|
-
const
|
|
4
|
+
// Helper function to strip line numbers from content (format: "123→content")
|
|
5
|
+
function stripLineNumbers(content) {
|
|
6
|
+
return content
|
|
7
|
+
.split('\n')
|
|
8
|
+
.map(line => {
|
|
9
|
+
// Match pattern: digits + → + content
|
|
10
|
+
const match = line.match(/^\s*\d+→(.*)$/);
|
|
11
|
+
return match ? match[1] : line;
|
|
12
|
+
})
|
|
13
|
+
.join('\n');
|
|
14
|
+
}
|
|
15
|
+
export default function DiffViewer({ oldContent = '', newContent, filename, completeOldContent, completeNewContent, }) {
|
|
16
|
+
// If complete file contents are provided, use them for intelligent diff
|
|
17
|
+
const useCompleteContent = completeOldContent && completeNewContent;
|
|
18
|
+
const diffOldContent = useCompleteContent
|
|
19
|
+
? completeOldContent
|
|
20
|
+
: stripLineNumbers(oldContent);
|
|
21
|
+
const diffNewContent = useCompleteContent
|
|
22
|
+
? completeNewContent
|
|
23
|
+
: stripLineNumbers(newContent);
|
|
19
24
|
// If no old content, show as new file creation
|
|
20
|
-
const isNewFile = !
|
|
25
|
+
const isNewFile = !diffOldContent || diffOldContent.trim() === '';
|
|
21
26
|
if (isNewFile) {
|
|
22
|
-
const allLines =
|
|
23
|
-
const totalLines = allLines.length;
|
|
24
|
-
const lineNumberWidth = String(totalLines).length;
|
|
27
|
+
const allLines = diffNewContent.split('\n');
|
|
25
28
|
return (React.createElement(Box, { flexDirection: "column" },
|
|
26
29
|
React.createElement(Box, { marginBottom: 1 },
|
|
27
30
|
React.createElement(Text, { bold: true, color: "green" }, "[New File]"),
|
|
28
31
|
filename && (React.createElement(Text, { color: "cyan" },
|
|
29
32
|
' ',
|
|
30
33
|
filename))),
|
|
31
|
-
React.createElement(Box, { flexDirection: "column" }, allLines.map((line, index) => (React.createElement(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
" \u2502"),
|
|
35
|
-
React.createElement(Text, { color: "white", backgroundColor: "#006400" },
|
|
36
|
-
"+ ",
|
|
37
|
-
line)))))));
|
|
34
|
+
React.createElement(Box, { flexDirection: "column" }, allLines.map((line, index) => (React.createElement(Text, { key: index, color: "white", backgroundColor: "#006400" },
|
|
35
|
+
"+ ",
|
|
36
|
+
line))))));
|
|
38
37
|
}
|
|
39
|
-
// Generate
|
|
40
|
-
const diffResult = Diff.diffLines(
|
|
41
|
-
|
|
42
|
-
const totalOldLines = cleanOldContent.split('\n').length;
|
|
43
|
-
const totalNewLines = cleanNewContent.split('\n').length;
|
|
44
|
-
const lineNumberWidth = Math.max(String(totalOldLines).length, String(totalNewLines).length, 2);
|
|
45
|
-
const displayLines = [];
|
|
38
|
+
// Generate line-by-line diff
|
|
39
|
+
const diffResult = Diff.diffLines(diffOldContent, diffNewContent);
|
|
40
|
+
const allChanges = [];
|
|
46
41
|
let oldLineNum = 1;
|
|
47
42
|
let newLineNum = 1;
|
|
48
43
|
diffResult.forEach((part) => {
|
|
49
44
|
const lines = part.value.replace(/\n$/, '').split('\n');
|
|
50
45
|
lines.forEach((line) => {
|
|
51
46
|
if (part.added) {
|
|
52
|
-
|
|
47
|
+
allChanges.push({
|
|
53
48
|
type: 'added',
|
|
54
49
|
content: line,
|
|
55
50
|
oldLineNum: null,
|
|
56
|
-
newLineNum: newLineNum
|
|
51
|
+
newLineNum: newLineNum++,
|
|
57
52
|
});
|
|
58
53
|
}
|
|
59
54
|
else if (part.removed) {
|
|
60
|
-
|
|
55
|
+
allChanges.push({
|
|
61
56
|
type: 'removed',
|
|
62
57
|
content: line,
|
|
63
58
|
oldLineNum: oldLineNum++,
|
|
64
|
-
newLineNum: null
|
|
59
|
+
newLineNum: null,
|
|
65
60
|
});
|
|
66
61
|
}
|
|
67
62
|
else {
|
|
68
|
-
|
|
63
|
+
allChanges.push({
|
|
69
64
|
type: 'unchanged',
|
|
70
65
|
content: line,
|
|
71
66
|
oldLineNum: oldLineNum++,
|
|
72
|
-
newLineNum: newLineNum
|
|
67
|
+
newLineNum: newLineNum++,
|
|
73
68
|
});
|
|
74
69
|
}
|
|
75
70
|
});
|
|
76
71
|
});
|
|
72
|
+
// Find diff hunks (groups of changes with context)
|
|
73
|
+
const hunks = [];
|
|
74
|
+
const contextLines = 3; // Number of context lines before and after changes
|
|
75
|
+
for (let i = 0; i < allChanges.length; i++) {
|
|
76
|
+
const change = allChanges[i];
|
|
77
|
+
if (change?.type !== 'unchanged') {
|
|
78
|
+
// Found a change, create a hunk
|
|
79
|
+
const hunkStart = Math.max(0, i - contextLines);
|
|
80
|
+
let hunkEnd = i;
|
|
81
|
+
// Extend the hunk to include all consecutive changes
|
|
82
|
+
while (hunkEnd < allChanges.length - 1) {
|
|
83
|
+
const nextChange = allChanges[hunkEnd + 1];
|
|
84
|
+
if (!nextChange)
|
|
85
|
+
break;
|
|
86
|
+
// If next line is a change, extend the hunk
|
|
87
|
+
if (nextChange.type !== 'unchanged') {
|
|
88
|
+
hunkEnd++;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// If there are more changes within context distance, extend the hunk
|
|
92
|
+
let hasMoreChanges = false;
|
|
93
|
+
for (let j = hunkEnd + 1; j < Math.min(allChanges.length, hunkEnd + 1 + contextLines * 2); j++) {
|
|
94
|
+
if (allChanges[j]?.type !== 'unchanged') {
|
|
95
|
+
hasMoreChanges = true;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (hasMoreChanges) {
|
|
100
|
+
hunkEnd++;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Add context lines after the hunk
|
|
107
|
+
hunkEnd = Math.min(allChanges.length - 1, hunkEnd + contextLines);
|
|
108
|
+
// Extract the hunk
|
|
109
|
+
const hunkChanges = allChanges.slice(hunkStart, hunkEnd + 1);
|
|
110
|
+
const firstChange = hunkChanges[0];
|
|
111
|
+
const lastChange = hunkChanges[hunkChanges.length - 1];
|
|
112
|
+
if (firstChange && lastChange) {
|
|
113
|
+
hunks.push({
|
|
114
|
+
startLine: firstChange.oldLineNum || firstChange.newLineNum || 1,
|
|
115
|
+
endLine: lastChange.oldLineNum || lastChange.newLineNum || 1,
|
|
116
|
+
changes: hunkChanges,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// Skip to the end of this hunk
|
|
120
|
+
i = hunkEnd;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
77
123
|
return (React.createElement(Box, { flexDirection: "column" },
|
|
78
124
|
React.createElement(Box, { marginBottom: 1 },
|
|
79
125
|
React.createElement(Text, { bold: true, color: "yellow" }, "[File Modified]"),
|
|
80
126
|
filename && (React.createElement(Text, { color: "cyan" },
|
|
81
127
|
' ',
|
|
82
128
|
filename))),
|
|
83
|
-
React.createElement(Box, { flexDirection: "column" },
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
React.createElement(Text, { color: "gray", dimColor: true }, oldNum),
|
|
104
|
-
React.createElement(Text, { color: "red", dimColor: true }, ' - '),
|
|
105
|
-
React.createElement(Text, { color: "gray", dimColor: true }, newNum),
|
|
106
|
-
React.createElement(Text, { dimColor: true }, " \u2502 ")),
|
|
107
|
-
React.createElement(Box, null,
|
|
108
|
-
React.createElement(Text, { color: "white", backgroundColor: "#8B0000", wrap: "truncate-end" }, ' ' + displayLine.content))));
|
|
109
|
-
}
|
|
110
|
-
// Unchanged lines
|
|
111
|
-
return (React.createElement(Box, { key: index, flexDirection: "row" },
|
|
112
|
-
React.createElement(Box, { flexShrink: 0 },
|
|
113
|
-
React.createElement(Text, { color: "gray", dimColor: true }, oldNum),
|
|
114
|
-
React.createElement(Text, { dimColor: true }, ' '),
|
|
115
|
-
React.createElement(Text, { color: "gray", dimColor: true }, newNum),
|
|
116
|
-
React.createElement(Text, { dimColor: true }, " \u2502 ")),
|
|
117
|
-
React.createElement(Box, null,
|
|
118
|
-
React.createElement(Text, { dimColor: true, wrap: "truncate-end" }, displayLine.content))));
|
|
119
|
-
}))));
|
|
129
|
+
React.createElement(Box, { flexDirection: "column" },
|
|
130
|
+
hunks.map((hunk, hunkIndex) => (React.createElement(Box, { key: hunkIndex, flexDirection: "column", marginBottom: 1 }, hunk.changes.map((change, changeIndex) => {
|
|
131
|
+
if (change.type === 'added') {
|
|
132
|
+
return (React.createElement(Text, { key: changeIndex, color: "white", backgroundColor: "#006400" },
|
|
133
|
+
"+ ",
|
|
134
|
+
change.content));
|
|
135
|
+
}
|
|
136
|
+
if (change.type === 'removed') {
|
|
137
|
+
return (React.createElement(Text, { key: changeIndex, color: "white", backgroundColor: "#8B0000" },
|
|
138
|
+
"- ",
|
|
139
|
+
change.content));
|
|
140
|
+
}
|
|
141
|
+
// Unchanged lines (context)
|
|
142
|
+
return (React.createElement(Text, { key: changeIndex, dimColor: true }, change.content));
|
|
143
|
+
})))),
|
|
144
|
+
hunks.length > 1 && (React.createElement(Box, { marginTop: 1 },
|
|
145
|
+
React.createElement(Text, { color: "gray", dimColor: true },
|
|
146
|
+
"Total: ",
|
|
147
|
+
hunks.length,
|
|
148
|
+
" change region(s)"))))));
|
|
120
149
|
}
|
|
@@ -1,73 +1,108 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
-
import
|
|
3
|
+
import SelectInput from 'ink-select-input';
|
|
4
|
+
import { getMCPServicesInfo, refreshMCPToolsCache, reconnectMCPService } from '../../utils/mcpToolsManager.js';
|
|
4
5
|
export default function MCPInfoPanel() {
|
|
5
6
|
const [mcpStatus, setMcpStatus] = useState([]);
|
|
6
7
|
const [isLoading, setIsLoading] = useState(true);
|
|
7
8
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
9
|
+
const [isReconnecting, setIsReconnecting] = useState(false);
|
|
10
|
+
const loadMCPStatus = async () => {
|
|
11
|
+
try {
|
|
12
|
+
const servicesInfo = await getMCPServicesInfo();
|
|
13
|
+
const statusList = servicesInfo.map(service => ({
|
|
14
|
+
name: service.serviceName,
|
|
15
|
+
connected: service.connected,
|
|
16
|
+
tools: service.tools.map(tool => tool.name),
|
|
17
|
+
connectionMethod: service.isBuiltIn ? 'Built-in' : 'External',
|
|
18
|
+
isBuiltIn: service.isBuiltIn,
|
|
19
|
+
error: service.error
|
|
20
|
+
}));
|
|
21
|
+
setMcpStatus(statusList);
|
|
22
|
+
setErrorMessage(null);
|
|
23
|
+
setIsLoading(false);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
setErrorMessage(error instanceof Error ? error.message : 'Failed to load MCP services');
|
|
27
|
+
setIsLoading(false);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
8
30
|
useEffect(() => {
|
|
9
31
|
let isMounted = true;
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
const servicesInfo = await getMCPServicesInfo();
|
|
13
|
-
if (isMounted) {
|
|
14
|
-
const statusList = servicesInfo.map(service => ({
|
|
15
|
-
name: service.serviceName,
|
|
16
|
-
connected: service.connected,
|
|
17
|
-
tools: service.tools.map(tool => tool.name),
|
|
18
|
-
connectionMethod: service.isBuiltIn ? 'Built-in' : 'External',
|
|
19
|
-
isBuiltIn: service.isBuiltIn,
|
|
20
|
-
error: service.error
|
|
21
|
-
}));
|
|
22
|
-
setMcpStatus(statusList);
|
|
23
|
-
setErrorMessage(null);
|
|
24
|
-
setIsLoading(false);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
if (isMounted) {
|
|
29
|
-
setErrorMessage(error instanceof Error ? error.message : 'Failed to load MCP services');
|
|
30
|
-
setIsLoading(false);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
32
|
+
const load = async () => {
|
|
33
|
+
await loadMCPStatus();
|
|
33
34
|
};
|
|
34
|
-
|
|
35
|
+
if (isMounted) {
|
|
36
|
+
load();
|
|
37
|
+
}
|
|
35
38
|
return () => {
|
|
36
39
|
isMounted = false;
|
|
37
40
|
};
|
|
38
41
|
}, []);
|
|
42
|
+
const handleServiceSelect = async (item) => {
|
|
43
|
+
setIsReconnecting(true);
|
|
44
|
+
try {
|
|
45
|
+
if (item.value === 'refresh-all') {
|
|
46
|
+
// Refresh all services
|
|
47
|
+
await refreshMCPToolsCache();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Reconnect specific service
|
|
51
|
+
await reconnectMCPService(item.value);
|
|
52
|
+
}
|
|
53
|
+
await loadMCPStatus();
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
setErrorMessage(error instanceof Error ? error.message : 'Failed to reconnect');
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
setIsReconnecting(false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
// Build select items from all services
|
|
63
|
+
const selectItems = [
|
|
64
|
+
{ label: 'Refresh all services', value: 'refresh-all', isRefreshAll: true },
|
|
65
|
+
...mcpStatus.map(s => ({
|
|
66
|
+
label: s.name,
|
|
67
|
+
value: s.name,
|
|
68
|
+
connected: s.connected,
|
|
69
|
+
isBuiltIn: s.isBuiltIn,
|
|
70
|
+
error: s.error
|
|
71
|
+
}))
|
|
72
|
+
];
|
|
73
|
+
// Custom item component to render with colors
|
|
74
|
+
const ItemComponent = ({ isSelected, label }) => {
|
|
75
|
+
const item = selectItems.find(i => i.label === label);
|
|
76
|
+
if (!item)
|
|
77
|
+
return React.createElement(Text, null, label);
|
|
78
|
+
if (item.isRefreshAll) {
|
|
79
|
+
return (React.createElement(Text, { color: isSelected ? 'cyan' : 'blue' },
|
|
80
|
+
"\u21BB ",
|
|
81
|
+
label));
|
|
82
|
+
}
|
|
83
|
+
const statusColor = item.connected ? 'green' : 'red';
|
|
84
|
+
const suffix = item.isBuiltIn ? ' (System)' : item.connected ? ' (External)' : ` - ${item.error || 'Failed'}`;
|
|
85
|
+
return (React.createElement(Box, null,
|
|
86
|
+
React.createElement(Text, { color: statusColor }, "\u25CF "),
|
|
87
|
+
React.createElement(Text, { color: isSelected ? 'cyan' : 'white' }, label),
|
|
88
|
+
React.createElement(Text, { color: "gray", dimColor: true }, suffix)));
|
|
89
|
+
};
|
|
39
90
|
if (isLoading) {
|
|
40
91
|
return (React.createElement(Text, { color: "gray" }, "Loading MCP services..."));
|
|
41
92
|
}
|
|
42
93
|
if (errorMessage) {
|
|
43
|
-
return (React.createElement(Box, { borderColor: "red", borderStyle: "round", paddingX: 2, paddingY:
|
|
94
|
+
return (React.createElement(Box, { borderColor: "red", borderStyle: "round", paddingX: 2, paddingY: 0 },
|
|
44
95
|
React.createElement(Text, { color: "red", dimColor: true },
|
|
45
|
-
"Error
|
|
96
|
+
"Error: ",
|
|
46
97
|
errorMessage)));
|
|
47
98
|
}
|
|
48
99
|
if (mcpStatus.length === 0) {
|
|
49
|
-
return (React.createElement(Box, { borderColor: "cyan", borderStyle: "round", paddingX: 2, paddingY:
|
|
50
|
-
React.createElement(Text, { color: "gray", dimColor: true }, "No available MCP
|
|
100
|
+
return (React.createElement(Box, { borderColor: "cyan", borderStyle: "round", paddingX: 2, paddingY: 0 },
|
|
101
|
+
React.createElement(Text, { color: "gray", dimColor: true }, "No available MCP services detected")));
|
|
51
102
|
}
|
|
52
|
-
return (React.createElement(Box, { borderColor: "cyan", borderStyle: "round", paddingX: 2, paddingY:
|
|
103
|
+
return (React.createElement(Box, { borderColor: "cyan", borderStyle: "round", paddingX: 2, paddingY: 0 },
|
|
53
104
|
React.createElement(Box, { flexDirection: "column" },
|
|
54
|
-
React.createElement(Text, { color: "cyan", bold: true },
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
React.createElement(Text, { color: status.connected ? "green" : "red" }, status.connected ? "●" : "●"),
|
|
58
|
-
React.createElement(Box, { marginLeft: 1 },
|
|
59
|
-
React.createElement(Text, { color: "white", bold: true }, status.name),
|
|
60
|
-
status.isBuiltIn && (React.createElement(Text, { color: "blue", dimColor: true }, "(System)")),
|
|
61
|
-
status.connected && status.connectionMethod && !status.isBuiltIn && (React.createElement(Text, { color: "gray", dimColor: true },
|
|
62
|
-
"(",
|
|
63
|
-
status.connectionMethod,
|
|
64
|
-
")")))),
|
|
65
|
-
status.connected && status.tools.length > 0 && (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
|
|
66
|
-
React.createElement(Text, { color: "gray", dimColor: true },
|
|
67
|
-
"Tools: ",
|
|
68
|
-
status.tools.join(', ')))),
|
|
69
|
-
!status.connected && status.error && (React.createElement(Box, { marginLeft: 2 },
|
|
70
|
-
React.createElement(Text, { color: "red", dimColor: true },
|
|
71
|
-
"Error: ",
|
|
72
|
-
status.error)))))))));
|
|
105
|
+
React.createElement(Text, { color: "cyan", bold: true }, isReconnecting ? 'Refreshing services...' : 'MCP Services'),
|
|
106
|
+
!isReconnecting && (React.createElement(SelectInput, { items: selectItems, onSelect: handleServiceSelect, itemComponent: ItemComponent })),
|
|
107
|
+
isReconnecting && (React.createElement(Text, { color: "yellow", dimColor: true }, "Please wait...")))));
|
|
73
108
|
}
|
|
@@ -1,7 +1,32 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
|
-
import
|
|
3
|
+
import SelectInput from 'ink-select-input';
|
|
4
|
+
import { getMCPServicesInfo, refreshMCPToolsCache, reconnectMCPService } from '../../utils/mcpToolsManager.js';
|
|
4
5
|
export default function MCPInfoScreen({ onClose, panelKey }) {
|
|
6
|
+
const [mcpStatus, setMcpStatus] = useState([]);
|
|
7
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
8
|
+
const [errorMessage, setErrorMessage] = useState(null);
|
|
9
|
+
const [isReconnecting, setIsReconnecting] = useState(false);
|
|
10
|
+
const loadMCPStatus = async () => {
|
|
11
|
+
try {
|
|
12
|
+
const servicesInfo = await getMCPServicesInfo();
|
|
13
|
+
const statusList = servicesInfo.map(service => ({
|
|
14
|
+
name: service.serviceName,
|
|
15
|
+
connected: service.connected,
|
|
16
|
+
tools: service.tools.map(tool => tool.name),
|
|
17
|
+
connectionMethod: service.isBuiltIn ? 'Built-in' : 'External',
|
|
18
|
+
isBuiltIn: service.isBuiltIn,
|
|
19
|
+
error: service.error
|
|
20
|
+
}));
|
|
21
|
+
setMcpStatus(statusList);
|
|
22
|
+
setErrorMessage(null);
|
|
23
|
+
setIsLoading(false);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
setErrorMessage(error instanceof Error ? error.message : 'Failed to load MCP services');
|
|
27
|
+
setIsLoading(false);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
5
30
|
useEffect(() => {
|
|
6
31
|
process.stdout.write('\x1B[?1049h');
|
|
7
32
|
process.stdout.write('\x1B[2J');
|
|
@@ -11,17 +36,80 @@ export default function MCPInfoScreen({ onClose, panelKey }) {
|
|
|
11
36
|
process.stdout.write('\x1B[?1049l');
|
|
12
37
|
};
|
|
13
38
|
}, []);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
loadMCPStatus();
|
|
41
|
+
}, [panelKey]);
|
|
14
42
|
useInput((_, key) => {
|
|
15
43
|
if (key.escape) {
|
|
16
44
|
onClose();
|
|
17
45
|
}
|
|
18
46
|
});
|
|
47
|
+
const handleServiceSelect = async (item) => {
|
|
48
|
+
setIsReconnecting(true);
|
|
49
|
+
try {
|
|
50
|
+
if (item.value === 'refresh-all') {
|
|
51
|
+
// Refresh all services
|
|
52
|
+
await refreshMCPToolsCache();
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Reconnect specific service
|
|
56
|
+
await reconnectMCPService(item.value);
|
|
57
|
+
}
|
|
58
|
+
await loadMCPStatus();
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
setErrorMessage(error instanceof Error ? error.message : 'Failed to reconnect');
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
setIsReconnecting(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
// Build select items from all services
|
|
68
|
+
const selectItems = [
|
|
69
|
+
{ label: 'Refresh all services', value: 'refresh-all', isRefreshAll: true },
|
|
70
|
+
...mcpStatus.map(s => ({
|
|
71
|
+
label: s.name,
|
|
72
|
+
value: s.name,
|
|
73
|
+
connected: s.connected,
|
|
74
|
+
isBuiltIn: s.isBuiltIn,
|
|
75
|
+
error: s.error
|
|
76
|
+
}))
|
|
77
|
+
];
|
|
78
|
+
// Custom item component to render with colors
|
|
79
|
+
const ItemComponent = ({ isSelected, label }) => {
|
|
80
|
+
const item = selectItems.find(i => i.label === label);
|
|
81
|
+
if (!item)
|
|
82
|
+
return React.createElement(Text, null, label);
|
|
83
|
+
if (item.isRefreshAll) {
|
|
84
|
+
return (React.createElement(Text, { color: isSelected ? 'cyan' : 'blue' },
|
|
85
|
+
"\u21BB ",
|
|
86
|
+
label));
|
|
87
|
+
}
|
|
88
|
+
const statusColor = item.connected ? 'green' : 'red';
|
|
89
|
+
const suffix = item.isBuiltIn ? ' (System)' : item.connected ? ' (External)' : ` - ${item.error || 'Failed'}`;
|
|
90
|
+
return (React.createElement(Box, null,
|
|
91
|
+
React.createElement(Text, { color: statusColor }, "\u25CF "),
|
|
92
|
+
React.createElement(Text, { color: isSelected ? 'cyan' : 'white' }, label),
|
|
93
|
+
React.createElement(Text, { color: "gray", dimColor: true }, suffix)));
|
|
94
|
+
};
|
|
95
|
+
if (isLoading) {
|
|
96
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
97
|
+
React.createElement(Text, { color: "gray" }, "Loading MCP services...")));
|
|
98
|
+
}
|
|
19
99
|
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
20
|
-
React.createElement(Box, {
|
|
100
|
+
React.createElement(Box, { borderStyle: "double", paddingX: 2, paddingY: 0, borderColor: "cyan" },
|
|
21
101
|
React.createElement(Box, { flexDirection: "column" },
|
|
22
102
|
React.createElement(Text, { color: "white", bold: true },
|
|
23
103
|
React.createElement(Text, { color: "cyan" }, "\u2746 "),
|
|
24
104
|
"MCP Services Overview"),
|
|
25
|
-
React.createElement(Text, { color: "gray", dimColor: true }, "Press ESC to return to
|
|
26
|
-
React.createElement(
|
|
105
|
+
React.createElement(Text, { color: "gray", dimColor: true }, "Press ESC to return | Use \u2191\u2193 and Enter to refresh"))),
|
|
106
|
+
errorMessage && (React.createElement(Box, { borderColor: "red", borderStyle: "round", paddingX: 2, paddingY: 0, marginTop: 1 },
|
|
107
|
+
React.createElement(Text, { color: "red", dimColor: true },
|
|
108
|
+
"Error: ",
|
|
109
|
+
errorMessage))),
|
|
110
|
+
React.createElement(Box, { borderColor: "cyan", borderStyle: "round", paddingX: 2, paddingY: 0, marginTop: 1 },
|
|
111
|
+
React.createElement(Box, { flexDirection: "column" },
|
|
112
|
+
React.createElement(Text, { color: "cyan", bold: true }, isReconnecting ? 'Refreshing services...' : 'MCP Services'),
|
|
113
|
+
!isReconnecting && (React.createElement(SelectInput, { items: selectItems, onSelect: handleServiceSelect, itemComponent: ItemComponent })),
|
|
114
|
+
isReconnecting && (React.createElement(Text, { color: "yellow", dimColor: true }, "Please wait..."))))));
|
|
27
115
|
}
|
|
@@ -83,7 +83,7 @@ export default function ToolConfirmation({ toolName, toolArguments, allTools, on
|
|
|
83
83
|
onConfirm(item.value);
|
|
84
84
|
}
|
|
85
85
|
};
|
|
86
|
-
return (React.createElement(Box, { flexDirection: "column", marginX: 1, marginY: 1, paddingX: 1 },
|
|
86
|
+
return (React.createElement(Box, { flexDirection: "column", marginX: 1, marginY: 1, borderStyle: 'round', borderColor: 'yellow', paddingX: 1 },
|
|
87
87
|
React.createElement(Box, { marginBottom: 1 },
|
|
88
88
|
React.createElement(Text, { bold: true, color: "yellow" }, "[Tool Confirmation]")),
|
|
89
89
|
!formattedAllTools && (React.createElement(React.Fragment, null,
|
|
@@ -493,7 +493,7 @@ export default function ChatScreen({}) {
|
|
|
493
493
|
message.toolCall.name === 'filesystem-edit' &&
|
|
494
494
|
message.toolCall.arguments.oldContent &&
|
|
495
495
|
message.toolCall.arguments.newContent && (React.createElement(Box, { marginTop: 1 },
|
|
496
|
-
React.createElement(DiffViewer, { oldContent: message.toolCall.arguments.oldContent, newContent: message.toolCall.arguments.newContent, filename: message.toolCall.arguments.filename }))),
|
|
496
|
+
React.createElement(DiffViewer, { oldContent: message.toolCall.arguments.oldContent, newContent: message.toolCall.arguments.newContent, filename: message.toolCall.arguments.filename, completeOldContent: message.toolCall.arguments.completeOldContent, completeNewContent: message.toolCall.arguments.completeNewContent }))),
|
|
497
497
|
message.toolCall &&
|
|
498
498
|
message.toolCall.name === 'terminal-execute' &&
|
|
499
499
|
message.toolCall.arguments.command && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
@@ -26,6 +26,11 @@ export declare function getTodoService(): TodoService;
|
|
|
26
26
|
* Manually refresh the tools cache (for configuration changes)
|
|
27
27
|
*/
|
|
28
28
|
export declare function refreshMCPToolsCache(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Reconnect a specific MCP service and update cache
|
|
31
|
+
* @param serviceName - Name of the service to reconnect
|
|
32
|
+
*/
|
|
33
|
+
export declare function reconnectMCPService(serviceName: string): Promise<void>;
|
|
29
34
|
/**
|
|
30
35
|
* Clear the tools cache (useful for testing or forcing refresh)
|
|
31
36
|
*/
|
|
@@ -182,6 +182,70 @@ export async function refreshMCPToolsCache() {
|
|
|
182
182
|
toolsCache = null;
|
|
183
183
|
await refreshToolsCache();
|
|
184
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Reconnect a specific MCP service and update cache
|
|
187
|
+
* @param serviceName - Name of the service to reconnect
|
|
188
|
+
*/
|
|
189
|
+
export async function reconnectMCPService(serviceName) {
|
|
190
|
+
if (!toolsCache) {
|
|
191
|
+
// If no cache, do full refresh
|
|
192
|
+
await refreshToolsCache();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
// Handle built-in services (they don't need reconnection)
|
|
196
|
+
if (serviceName === 'filesystem' || serviceName === 'terminal' || serviceName === 'todo') {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
// Get the server config
|
|
200
|
+
const mcpConfig = getMCPConfig();
|
|
201
|
+
const server = mcpConfig.mcpServers[serviceName];
|
|
202
|
+
if (!server) {
|
|
203
|
+
throw new Error(`Service ${serviceName} not found in configuration`);
|
|
204
|
+
}
|
|
205
|
+
// Find and update the service in cache
|
|
206
|
+
const serviceIndex = toolsCache.servicesInfo.findIndex(s => s.serviceName === serviceName);
|
|
207
|
+
if (serviceIndex === -1) {
|
|
208
|
+
// Service not in cache, do full refresh
|
|
209
|
+
await refreshToolsCache();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
// Try to reconnect to the service
|
|
214
|
+
const serviceTools = await probeServiceTools(serviceName, server);
|
|
215
|
+
// Update service info in cache
|
|
216
|
+
toolsCache.servicesInfo[serviceIndex] = {
|
|
217
|
+
serviceName,
|
|
218
|
+
tools: serviceTools,
|
|
219
|
+
isBuiltIn: false,
|
|
220
|
+
connected: true,
|
|
221
|
+
};
|
|
222
|
+
// Remove old tools for this service from the tools list
|
|
223
|
+
toolsCache.tools = toolsCache.tools.filter(tool => !tool.function.name.startsWith(`${serviceName}-`));
|
|
224
|
+
// Add new tools for this service
|
|
225
|
+
for (const tool of serviceTools) {
|
|
226
|
+
toolsCache.tools.push({
|
|
227
|
+
type: 'function',
|
|
228
|
+
function: {
|
|
229
|
+
name: `${serviceName}-${tool.name}`,
|
|
230
|
+
description: tool.description,
|
|
231
|
+
parameters: tool.inputSchema,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
// Update service as failed
|
|
238
|
+
toolsCache.servicesInfo[serviceIndex] = {
|
|
239
|
+
serviceName,
|
|
240
|
+
tools: [],
|
|
241
|
+
isBuiltIn: false,
|
|
242
|
+
connected: false,
|
|
243
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
244
|
+
};
|
|
245
|
+
// Remove tools for this service from the tools list
|
|
246
|
+
toolsCache.tools = toolsCache.tools.filter(tool => !tool.function.name.startsWith(`${serviceName}-`));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
185
249
|
/**
|
|
186
250
|
* Clear the tools cache (useful for testing or forcing refresh)
|
|
187
251
|
*/
|