@wonderwhy-er/desktop-commander 0.1.38 → 0.2.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.
Files changed (50) hide show
  1. package/README.md +89 -6
  2. package/dist/REPLSessionManager.d.ts +109 -0
  3. package/dist/REPLSessionManager.js +364 -0
  4. package/dist/REPLSessionManager.test.d.ts +1 -0
  5. package/dist/REPLSessionManager.test.js +75 -0
  6. package/dist/client/replClient.d.ts +63 -0
  7. package/dist/client/replClient.js +217 -0
  8. package/dist/client/sshClient.d.ts +82 -0
  9. package/dist/client/sshClient.js +200 -0
  10. package/dist/config-manager.d.ts +2 -0
  11. package/dist/config-manager.js +3 -1
  12. package/dist/config.d.ts +1 -0
  13. package/dist/config.js +1 -0
  14. package/dist/handlers/filesystem-handlers.js +37 -3
  15. package/dist/handlers/fuzzy-search-log-handlers.d.ts +13 -0
  16. package/dist/handlers/fuzzy-search-log-handlers.js +179 -0
  17. package/dist/handlers/repl-handlers.d.ts +21 -0
  18. package/dist/handlers/repl-handlers.js +37 -0
  19. package/dist/handlers/replCommandHandler.d.ts +125 -0
  20. package/dist/handlers/replCommandHandler.js +255 -0
  21. package/dist/handlers/replCommandHandler.test.d.ts +1 -0
  22. package/dist/handlers/replCommandHandler.test.js +103 -0
  23. package/dist/repl-manager.d.ts +73 -0
  24. package/dist/repl-manager.js +407 -0
  25. package/dist/replIntegration.d.ts +14 -0
  26. package/dist/replIntegration.js +27 -0
  27. package/dist/server.js +37 -19
  28. package/dist/setup-claude-server.js +0 -20
  29. package/dist/tools/edit.js +129 -29
  30. package/dist/tools/enhanced-read-output.js +69 -0
  31. package/dist/tools/enhanced-send-input.js +111 -0
  32. package/dist/tools/filesystem.d.ts +7 -5
  33. package/dist/tools/filesystem.js +56 -27
  34. package/dist/tools/repl.d.ts +21 -0
  35. package/dist/tools/repl.js +217 -0
  36. package/dist/tools/schemas.d.ts +9 -0
  37. package/dist/tools/schemas.js +3 -0
  38. package/dist/tools/send-input.d.ts +2 -0
  39. package/dist/tools/send-input.js +45 -0
  40. package/dist/utils/fuzzySearchLogger.d.ts +30 -0
  41. package/dist/utils/fuzzySearchLogger.js +126 -0
  42. package/dist/utils/lineEndingHandler.d.ts +21 -0
  43. package/dist/utils/lineEndingHandler.js +77 -0
  44. package/dist/utils/lineEndingHandler_optimized.d.ts +21 -0
  45. package/dist/utils/lineEndingHandler_optimized.js +77 -0
  46. package/dist/utils/trackTools.d.ts +6 -0
  47. package/dist/utils/trackTools.js +54 -0
  48. package/dist/version.d.ts +1 -1
  49. package/dist/version.js +1 -1
  50. package/package.json +7 -2
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Detect the line ending style used in a file - Optimized version
3
+ * This algorithm uses early termination for maximum performance
4
+ */
5
+ export function detectLineEnding(content) {
6
+ for (let i = 0; i < content.length; i++) {
7
+ if (content[i] === '\r') {
8
+ if (i + 1 < content.length && content[i + 1] === '\n') {
9
+ return '\r\n';
10
+ }
11
+ return '\r';
12
+ }
13
+ if (content[i] === '\n') {
14
+ return '\n';
15
+ }
16
+ }
17
+ // Default to system line ending if no line endings found
18
+ return process.platform === 'win32' ? '\r\n' : '\n';
19
+ }
20
+ /**
21
+ * Normalize line endings to match the target style
22
+ */
23
+ export function normalizeLineEndings(text, targetLineEnding) {
24
+ // First normalize to LF
25
+ let normalized = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
26
+ // Then convert to target
27
+ if (targetLineEnding === '\r\n') {
28
+ return normalized.replace(/\n/g, '\r\n');
29
+ }
30
+ else if (targetLineEnding === '\r') {
31
+ return normalized.replace(/\n/g, '\r');
32
+ }
33
+ return normalized;
34
+ }
35
+ /**
36
+ * Analyze line ending usage in content
37
+ */
38
+ export function analyzeLineEndings(content) {
39
+ let crlfCount = 0;
40
+ let lfCount = 0;
41
+ let crCount = 0;
42
+ // Count line endings
43
+ for (let i = 0; i < content.length; i++) {
44
+ if (content[i] === '\r') {
45
+ if (i + 1 < content.length && content[i + 1] === '\n') {
46
+ crlfCount++;
47
+ i++; // Skip the LF
48
+ }
49
+ else {
50
+ crCount++;
51
+ }
52
+ }
53
+ else if (content[i] === '\n') {
54
+ lfCount++;
55
+ }
56
+ }
57
+ // Determine predominant style
58
+ const total = crlfCount + lfCount + crCount;
59
+ let style;
60
+ if (crlfCount > lfCount && crlfCount > crCount) {
61
+ style = '\r\n';
62
+ }
63
+ else if (lfCount > crCount) {
64
+ style = '\n';
65
+ }
66
+ else {
67
+ style = '\r';
68
+ }
69
+ // Check for mixed line endings
70
+ const usedStyles = [crlfCount > 0, lfCount > 0, crCount > 0].filter(Boolean).length;
71
+ const hasMixed = usedStyles > 1;
72
+ return {
73
+ style,
74
+ count: total,
75
+ hasMixed
76
+ };
77
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Line ending types
3
+ */
4
+ export type LineEndingStyle = '\r\n' | '\n' | '\r';
5
+ /**
6
+ * Detect the line ending style used in a file - Optimized version
7
+ * This algorithm uses early termination for maximum performance
8
+ */
9
+ export declare function detectLineEnding(content: string): LineEndingStyle;
10
+ /**
11
+ * Normalize line endings to match the target style
12
+ */
13
+ export declare function normalizeLineEndings(text: string, targetLineEnding: LineEndingStyle): string;
14
+ /**
15
+ * Analyze line ending usage in content
16
+ */
17
+ export declare function analyzeLineEndings(content: string): {
18
+ style: LineEndingStyle;
19
+ count: number;
20
+ hasMixed: boolean;
21
+ };
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Detect the line ending style used in a file - Optimized version
3
+ * This algorithm uses early termination for maximum performance
4
+ */
5
+ export function detectLineEnding(content) {
6
+ for (let i = 0; i < content.length; i++) {
7
+ if (content[i] === '\r') {
8
+ if (i + 1 < content.length && content[i + 1] === '\n') {
9
+ return '\r\n';
10
+ }
11
+ return '\r';
12
+ }
13
+ if (content[i] === '\n') {
14
+ return '\n';
15
+ }
16
+ }
17
+ // Default to system line ending if no line endings found
18
+ return process.platform === 'win32' ? '\r\n' : '\n';
19
+ }
20
+ /**
21
+ * Normalize line endings to match the target style
22
+ */
23
+ export function normalizeLineEndings(text, targetLineEnding) {
24
+ // First normalize to LF
25
+ let normalized = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
26
+ // Then convert to target
27
+ if (targetLineEnding === '\r\n') {
28
+ return normalized.replace(/\n/g, '\r\n');
29
+ }
30
+ else if (targetLineEnding === '\r') {
31
+ return normalized.replace(/\n/g, '\r');
32
+ }
33
+ return normalized;
34
+ }
35
+ /**
36
+ * Analyze line ending usage in content
37
+ */
38
+ export function analyzeLineEndings(content) {
39
+ let crlfCount = 0;
40
+ let lfCount = 0;
41
+ let crCount = 0;
42
+ // Count line endings
43
+ for (let i = 0; i < content.length; i++) {
44
+ if (content[i] === '\r') {
45
+ if (i + 1 < content.length && content[i + 1] === '\n') {
46
+ crlfCount++;
47
+ i++; // Skip the LF
48
+ }
49
+ else {
50
+ crCount++;
51
+ }
52
+ }
53
+ else if (content[i] === '\n') {
54
+ lfCount++;
55
+ }
56
+ }
57
+ // Determine predominant style
58
+ const total = crlfCount + lfCount + crCount;
59
+ let style;
60
+ if (crlfCount > lfCount && crlfCount > crCount) {
61
+ style = '\r\n';
62
+ }
63
+ else if (lfCount > crCount) {
64
+ style = '\n';
65
+ }
66
+ else {
67
+ style = '\r';
68
+ }
69
+ // Check for mixed line endings
70
+ const usedStyles = [crlfCount > 0, lfCount > 0, crCount > 0].filter(Boolean).length;
71
+ const hasMixed = usedStyles > 1;
72
+ return {
73
+ style,
74
+ count: total,
75
+ hasMixed
76
+ };
77
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Track tool calls and save them to a log file
3
+ * @param toolName Name of the tool being called
4
+ * @param args Arguments passed to the tool (optional)
5
+ */
6
+ export declare function trackToolCall(toolName: string, args?: unknown): Promise<void>;
@@ -0,0 +1,54 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { TOOL_CALL_FILE, TOOL_CALL_FILE_MAX_SIZE } from '../config.js';
4
+ // Ensure the directory for the log file exists
5
+ const logDir = path.dirname(TOOL_CALL_FILE);
6
+ await fs.promises.mkdir(logDir, { recursive: true });
7
+ /**
8
+ * Track tool calls and save them to a log file
9
+ * @param toolName Name of the tool being called
10
+ * @param args Arguments passed to the tool (optional)
11
+ */
12
+ export async function trackToolCall(toolName, args) {
13
+ try {
14
+ // Get current timestamp
15
+ const timestamp = new Date().toISOString();
16
+ // Format the log entry
17
+ const logEntry = `${timestamp} | ${toolName.padEnd(20, ' ')}${args ? `\t| Arguments: ${JSON.stringify(args)}` : ''}\n`;
18
+ // Check if file exists and get its size
19
+ let fileSize = 0;
20
+ try {
21
+ const stats = await fs.promises.stat(TOOL_CALL_FILE);
22
+ fileSize = stats.size;
23
+ }
24
+ catch (err) {
25
+ // File doesn't exist yet, size remains 0
26
+ }
27
+ // If file size is 10MB or larger, rotate the log file
28
+ if (fileSize >= TOOL_CALL_FILE_MAX_SIZE) {
29
+ const fileExt = path.extname(TOOL_CALL_FILE);
30
+ const fileBase = path.basename(TOOL_CALL_FILE, fileExt);
31
+ const dirName = path.dirname(TOOL_CALL_FILE);
32
+ // Create a timestamp-based filename for the old log
33
+ const date = new Date();
34
+ const rotateTimestamp = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}_${String(date.getHours()).padStart(2, '0')}-${String(date.getMinutes()).padStart(2, '0')}-${String(date.getSeconds()).padStart(2, '0')}`;
35
+ const newFileName = path.join(dirName, `${fileBase}_${rotateTimestamp}${fileExt}`);
36
+ // Rename the current file
37
+ await fs.promises.rename(TOOL_CALL_FILE, newFileName);
38
+ }
39
+ // Append to log file (if file was renamed, this will create a new file)
40
+ await fs.promises.appendFile(TOOL_CALL_FILE, logEntry, 'utf8');
41
+ }
42
+ catch (error) {
43
+ const errorMessage = error instanceof Error ? error.message : String(error);
44
+ const { capture } = await import('./capture.js');
45
+ // Send a final telemetry event noting that the user has opted out
46
+ // This helps us track opt-out rates while respecting the user's choice
47
+ await capture('server_track_tool_call_error', {
48
+ error: errorMessage,
49
+ toolName
50
+ });
51
+ // Don't let logging errors affect the main functionality
52
+ console.error(`Error logging tool call: ${error instanceof Error ? error.message : String(error)}`);
53
+ }
54
+ }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.38";
1
+ export declare const VERSION = "0.2.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.1.38';
1
+ export const VERSION = '0.2.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonderwhy-er/desktop-commander",
3
- "version": "0.1.38",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server for terminal operations and file editing",
5
5
  "license": "MIT",
6
6
  "author": "Eduards Ruzga",
@@ -34,7 +34,11 @@
34
34
  "test": "node test/run-all-tests.js",
35
35
  "link:local": "npm run build && npm link",
36
36
  "unlink:local": "npm unlink",
37
- "inspector": "npx @modelcontextprotocol/inspector dist/index.js"
37
+ "inspector": "npx @modelcontextprotocol/inspector dist/index.js",
38
+ "logs:view": "npm run build && node scripts/view-fuzzy-logs.js",
39
+ "logs:analyze": "npm run build && node scripts/analyze-fuzzy-logs.js",
40
+ "logs:clear": "npm run build && node scripts/clear-fuzzy-logs.js",
41
+ "logs:export": "npm run build && node scripts/export-fuzzy-logs.js"
38
42
  },
39
43
  "publishConfig": {
40
44
  "access": "public"
@@ -69,6 +73,7 @@
69
73
  },
70
74
  "devDependencies": {
71
75
  "@types/node": "^20.17.24",
76
+ "commander": "^13.1.0",
72
77
  "nexe": "^5.0.0-beta.4",
73
78
  "nodemon": "^3.0.2",
74
79
  "shx": "^0.3.4",