@wonderwhy-er/desktop-commander 0.1.30 → 0.1.32
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 +31 -6
- package/dist/handlers/command-handlers.d.ts +27 -0
- package/dist/handlers/command-handlers.js +31 -0
- package/dist/handlers/edit-search-handlers.d.ts +18 -0
- package/dist/handlers/edit-search-handlers.js +72 -0
- package/dist/handlers/filesystem-handlers.d.ts +90 -0
- package/dist/handlers/filesystem-handlers.js +179 -0
- package/dist/handlers/index.d.ts +5 -0
- package/dist/handlers/index.js +6 -0
- package/dist/handlers/process-handlers.d.ts +18 -0
- package/dist/handlers/process-handlers.js +15 -0
- package/dist/handlers/terminal-handlers.d.ts +36 -0
- package/dist/handlers/terminal-handlers.js +29 -0
- package/dist/server.js +54 -259
- package/dist/setup-claude-server.js +1 -1
- package/dist/tools/edit.js +6 -3
- package/dist/tools/filesystem.d.ts +44 -2
- package/dist/tools/filesystem.js +215 -56
- package/dist/tools/mime-types.js +2 -8
- package/dist/tools/schemas.d.ts +9 -0
- package/dist/tools/schemas.js +3 -0
- package/dist/tools/search.js +9 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +39 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
[](https://discord.gg/kQ27sNnZr7)
|
|
9
9
|
|
|
10
|
-
Short version.
|
|
10
|
+
Short version. Four key things. Terminal commands, diff based file editing, ripgrep based text search in folders, ability to read files from urls
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|

|
|
@@ -63,7 +63,14 @@ npx @wonderwhy-er/desktop-commander@latest setup --debug
|
|
|
63
63
|
```
|
|
64
64
|
Restart Claude if running
|
|
65
65
|
|
|
66
|
-
### Option 2:
|
|
66
|
+
### Option 2: Using bash script installer (macOS)
|
|
67
|
+
For macOS users, you can use our automated bash installer which will check your Node.js version, install it if needed, and automatically configure Desktop Commander:
|
|
68
|
+
```
|
|
69
|
+
curl -fsSL https://raw.githubusercontent.com/wonderwhy-er/DesktopCommanderMCP/refs/heads/main/install.sh | bash
|
|
70
|
+
```
|
|
71
|
+
This script handles all dependencies and configuration automatically for a seamless setup experience.
|
|
72
|
+
|
|
73
|
+
### Option 3: Installing via Smithery
|
|
67
74
|
|
|
68
75
|
To install Desktop Commander for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@wonderwhy-er/desktop-commander):
|
|
69
76
|
|
|
@@ -71,7 +78,7 @@ To install Desktop Commander for Claude Desktop automatically via [Smithery](htt
|
|
|
71
78
|
npx -y @smithery/cli install @wonderwhy-er/desktop-commander --client claude
|
|
72
79
|
```
|
|
73
80
|
|
|
74
|
-
### Option
|
|
81
|
+
### Option 4: Add to claude_desktop_config by hand
|
|
75
82
|
Add this entry to your claude_desktop_config.json:
|
|
76
83
|
|
|
77
84
|
- On Mac: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
|
|
@@ -93,7 +100,7 @@ Add this entry to your claude_desktop_config.json:
|
|
|
93
100
|
```
|
|
94
101
|
Restart Claude if running
|
|
95
102
|
|
|
96
|
-
### Option
|
|
103
|
+
### Option 5: Checkout locally
|
|
97
104
|
1. Clone and build:
|
|
98
105
|
```bash
|
|
99
106
|
git clone https://github.com/wonderwhy-er/ClaudeComputerCommander.git
|
|
@@ -108,6 +115,12 @@ The setup command will:
|
|
|
108
115
|
- Configure Claude's desktop app
|
|
109
116
|
- Add MCP servers to Claude's config if needed
|
|
110
117
|
|
|
118
|
+
### Updating Desktop Commander
|
|
119
|
+
|
|
120
|
+
When installed through npx (Option 1) or Smithery (Option 2), Desktop Commander will automatically update to the latest version whenever you restart Claude. No manual update process is needed.
|
|
121
|
+
|
|
122
|
+
For manual installations, you can update by running the setup command again.
|
|
123
|
+
|
|
111
124
|
## Usage
|
|
112
125
|
|
|
113
126
|
The server provides these tool categories:
|
|
@@ -122,7 +135,7 @@ The server provides these tool categories:
|
|
|
122
135
|
- `block_command`/`unblock_command`: Manage command blacklist
|
|
123
136
|
|
|
124
137
|
### Filesystem Tools
|
|
125
|
-
- `read_file`/`write_file`:
|
|
138
|
+
- `read_file`/`write_file`: Read files from local filesystem or URLs (supports viewing PNG, JPEG, GIF, and WebP images directly in Claude)
|
|
126
139
|
- `create_directory`/`list_directory`: Directory management
|
|
127
140
|
- `move_file`: Move/rename files
|
|
128
141
|
- `search_files`: Pattern-based file search
|
|
@@ -153,6 +166,14 @@ console.log("new message");
|
|
|
153
166
|
>>>>>>> REPLACE
|
|
154
167
|
```
|
|
155
168
|
|
|
169
|
+
### URL Support
|
|
170
|
+
- `read_file` can now fetch content from both local files and URLs
|
|
171
|
+
- Example: `read_file` with `isUrl: true` parameter to read from web resources
|
|
172
|
+
- Handles both text and image content from remote sources
|
|
173
|
+
- Images (local or from URLs) are displayed visually in Claude's interface, not as text
|
|
174
|
+
- Claude can see and analyze the actual image content
|
|
175
|
+
- Default 30-second timeout for URL requests
|
|
176
|
+
|
|
156
177
|
## Handling Long-Running Commands
|
|
157
178
|
|
|
158
179
|
For commands that may take a while:
|
|
@@ -208,6 +229,7 @@ This project extends the MCP Filesystem Server to enable:
|
|
|
208
229
|
Created as part of exploring Claude MCPs: https://youtube.com/live/TlbjFDbl5Us
|
|
209
230
|
|
|
210
231
|
## DONE
|
|
232
|
+
- **07-04-2025 Added URL support** - `read_file` command can now fetch content from URLs
|
|
211
233
|
- **28-03-2025 Fixed "Watching /" JSON error** - Implemented custom stdio transport to handle non-JSON messages and prevent server crashes
|
|
212
234
|
- **25-03-2025 Better code search** ([merged](https://github.com/wonderwhy-er/ClaudeDesktopCommander/pull/17)) - Enhanced code exploration with context-aware results
|
|
213
235
|
|
|
@@ -252,7 +274,7 @@ Join our [Discord server](https://discord.gg/kQ27sNnZr7) to get help, share feed
|
|
|
252
274
|
https://www.youtube.com/watch?v=ly3bed99Dy8&lc=UgztdHvDMqTb9jiqnf54AaABAg](https://www.youtube.com/watch?v=ly3bed99Dy8&lc=UgztdHvDMqTb9jiqnf54AaABAg
|
|
253
275
|
)
|
|
254
276
|
|
|
255
|
-
[
|
|
257
279
|
https://www.youtube.com/watch?v=ly3bed99Dy8&lc=UgyQFTmYLJ4VBwIlmql4AaABAg](https://www.youtube.com/watch?v=ly3bed99Dy8&lc=UgyQFTmYLJ4VBwIlmql4AaABAg)
|
|
258
280
|
|
|
@@ -293,6 +315,9 @@ Unlike IDE-focused tools, Claude Desktop Commander provides a solution-centric a
|
|
|
293
315
|
### Do I need to pay for API credits?
|
|
294
316
|
No. This tool works with Claude Desktop's standard Pro subscription ($20/month), not with API calls, so you won't incur additional costs beyond the subscription fee.
|
|
295
317
|
|
|
318
|
+
### Does Desktop Commander automatically update?
|
|
319
|
+
Yes, when installed through npx or Smithery, Desktop Commander automatically updates to the latest version when you restart Claude. No manual update process is needed.
|
|
320
|
+
|
|
296
321
|
### What are the most common use cases?
|
|
297
322
|
- Exploring and understanding complex codebases
|
|
298
323
|
- Generating diagrams and documentation
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle block_command command
|
|
3
|
+
*/
|
|
4
|
+
export declare function handleBlockCommand(args: unknown): Promise<{
|
|
5
|
+
content: {
|
|
6
|
+
type: string;
|
|
7
|
+
text: boolean;
|
|
8
|
+
}[];
|
|
9
|
+
}>;
|
|
10
|
+
/**
|
|
11
|
+
* Handle unblock_command command
|
|
12
|
+
*/
|
|
13
|
+
export declare function handleUnblockCommand(args: unknown): Promise<{
|
|
14
|
+
content: {
|
|
15
|
+
type: string;
|
|
16
|
+
text: boolean;
|
|
17
|
+
}[];
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Handle list_blocked_commands command
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleListBlockedCommands(): {
|
|
23
|
+
content: {
|
|
24
|
+
type: string;
|
|
25
|
+
text: string;
|
|
26
|
+
}[];
|
|
27
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { commandManager } from '../command-manager.js';
|
|
2
|
+
import { BlockCommandArgsSchema, UnblockCommandArgsSchema } from '../tools/schemas.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handle block_command command
|
|
5
|
+
*/
|
|
6
|
+
export async function handleBlockCommand(args) {
|
|
7
|
+
const parsed = BlockCommandArgsSchema.parse(args);
|
|
8
|
+
const blockResult = await commandManager.blockCommand(parsed.command);
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: "text", text: blockResult }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Handle unblock_command command
|
|
15
|
+
*/
|
|
16
|
+
export async function handleUnblockCommand(args) {
|
|
17
|
+
const parsed = UnblockCommandArgsSchema.parse(args);
|
|
18
|
+
const unblockResult = await commandManager.unblockCommand(parsed.command);
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text: unblockResult }],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Handle list_blocked_commands command
|
|
25
|
+
*/
|
|
26
|
+
export function handleListBlockedCommands() {
|
|
27
|
+
const blockedCommands = commandManager.listBlockedCommands();
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: "text", text: blockedCommands.join('\n') }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle edit_block command
|
|
3
|
+
*/
|
|
4
|
+
export declare function handleEditBlock(args: unknown): Promise<{
|
|
5
|
+
content: {
|
|
6
|
+
type: string;
|
|
7
|
+
text: string;
|
|
8
|
+
}[];
|
|
9
|
+
}>;
|
|
10
|
+
/**
|
|
11
|
+
* Handle search_code command
|
|
12
|
+
*/
|
|
13
|
+
export declare function handleSearchCode(args: unknown): Promise<{
|
|
14
|
+
content: {
|
|
15
|
+
type: string;
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { parseEditBlock, performSearchReplace } from '../tools/edit.js';
|
|
2
|
+
import { searchTextInFiles } from '../tools/search.js';
|
|
3
|
+
import { EditBlockArgsSchema, SearchCodeArgsSchema } from '../tools/schemas.js';
|
|
4
|
+
import { withTimeout } from '../utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Handle edit_block command
|
|
7
|
+
*/
|
|
8
|
+
export async function handleEditBlock(args) {
|
|
9
|
+
const parsed = EditBlockArgsSchema.parse(args);
|
|
10
|
+
const { filePath, searchReplace } = await parseEditBlock(parsed.blockContent);
|
|
11
|
+
await performSearchReplace(filePath, searchReplace);
|
|
12
|
+
return {
|
|
13
|
+
content: [{ type: "text", text: `Successfully applied edit to ${filePath}` }],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Handle search_code command
|
|
18
|
+
*/
|
|
19
|
+
export async function handleSearchCode(args) {
|
|
20
|
+
const parsed = SearchCodeArgsSchema.parse(args);
|
|
21
|
+
const timeoutMs = parsed.timeoutMs || 30000; // 30 seconds default
|
|
22
|
+
// Apply timeout at the handler level
|
|
23
|
+
const searchOperation = async () => {
|
|
24
|
+
return await searchTextInFiles({
|
|
25
|
+
rootPath: parsed.path,
|
|
26
|
+
pattern: parsed.pattern,
|
|
27
|
+
filePattern: parsed.filePattern,
|
|
28
|
+
ignoreCase: parsed.ignoreCase,
|
|
29
|
+
maxResults: parsed.maxResults,
|
|
30
|
+
includeHidden: parsed.includeHidden,
|
|
31
|
+
contextLines: parsed.contextLines,
|
|
32
|
+
// Don't pass timeoutMs down to the implementation
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
// Use withTimeout at the handler level
|
|
36
|
+
const results = await withTimeout(searchOperation(), timeoutMs, 'Code search operation', [] // Empty array as default on timeout
|
|
37
|
+
);
|
|
38
|
+
// If timeout occurred, try to terminate the ripgrep process
|
|
39
|
+
if (results.length === 0 && globalThis.currentSearchProcess) {
|
|
40
|
+
try {
|
|
41
|
+
console.log(`Terminating timed out search process (PID: ${globalThis.currentSearchProcess.pid})`);
|
|
42
|
+
globalThis.currentSearchProcess.kill();
|
|
43
|
+
delete globalThis.currentSearchProcess;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('Error terminating search process:', error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (results.length === 0) {
|
|
50
|
+
if (timeoutMs > 0) {
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: "text", text: `No matches found or search timed out after ${timeoutMs}ms.` }],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: "text", text: "No matches found" }],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Format the results in a VS Code-like format
|
|
60
|
+
let currentFile = "";
|
|
61
|
+
let formattedResults = "";
|
|
62
|
+
results.forEach(result => {
|
|
63
|
+
if (result.file !== currentFile) {
|
|
64
|
+
formattedResults += `\n${result.file}:\n`;
|
|
65
|
+
currentFile = result.file;
|
|
66
|
+
}
|
|
67
|
+
formattedResults += ` ${result.line}: ${result.match}\n`;
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: "text", text: formattedResults.trim() }],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle read_file command
|
|
3
|
+
*/
|
|
4
|
+
export declare function handleReadFile(args: unknown): Promise<{
|
|
5
|
+
content: ({
|
|
6
|
+
type: string;
|
|
7
|
+
text: string;
|
|
8
|
+
data?: undefined;
|
|
9
|
+
mimeType?: undefined;
|
|
10
|
+
} | {
|
|
11
|
+
type: string;
|
|
12
|
+
data: string;
|
|
13
|
+
mimeType: string;
|
|
14
|
+
text?: undefined;
|
|
15
|
+
})[];
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Handle read_multiple_files command
|
|
19
|
+
*/
|
|
20
|
+
export declare function handleReadMultipleFiles(args: unknown): Promise<{
|
|
21
|
+
content: {
|
|
22
|
+
type: string;
|
|
23
|
+
text?: string;
|
|
24
|
+
data?: string;
|
|
25
|
+
mimeType?: string;
|
|
26
|
+
}[];
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* Handle write_file command
|
|
30
|
+
*/
|
|
31
|
+
export declare function handleWriteFile(args: unknown): Promise<{
|
|
32
|
+
content: {
|
|
33
|
+
type: string;
|
|
34
|
+
text: string;
|
|
35
|
+
}[];
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Handle create_directory command
|
|
39
|
+
*/
|
|
40
|
+
export declare function handleCreateDirectory(args: unknown): Promise<{
|
|
41
|
+
content: {
|
|
42
|
+
type: string;
|
|
43
|
+
text: string;
|
|
44
|
+
}[];
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Handle list_directory command
|
|
48
|
+
*/
|
|
49
|
+
export declare function handleListDirectory(args: unknown): Promise<{
|
|
50
|
+
content: {
|
|
51
|
+
type: string;
|
|
52
|
+
text: string;
|
|
53
|
+
}[];
|
|
54
|
+
}>;
|
|
55
|
+
/**
|
|
56
|
+
* Handle move_file command
|
|
57
|
+
*/
|
|
58
|
+
export declare function handleMoveFile(args: unknown): Promise<{
|
|
59
|
+
content: {
|
|
60
|
+
type: string;
|
|
61
|
+
text: string;
|
|
62
|
+
}[];
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Handle search_files command
|
|
66
|
+
*/
|
|
67
|
+
export declare function handleSearchFiles(args: unknown): Promise<{
|
|
68
|
+
content: {
|
|
69
|
+
type: string;
|
|
70
|
+
text: string;
|
|
71
|
+
}[];
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Handle get_file_info command
|
|
75
|
+
*/
|
|
76
|
+
export declare function handleGetFileInfo(args: unknown): Promise<{
|
|
77
|
+
content: {
|
|
78
|
+
type: string;
|
|
79
|
+
text: string;
|
|
80
|
+
}[];
|
|
81
|
+
}>;
|
|
82
|
+
/**
|
|
83
|
+
* Handle list_allowed_directories command
|
|
84
|
+
*/
|
|
85
|
+
export declare function handleListAllowedDirectories(): {
|
|
86
|
+
content: {
|
|
87
|
+
type: string;
|
|
88
|
+
text: string;
|
|
89
|
+
}[];
|
|
90
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { readFile, readMultipleFiles, writeFile, createDirectory, listDirectory, moveFile, searchFiles, getFileInfo, listAllowedDirectories } from '../tools/filesystem.js';
|
|
2
|
+
import { withTimeout } from '../utils.js';
|
|
3
|
+
import { ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, SearchFilesArgsSchema, GetFileInfoArgsSchema } from '../tools/schemas.js';
|
|
4
|
+
/**
|
|
5
|
+
* Handle read_file command
|
|
6
|
+
*/
|
|
7
|
+
export async function handleReadFile(args) {
|
|
8
|
+
const HANDLER_TIMEOUT = 60000; // 60 seconds total operation timeout
|
|
9
|
+
const readFileOperation = async () => {
|
|
10
|
+
const parsed = ReadFileArgsSchema.parse(args);
|
|
11
|
+
// Explicitly cast the result to FileResult since we're passing true
|
|
12
|
+
const fileResult = await readFile(parsed.path, true, parsed.isUrl);
|
|
13
|
+
if (fileResult.isImage) {
|
|
14
|
+
// For image files, return as an image content type
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: "text",
|
|
19
|
+
text: `Image file: ${parsed.path} (${fileResult.mimeType})\n`
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: "image",
|
|
23
|
+
data: fileResult.content,
|
|
24
|
+
mimeType: fileResult.mimeType
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// For all other files, return as text
|
|
31
|
+
return {
|
|
32
|
+
content: [{ type: "text", text: fileResult.content }],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
// Execute with timeout at the handler level
|
|
37
|
+
return await withTimeout(readFileOperation(), HANDLER_TIMEOUT, 'Read file handler operation', {
|
|
38
|
+
content: [{ type: "text", text: `Operation timed out after ${HANDLER_TIMEOUT / 1000} seconds. The file might be too large or on a slow/unresponsive storage device.` }],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Handle read_multiple_files command
|
|
43
|
+
*/
|
|
44
|
+
export async function handleReadMultipleFiles(args) {
|
|
45
|
+
const parsed = ReadMultipleFilesArgsSchema.parse(args);
|
|
46
|
+
const fileResults = await readMultipleFiles(parsed.paths);
|
|
47
|
+
// Create a text summary of all files
|
|
48
|
+
const textSummary = fileResults.map(result => {
|
|
49
|
+
if (result.error) {
|
|
50
|
+
return `${result.path}: Error - ${result.error}`;
|
|
51
|
+
}
|
|
52
|
+
else if (result.mimeType) {
|
|
53
|
+
return `${result.path}: ${result.mimeType} ${result.isImage ? '(image)' : '(text)'}`;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return `${result.path}: Unknown type`;
|
|
57
|
+
}
|
|
58
|
+
}).join("\n");
|
|
59
|
+
// Create content items for each file
|
|
60
|
+
const contentItems = [];
|
|
61
|
+
// Add the text summary
|
|
62
|
+
contentItems.push({ type: "text", text: textSummary });
|
|
63
|
+
// Add each file content
|
|
64
|
+
for (const result of fileResults) {
|
|
65
|
+
if (!result.error && result.content !== undefined) {
|
|
66
|
+
if (result.isImage && result.mimeType) {
|
|
67
|
+
// For image files, add an image content item
|
|
68
|
+
contentItems.push({
|
|
69
|
+
type: "image",
|
|
70
|
+
data: result.content,
|
|
71
|
+
mimeType: result.mimeType
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// For text files, add a text summary
|
|
76
|
+
contentItems.push({
|
|
77
|
+
type: "text",
|
|
78
|
+
text: `\n--- ${result.path} contents: ---\n${result.content}`
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { content: contentItems };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Handle write_file command
|
|
87
|
+
*/
|
|
88
|
+
export async function handleWriteFile(args) {
|
|
89
|
+
const parsed = WriteFileArgsSchema.parse(args);
|
|
90
|
+
await writeFile(parsed.path, parsed.content);
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text", text: `Successfully wrote to ${parsed.path}` }],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Handle create_directory command
|
|
97
|
+
*/
|
|
98
|
+
export async function handleCreateDirectory(args) {
|
|
99
|
+
const parsed = CreateDirectoryArgsSchema.parse(args);
|
|
100
|
+
await createDirectory(parsed.path);
|
|
101
|
+
return {
|
|
102
|
+
content: [{ type: "text", text: `Successfully created directory ${parsed.path}` }],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Handle list_directory command
|
|
107
|
+
*/
|
|
108
|
+
export async function handleListDirectory(args) {
|
|
109
|
+
const parsed = ListDirectoryArgsSchema.parse(args);
|
|
110
|
+
const entries = await listDirectory(parsed.path);
|
|
111
|
+
return {
|
|
112
|
+
content: [{ type: "text", text: entries.join('\n') }],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Handle move_file command
|
|
117
|
+
*/
|
|
118
|
+
export async function handleMoveFile(args) {
|
|
119
|
+
const parsed = MoveFileArgsSchema.parse(args);
|
|
120
|
+
await moveFile(parsed.source, parsed.destination);
|
|
121
|
+
return {
|
|
122
|
+
content: [{ type: "text", text: `Successfully moved ${parsed.source} to ${parsed.destination}` }],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Handle search_files command
|
|
127
|
+
*/
|
|
128
|
+
export async function handleSearchFiles(args) {
|
|
129
|
+
const parsed = SearchFilesArgsSchema.parse(args);
|
|
130
|
+
const timeoutMs = parsed.timeoutMs || 30000; // 30 seconds default
|
|
131
|
+
// Apply timeout at the handler level
|
|
132
|
+
const searchOperation = async () => {
|
|
133
|
+
return await searchFiles(parsed.path, parsed.pattern);
|
|
134
|
+
};
|
|
135
|
+
// Use withTimeout at the handler level
|
|
136
|
+
const results = await withTimeout(searchOperation(), timeoutMs, 'File search operation', [] // Empty array as default on timeout
|
|
137
|
+
);
|
|
138
|
+
if (results.length === 0) {
|
|
139
|
+
// Similar approach as in handleSearchCode
|
|
140
|
+
if (timeoutMs > 0) {
|
|
141
|
+
return {
|
|
142
|
+
content: [{ type: "text", text: `No matches found or search timed out after ${timeoutMs}ms.` }],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
content: [{ type: "text", text: "No matches found" }],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
content: [{ type: "text", text: results.join('\n') }],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Handle get_file_info command
|
|
155
|
+
*/
|
|
156
|
+
export async function handleGetFileInfo(args) {
|
|
157
|
+
const parsed = GetFileInfoArgsSchema.parse(args);
|
|
158
|
+
const info = await getFileInfo(parsed.path);
|
|
159
|
+
return {
|
|
160
|
+
content: [{
|
|
161
|
+
type: "text",
|
|
162
|
+
text: Object.entries(info)
|
|
163
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
164
|
+
.join('\n')
|
|
165
|
+
}],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Handle list_allowed_directories command
|
|
170
|
+
*/
|
|
171
|
+
export function handleListAllowedDirectories() {
|
|
172
|
+
const directories = listAllowedDirectories();
|
|
173
|
+
return {
|
|
174
|
+
content: [{
|
|
175
|
+
type: "text",
|
|
176
|
+
text: `Allowed directories:\n${directories.join('\n')}`
|
|
177
|
+
}],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle list_processes command
|
|
3
|
+
*/
|
|
4
|
+
export declare function handleListProcesses(): Promise<{
|
|
5
|
+
content: Array<{
|
|
6
|
+
type: string;
|
|
7
|
+
text: string;
|
|
8
|
+
}>;
|
|
9
|
+
}>;
|
|
10
|
+
/**
|
|
11
|
+
* Handle kill_process command
|
|
12
|
+
*/
|
|
13
|
+
export declare function handleKillProcess(args: unknown): Promise<{
|
|
14
|
+
content: {
|
|
15
|
+
type: string;
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { listProcesses, killProcess } from '../tools/process.js';
|
|
2
|
+
import { KillProcessArgsSchema } from '../tools/schemas.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handle list_processes command
|
|
5
|
+
*/
|
|
6
|
+
export async function handleListProcesses() {
|
|
7
|
+
return listProcesses();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Handle kill_process command
|
|
11
|
+
*/
|
|
12
|
+
export async function handleKillProcess(args) {
|
|
13
|
+
const parsed = KillProcessArgsSchema.parse(args);
|
|
14
|
+
return killProcess(parsed);
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle execute_command command
|
|
3
|
+
*/
|
|
4
|
+
export declare function handleExecuteCommand(args: unknown): Promise<{
|
|
5
|
+
content: {
|
|
6
|
+
type: string;
|
|
7
|
+
text: string;
|
|
8
|
+
}[];
|
|
9
|
+
}>;
|
|
10
|
+
/**
|
|
11
|
+
* Handle read_output command
|
|
12
|
+
*/
|
|
13
|
+
export declare function handleReadOutput(args: unknown): Promise<{
|
|
14
|
+
content: {
|
|
15
|
+
type: string;
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Handle force_terminate command
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleForceTerminate(args: unknown): Promise<{
|
|
23
|
+
content: {
|
|
24
|
+
type: string;
|
|
25
|
+
text: string;
|
|
26
|
+
}[];
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* Handle list_sessions command
|
|
30
|
+
*/
|
|
31
|
+
export declare function handleListSessions(): Promise<{
|
|
32
|
+
content: {
|
|
33
|
+
type: string;
|
|
34
|
+
text: string;
|
|
35
|
+
}[];
|
|
36
|
+
}>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { executeCommand, readOutput, forceTerminate, listSessions } from '../tools/execute.js';
|
|
2
|
+
import { ExecuteCommandArgsSchema, ReadOutputArgsSchema, ForceTerminateArgsSchema } from '../tools/schemas.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handle execute_command command
|
|
5
|
+
*/
|
|
6
|
+
export async function handleExecuteCommand(args) {
|
|
7
|
+
const parsed = ExecuteCommandArgsSchema.parse(args);
|
|
8
|
+
return executeCommand(parsed);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Handle read_output command
|
|
12
|
+
*/
|
|
13
|
+
export async function handleReadOutput(args) {
|
|
14
|
+
const parsed = ReadOutputArgsSchema.parse(args);
|
|
15
|
+
return readOutput(parsed);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Handle force_terminate command
|
|
19
|
+
*/
|
|
20
|
+
export async function handleForceTerminate(args) {
|
|
21
|
+
const parsed = ForceTerminateArgsSchema.parse(args);
|
|
22
|
+
return forceTerminate(parsed);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Handle list_sessions command
|
|
26
|
+
*/
|
|
27
|
+
export async function handleListSessions() {
|
|
28
|
+
return listSessions();
|
|
29
|
+
}
|