@wonderwhy-er/desktop-commander 0.2.2 → 0.2.4

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 (51) hide show
  1. package/README.md +27 -4
  2. package/dist/config-manager.d.ts +10 -0
  3. package/dist/config-manager.js +7 -1
  4. package/dist/handlers/edit-search-handlers.js +25 -6
  5. package/dist/handlers/filesystem-handlers.js +2 -4
  6. package/dist/handlers/terminal-handlers.d.ts +8 -4
  7. package/dist/handlers/terminal-handlers.js +16 -10
  8. package/dist/index-dxt.d.ts +2 -0
  9. package/dist/index-dxt.js +76 -0
  10. package/dist/index-with-startup-detection.d.ts +5 -0
  11. package/dist/index-with-startup-detection.js +180 -0
  12. package/dist/server.d.ts +5 -0
  13. package/dist/server.js +381 -65
  14. package/dist/terminal-manager.d.ts +7 -0
  15. package/dist/terminal-manager.js +93 -18
  16. package/dist/tools/client.d.ts +10 -0
  17. package/dist/tools/client.js +13 -0
  18. package/dist/tools/config.d.ts +1 -1
  19. package/dist/tools/config.js +21 -3
  20. package/dist/tools/edit.js +4 -3
  21. package/dist/tools/environment.d.ts +55 -0
  22. package/dist/tools/environment.js +65 -0
  23. package/dist/tools/feedback.d.ts +8 -0
  24. package/dist/tools/feedback.js +132 -0
  25. package/dist/tools/filesystem.d.ts +10 -0
  26. package/dist/tools/filesystem.js +410 -60
  27. package/dist/tools/improved-process-tools.d.ts +24 -0
  28. package/dist/tools/improved-process-tools.js +453 -0
  29. package/dist/tools/schemas.d.ts +20 -2
  30. package/dist/tools/schemas.js +20 -3
  31. package/dist/tools/usage.d.ts +5 -0
  32. package/dist/tools/usage.js +24 -0
  33. package/dist/utils/capture.d.ts +2 -0
  34. package/dist/utils/capture.js +40 -9
  35. package/dist/utils/early-logger.d.ts +4 -0
  36. package/dist/utils/early-logger.js +35 -0
  37. package/dist/utils/mcp-logger.d.ts +30 -0
  38. package/dist/utils/mcp-logger.js +59 -0
  39. package/dist/utils/process-detection.d.ts +23 -0
  40. package/dist/utils/process-detection.js +150 -0
  41. package/dist/utils/smithery-detector.d.ts +94 -0
  42. package/dist/utils/smithery-detector.js +292 -0
  43. package/dist/utils/startup-detector.d.ts +65 -0
  44. package/dist/utils/startup-detector.js +390 -0
  45. package/dist/utils/system-info.d.ts +30 -0
  46. package/dist/utils/system-info.js +146 -0
  47. package/dist/utils/usageTracker.d.ts +85 -0
  48. package/dist/utils/usageTracker.js +280 -0
  49. package/dist/version.d.ts +1 -1
  50. package/dist/version.js +1 -1
  51. package/package.json +4 -1
package/dist/server.js CHANGED
@@ -1,14 +1,21 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListPromptsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
2
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListPromptsRequestSchema, InitializeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
3
3
  import { zodToJsonSchema } from "zod-to-json-schema";
4
- // Shared constants for tool descriptions
5
- const PATH_GUIDANCE = `IMPORTANT: Always use absolute paths (starting with '/' or drive letter like 'C:\\') for reliability. Relative paths may fail as they depend on the current working directory. Tilde paths (~/...) might not work in all contexts. Unless the user explicitly asks for relative paths, use absolute paths.`;
4
+ import { getSystemInfo, getOSSpecificGuidance, getPathGuidance, getDevelopmentToolGuidance } from './utils/system-info.js';
5
+ // Get system information once at startup
6
+ const SYSTEM_INFO = getSystemInfo();
7
+ const OS_GUIDANCE = getOSSpecificGuidance(SYSTEM_INFO);
8
+ const DEV_TOOL_GUIDANCE = getDevelopmentToolGuidance(SYSTEM_INFO);
9
+ const PATH_GUIDANCE = `IMPORTANT: ${getPathGuidance(SYSTEM_INFO)} Relative paths may fail as they depend on the current working directory. Tilde paths (~/...) might not work in all contexts. Unless the user explicitly asks for relative paths, use absolute paths.`;
6
10
  const CMD_PREFIX_DESCRIPTION = `This command can be referenced as "DC: ..." or "use Desktop Commander to ..." in your instructions.`;
7
- import { ExecuteCommandArgsSchema, ReadOutputArgsSchema, ForceTerminateArgsSchema, ListSessionsArgsSchema, KillProcessArgsSchema, ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, SearchFilesArgsSchema, GetFileInfoArgsSchema, SearchCodeArgsSchema, GetConfigArgsSchema, SetConfigValueArgsSchema, ListProcessesArgsSchema, EditBlockArgsSchema, } from './tools/schemas.js';
11
+ import { StartProcessArgsSchema, ReadProcessOutputArgsSchema, InteractWithProcessArgsSchema, ForceTerminateArgsSchema, ListSessionsArgsSchema, KillProcessArgsSchema, ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, SearchFilesArgsSchema, GetFileInfoArgsSchema, SearchCodeArgsSchema, GetConfigArgsSchema, SetConfigValueArgsSchema, ListProcessesArgsSchema, EditBlockArgsSchema, GetUsageStatsArgsSchema, GiveFeedbackArgsSchema, } from './tools/schemas.js';
8
12
  import { getConfig, setConfigValue } from './tools/config.js';
13
+ import { getUsageStats } from './tools/usage.js';
14
+ import { giveFeedbackToDesktopCommander } from './tools/feedback.js';
9
15
  import { trackToolCall } from './utils/trackTools.js';
16
+ import { usageTracker } from './utils/usageTracker.js';
10
17
  import { VERSION } from './version.js';
11
- import { capture } from "./utils/capture.js";
18
+ import { capture, capture_call_tool } from "./utils/capture.js";
12
19
  console.error("Loading server.ts");
13
20
  export const server = new Server({
14
21
  name: "desktop-commander",
@@ -34,6 +41,41 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
34
41
  prompts: [],
35
42
  };
36
43
  });
44
+ // Store current client info (simple variable)
45
+ let currentClient = { name: 'uninitialized', version: 'uninitialized' };
46
+ // Add handler for initialization method - capture client info
47
+ server.setRequestHandler(InitializeRequestSchema, async (request) => {
48
+ try {
49
+ // Extract and store current client information
50
+ const clientInfo = request.params?.clientInfo;
51
+ if (clientInfo) {
52
+ currentClient = {
53
+ name: clientInfo.name || 'unknown',
54
+ version: clientInfo.version || 'unknown'
55
+ };
56
+ console.log(`Client connected: ${currentClient.name} v${currentClient.version}`);
57
+ }
58
+ // Return standard initialization response
59
+ return {
60
+ protocolVersion: "2024-11-05",
61
+ capabilities: {
62
+ tools: {},
63
+ resources: {},
64
+ prompts: {},
65
+ },
66
+ serverInfo: {
67
+ name: "desktop-commander",
68
+ version: VERSION,
69
+ },
70
+ };
71
+ }
72
+ catch (error) {
73
+ console.error("Error in initialization handler:", error);
74
+ throw error;
75
+ }
76
+ });
77
+ // Export current client info for access by other modules
78
+ export { currentClient };
37
79
  console.error("Setting up request handlers...");
38
80
  server.setRequestHandler(ListToolsRequestSchema, async () => {
39
81
  try {
@@ -51,7 +93,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
51
93
  - fileReadLineLimit (max lines for read_file, default 1000)
52
94
  - fileWriteLineLimit (max lines per write_file call, default 50)
53
95
  - telemetryEnabled (boolean for telemetry opt-in/out)
54
- - version (version of the DesktopCommander)
96
+ - currentClient (information about the currently connected MCP client)
97
+ - clientHistory (history of all clients that have connected)
98
+ - version (version of the DesktopCommander)
99
+ - systemInfo (operating system and environment details)
55
100
  ${CMD_PREFIX_DESCRIPTION}`,
56
101
  inputSchema: zodToJsonSchema(GetConfigArgsSchema),
57
102
  },
@@ -87,7 +132,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
87
132
 
88
133
  Supports partial file reading with:
89
134
  - 'offset' (start line, default: 0)
135
+ * Positive: Start from line N (0-based indexing)
136
+ * Negative: Read last N lines from end (tail behavior)
90
137
  - 'length' (max lines to read, default: configurable via 'fileReadLineLimit' setting, initially 1000)
138
+ * Used with positive offsets for range reading
139
+ * Ignored when offset is negative (reads all requested tail lines)
140
+
141
+ Examples:
142
+ - offset: 0, length: 10 → First 10 lines
143
+ - offset: 100, length: 5 → Lines 100-104
144
+ - offset: -20 → Last 20 lines
145
+ - offset: -5, length: 10 → Last 5 lines (length ignored)
146
+
147
+ Performance optimizations:
148
+ - Large files with negative offsets use reverse reading for efficiency
149
+ - Large files with deep positive offsets use byte estimation
150
+ - Small files use fast readline streaming
91
151
 
92
152
  When reading from the file system, only works within allowed directories.
93
153
  Can fetch content from URLs when isUrl parameter is set to true
@@ -119,30 +179,30 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
119
179
  {
120
180
  name: "write_file",
121
181
  description: `
122
- Write or append to file contents with a configurable line limit per call (default: 50 lines).
123
- THIS IS A STRICT REQUIREMENT. ANY file with more than the configured limit MUST BE written in chunks or IT WILL FAIL.
182
+ Write or append to file contents.
183
+
184
+ CHUNKING IS STANDARD PRACTICE: Always write files in chunks of 25-30 lines maximum.
185
+ This is the normal, recommended way to write files - not an emergency measure.
186
+
187
+ STANDARD PROCESS FOR ANY FILE:
188
+ 1. FIRST → write_file(filePath, firstChunk, {mode: 'rewrite'}) [≤30 lines]
189
+ 2. THEN → write_file(filePath, secondChunk, {mode: 'append'}) [≤30 lines]
190
+ 3. CONTINUE → write_file(filePath, nextChunk, {mode: 'append'}) [≤30 lines]
124
191
 
125
- ⚠️ IMPORTANT: PREVENTATIVE CHUNKING REQUIRED in these scenarios:
126
- 1. When content exceeds 2,000 words or 30 lines
127
- 2. When writing MULTIPLE files one after another (each next file is more likely to be truncated)
128
- 3. When the file is the LAST ONE in a series of operations in the same message
129
-
130
- ALWAYS split files writes in to multiple smaller writes PREEMPTIVELY without asking the user in these scenarios.
131
-
132
- REQUIRED PROCESS FOR LARGE NEW FILE WRITES OR REWRITES:
133
- 1. FIRST write_file(filePath, firstChunk, {mode: 'rewrite'})
134
- 2. THEN write_file(filePath, secondChunk, {mode: 'append'})
135
- 3. THEN write_file(filePath, thirdChunk, {mode: 'append'})
136
- ... and so on for each chunk
137
-
138
- HANDLING TRUNCATION ("Continue" prompts):
139
- If user asked to "Continue" after unfinished file write:
140
- 1. First, read the file to find out what content was successfully written
141
- 2. Identify exactly where the content was truncated
142
- 3. Continue writing ONLY the remaining content using {mode: 'append'}
143
- 4. Split the remaining content into smaller chunks (15-20 lines per chunk)
144
-
145
- Files over the line limit (configurable via 'fileWriteLineLimit' setting) WILL BE REJECTED if not broken into chunks as described above.
192
+ ALWAYS CHUNK PROACTIVELY - don't wait for performance warnings!
193
+
194
+ WHEN TO CHUNK (always be proactive):
195
+ 1. Any file expected to be longer than 25-30 lines
196
+ 2. When writing multiple files in sequence
197
+ 3. When creating documentation, code files, or configuration files
198
+
199
+ HANDLING CONTINUATION ("Continue" prompts):
200
+ If user asks to "Continue" after an incomplete operation:
201
+ 1. Read the file to see what was successfully written
202
+ 2. Continue writing ONLY the remaining content using {mode: 'append'}
203
+ 3. Keep chunks to 25-30 lines each
204
+
205
+ Files over 50 lines will generate performance notes but are still written successfully.
146
206
  Only works within allowed directories.
147
207
 
148
208
  ${PATH_GUIDANCE}
@@ -277,27 +337,132 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
277
337
  },
278
338
  // Terminal tools
279
339
  {
280
- name: "execute_command",
340
+ name: "start_process",
281
341
  description: `
282
- Execute a terminal command with timeout.
342
+ Start a new terminal process with intelligent state detection.
343
+
344
+ PRIMARY TOOL FOR FILE ANALYSIS AND DATA PROCESSING
345
+ This is the ONLY correct tool for analyzing local files (CSV, JSON, logs, etc.).
346
+ The analysis tool CANNOT access local files and WILL FAIL - always use processes for file-based work.
347
+
348
+ CRITICAL RULE: For ANY local file work, ALWAYS use this tool + interact_with_process, NEVER use analysis/REPL tool.
349
+
350
+ ${OS_GUIDANCE}
351
+
352
+ REQUIRED WORKFLOW FOR LOCAL FILES:
353
+ 1. start_process("python3 -i") - Start Python REPL for data analysis
354
+ 2. interact_with_process(pid, "import pandas as pd, numpy as np")
355
+ 3. interact_with_process(pid, "df = pd.read_csv('/absolute/path/file.csv')")
356
+ 4. interact_with_process(pid, "print(df.describe())")
357
+ 5. Continue analysis with pandas, matplotlib, seaborn, etc.
358
+
359
+ COMMON FILE ANALYSIS PATTERNS:
360
+ • start_process("python3 -i") → Python REPL for data analysis (RECOMMENDED)
361
+ • start_process("node -i") → Node.js for JSON processing
362
+ • start_process("cut -d',' -f1 file.csv | sort | uniq -c") → Quick CSV analysis
363
+ • start_process("wc -l /path/file.csv") → Line counting
364
+ • start_process("head -10 /path/file.csv") → File preview
365
+
366
+ INTERACTIVE PROCESSES FOR DATA ANALYSIS:
367
+ 1. start_process("python3 -i") - Start Python REPL for data work
368
+ 2. start_process("node -i") - Start Node.js REPL for JSON/JS
369
+ 3. start_process("bash") - Start interactive bash shell
370
+ 4. Use interact_with_process() to send commands
371
+ 5. Use read_process_output() to get responses
372
+
373
+ SMART DETECTION:
374
+ - Detects REPL prompts (>>>, >, $, etc.)
375
+ - Identifies when process is waiting for input
376
+ - Recognizes process completion vs timeout
377
+ - Early exit prevents unnecessary waiting
378
+
379
+ STATES DETECTED:
380
+ Process waiting for input (shows prompt)
381
+ Process finished execution
382
+ Process running (use read_process_output)
383
+
384
+ ALWAYS USE FOR: Local file analysis, CSV processing, data exploration, system commands
385
+ NEVER USE ANALYSIS TOOL FOR: Local file access (analysis tool is browser-only and WILL FAIL)
283
386
 
284
- Command will continue running in background if it doesn't complete within timeout.
387
+ ${PATH_GUIDANCE}
388
+ ${CMD_PREFIX_DESCRIPTION}`,
389
+ inputSchema: zodToJsonSchema(StartProcessArgsSchema),
390
+ },
391
+ {
392
+ name: "read_process_output",
393
+ description: `
394
+ Read output from a running process with intelligent completion detection.
285
395
 
286
- NOTE: For file operations, prefer specialized tools like read_file, search_code,
287
- list_directory instead of cat, grep, or ls commands.
396
+ Automatically detects when process is ready for more input instead of timing out.
397
+
398
+ SMART FEATURES:
399
+ - Early exit when REPL shows prompt (>>>, >, etc.)
400
+ - Detects process completion vs still running
401
+ - Prevents hanging on interactive prompts
402
+ - Clear status messages about process state
403
+
404
+ REPL USAGE:
405
+ - Stops immediately when REPL prompt detected
406
+ - Shows clear status: waiting for input vs finished
407
+ - Shorter timeouts needed due to smart detection
408
+ - Works with Python, Node.js, R, Julia, etc.
409
+
410
+ DETECTION STATES:
411
+ Process waiting for input (ready for interact_with_process)
412
+ Process finished execution
413
+ Timeout reached (may still be running)
288
414
 
289
- ${PATH_GUIDANCE}
290
415
  ${CMD_PREFIX_DESCRIPTION}`,
291
- inputSchema: zodToJsonSchema(ExecuteCommandArgsSchema),
416
+ inputSchema: zodToJsonSchema(ReadProcessOutputArgsSchema),
292
417
  },
293
418
  {
294
- name: "read_output",
419
+ name: "interact_with_process",
295
420
  description: `
296
- Read new output from a running terminal session.
297
- Set timeout_ms for long running commands.
421
+ Send input to a running process and automatically receive the response.
422
+
423
+ CRITICAL: THIS IS THE PRIMARY TOOL FOR ALL LOCAL FILE ANALYSIS
424
+ For ANY local file analysis (CSV, JSON, data processing), ALWAYS use this instead of the analysis tool.
425
+ The analysis tool CANNOT access local files and WILL FAIL - use processes for ALL file-based work.
426
+
427
+ FILE ANALYSIS PRIORITY ORDER (MANDATORY):
428
+ 1. ALWAYS FIRST: Use this tool (start_process + interact_with_process) for local data analysis
429
+ 2. ALTERNATIVE: Use command-line tools (cut, awk, grep) for quick processing
430
+ 3. NEVER EVER: Use analysis tool for local file access (IT WILL FAIL)
431
+
432
+ REQUIRED INTERACTIVE WORKFLOW FOR FILE ANALYSIS:
433
+ 1. Start REPL: start_process("python3 -i")
434
+ 2. Load libraries: interact_with_process(pid, "import pandas as pd, numpy as np")
435
+ 3. Read file: interact_with_process(pid, "df = pd.read_csv('/absolute/path/file.csv')")
436
+ 4. Analyze: interact_with_process(pid, "print(df.describe())")
437
+ 5. Continue: interact_with_process(pid, "df.groupby('column').size()")
438
+
439
+ SMART DETECTION:
440
+ - Automatically waits for REPL prompt (>>>, >, etc.)
441
+ - Detects errors and completion states
442
+ - Early exit prevents timeout delays
443
+ - Clean output formatting (removes prompts)
444
+
445
+ SUPPORTED REPLs:
446
+ - Python: python3 -i (RECOMMENDED for data analysis)
447
+ - Node.js: node -i
448
+ - R: R
449
+ - Julia: julia
450
+ - Shell: bash, zsh
451
+ - Database: mysql, postgres
452
+
453
+ PARAMETERS:
454
+ - pid: Process ID from start_process
455
+ - input: Code/command to execute
456
+ - timeout_ms: Max wait (default: 8000ms)
457
+ - wait_for_prompt: Auto-wait for response (default: true)
458
+
459
+ Returns execution result with status indicators.
460
+
461
+ ALWAYS USE FOR: CSV analysis, JSON processing, file statistics, data visualization prep, ANY local file work
462
+ NEVER USE ANALYSIS TOOL FOR: Local file access (it cannot read files from disk and WILL FAIL)
298
463
 
299
464
  ${CMD_PREFIX_DESCRIPTION}`,
300
- inputSchema: zodToJsonSchema(ReadOutputArgsSchema),
465
+ inputSchema: zodToJsonSchema(InteractWithProcessArgsSchema),
301
466
  },
302
467
  {
303
468
  name: "force_terminate",
@@ -312,6 +477,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
312
477
  description: `
313
478
  List all active terminal sessions.
314
479
 
480
+ Shows session status including:
481
+ - PID: Process identifier
482
+ - Blocked: Whether session is waiting for input
483
+ - Runtime: How long the session has been running
484
+
485
+ DEBUGGING REPLs:
486
+ - "Blocked: true" often means REPL is waiting for input
487
+ - Use this to verify sessions are running before sending input
488
+ - Long runtime with blocked status may indicate stuck process
489
+
315
490
  ${CMD_PREFIX_DESCRIPTION}`,
316
491
  inputSchema: zodToJsonSchema(ListSessionsArgsSchema),
317
492
  },
@@ -335,6 +510,53 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
335
510
  ${CMD_PREFIX_DESCRIPTION}`,
336
511
  inputSchema: zodToJsonSchema(KillProcessArgsSchema),
337
512
  },
513
+ {
514
+ name: "get_usage_stats",
515
+ description: `
516
+ Get usage statistics for debugging and analysis.
517
+
518
+ Returns summary of tool usage, success/failure rates, and performance metrics.
519
+
520
+ ${CMD_PREFIX_DESCRIPTION}`,
521
+ inputSchema: zodToJsonSchema(GetUsageStatsArgsSchema),
522
+ },
523
+ {
524
+ name: "give_feedback_to_desktop_commander",
525
+ description: `
526
+ Open feedback form in browser to provide feedback about Desktop Commander.
527
+
528
+ IMPORTANT: This tool simply opens the feedback form - no pre-filling available.
529
+ The user will fill out the form manually in their browser.
530
+
531
+ WORKFLOW:
532
+ 1. When user agrees to give feedback, just call this tool immediately
533
+ 2. No need to ask questions or collect information
534
+ 3. Tool opens form with only usage statistics pre-filled automatically:
535
+ - tool_call_count: Number of commands they've made
536
+ - days_using: How many days they've used Desktop Commander
537
+ - platform: Their operating system (Mac/Windows/Linux)
538
+ - client_id: Analytics identifier
539
+
540
+ All survey questions will be answered directly in the form:
541
+ - Job title and technical comfort level
542
+ - Company URL for industry context
543
+ - Other AI tools they use
544
+ - Desktop Commander's biggest advantage
545
+ - How they typically use it
546
+ - Recommendation likelihood (0-10)
547
+ - User study participation interest
548
+ - Email and any additional feedback
549
+
550
+ EXAMPLE INTERACTION:
551
+ User: "sure, I'll give feedback"
552
+ Claude: "Perfect! Let me open the feedback form for you."
553
+ [calls tool immediately]
554
+
555
+ No parameters are needed - just call the tool to open the form.
556
+
557
+ ${CMD_PREFIX_DESCRIPTION}`,
558
+ inputSchema: zodToJsonSchema(GiveFeedbackArgsSchema),
559
+ },
338
560
  ],
339
561
  };
340
562
  }
@@ -345,83 +567,177 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
345
567
  });
346
568
  import * as handlers from './handlers/index.js';
347
569
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
570
+ const { name, arguments: args } = request.params;
348
571
  try {
349
- const { name, arguments: args } = request.params;
350
- capture('server_call_tool', {
572
+ capture_call_tool('server_call_tool', {
351
573
  name
352
574
  });
353
575
  // Track tool call
354
576
  trackToolCall(name, args);
355
577
  // Using a more structured approach with dedicated handlers
578
+ let result;
356
579
  switch (name) {
357
580
  // Config tools
358
581
  case "get_config":
359
582
  try {
360
- return await getConfig();
583
+ result = await getConfig();
361
584
  }
362
585
  catch (error) {
363
586
  capture('server_request_error', { message: `Error in get_config handler: ${error}` });
364
- return {
587
+ result = {
365
588
  content: [{ type: "text", text: `Error: Failed to get configuration` }],
366
589
  isError: true,
367
590
  };
368
591
  }
592
+ break;
369
593
  case "set_config_value":
370
594
  try {
371
- return await setConfigValue(args);
595
+ result = await setConfigValue(args);
372
596
  }
373
597
  catch (error) {
374
598
  capture('server_request_error', { message: `Error in set_config_value handler: ${error}` });
375
- return {
599
+ result = {
376
600
  content: [{ type: "text", text: `Error: Failed to set configuration value` }],
377
601
  isError: true,
378
602
  };
379
603
  }
604
+ break;
605
+ case "get_usage_stats":
606
+ try {
607
+ result = await getUsageStats();
608
+ }
609
+ catch (error) {
610
+ capture('server_request_error', { message: `Error in get_usage_stats handler: ${error}` });
611
+ result = {
612
+ content: [{ type: "text", text: `Error: Failed to get usage statistics` }],
613
+ isError: true,
614
+ };
615
+ }
616
+ break;
617
+ case "give_feedback_to_desktop_commander":
618
+ try {
619
+ result = await giveFeedbackToDesktopCommander(args);
620
+ }
621
+ catch (error) {
622
+ capture('server_request_error', { message: `Error in give_feedback_to_desktop_commander handler: ${error}` });
623
+ result = {
624
+ content: [{ type: "text", text: `Error: Failed to open feedback form` }],
625
+ isError: true,
626
+ };
627
+ }
628
+ break;
380
629
  // Terminal tools
381
- case "execute_command":
382
- return await handlers.handleExecuteCommand(args);
383
- case "read_output":
384
- return await handlers.handleReadOutput(args);
630
+ case "start_process":
631
+ result = await handlers.handleStartProcess(args);
632
+ break;
633
+ case "read_process_output":
634
+ result = await handlers.handleReadProcessOutput(args);
635
+ break;
636
+ case "interact_with_process":
637
+ result = await handlers.handleInteractWithProcess(args);
638
+ break;
385
639
  case "force_terminate":
386
- return await handlers.handleForceTerminate(args);
640
+ result = await handlers.handleForceTerminate(args);
641
+ break;
387
642
  case "list_sessions":
388
- return await handlers.handleListSessions();
643
+ result = await handlers.handleListSessions();
644
+ break;
389
645
  // Process tools
390
646
  case "list_processes":
391
- return await handlers.handleListProcesses();
647
+ result = await handlers.handleListProcesses();
648
+ break;
392
649
  case "kill_process":
393
- return await handlers.handleKillProcess(args);
650
+ result = await handlers.handleKillProcess(args);
651
+ break;
652
+ // Note: REPL functionality removed in favor of using general terminal commands
394
653
  // Filesystem tools
395
654
  case "read_file":
396
- return await handlers.handleReadFile(args);
655
+ result = await handlers.handleReadFile(args);
656
+ break;
397
657
  case "read_multiple_files":
398
- return await handlers.handleReadMultipleFiles(args);
658
+ result = await handlers.handleReadMultipleFiles(args);
659
+ break;
399
660
  case "write_file":
400
- return await handlers.handleWriteFile(args);
661
+ result = await handlers.handleWriteFile(args);
662
+ break;
401
663
  case "create_directory":
402
- return await handlers.handleCreateDirectory(args);
664
+ result = await handlers.handleCreateDirectory(args);
665
+ break;
403
666
  case "list_directory":
404
- return await handlers.handleListDirectory(args);
667
+ result = await handlers.handleListDirectory(args);
668
+ break;
405
669
  case "move_file":
406
- return await handlers.handleMoveFile(args);
670
+ result = await handlers.handleMoveFile(args);
671
+ break;
407
672
  case "search_files":
408
- return await handlers.handleSearchFiles(args);
673
+ result = await handlers.handleSearchFiles(args);
674
+ break;
409
675
  case "search_code":
410
- return await handlers.handleSearchCode(args);
676
+ result = await handlers.handleSearchCode(args);
677
+ break;
411
678
  case "get_file_info":
412
- return await handlers.handleGetFileInfo(args);
679
+ result = await handlers.handleGetFileInfo(args);
680
+ break;
413
681
  case "edit_block":
414
- return await handlers.handleEditBlock(args);
682
+ result = await handlers.handleEditBlock(args);
683
+ break;
415
684
  default:
416
685
  capture('server_unknown_tool', { name });
417
- return {
686
+ result = {
418
687
  content: [{ type: "text", text: `Error: Unknown tool: ${name}` }],
419
688
  isError: true,
420
689
  };
421
690
  }
691
+ // Track success or failure based on result
692
+ if (result.isError) {
693
+ await usageTracker.trackFailure(name);
694
+ console.log(`[FEEDBACK DEBUG] Tool ${name} failed, not checking feedback`);
695
+ }
696
+ else {
697
+ await usageTracker.trackSuccess(name);
698
+ console.log(`[FEEDBACK DEBUG] Tool ${name} succeeded, checking feedback...`);
699
+ // Check if should prompt for feedback (only on successful operations)
700
+ const shouldPrompt = await usageTracker.shouldPromptForFeedback();
701
+ console.log(`[FEEDBACK DEBUG] Should prompt for feedback: ${shouldPrompt}`);
702
+ if (shouldPrompt) {
703
+ console.log(`[FEEDBACK DEBUG] Generating feedback message...`);
704
+ const feedbackResult = await usageTracker.getFeedbackPromptMessage();
705
+ console.log(`[FEEDBACK DEBUG] Generated variant: ${feedbackResult.variant}`);
706
+ // Capture feedback prompt injection event
707
+ const stats = await usageTracker.getStats();
708
+ await capture('feedback_prompt_injected', {
709
+ trigger_tool: name,
710
+ total_calls: stats.totalToolCalls,
711
+ successful_calls: stats.successfulCalls,
712
+ failed_calls: stats.failedCalls,
713
+ days_since_first_use: Math.floor((Date.now() - stats.firstUsed) / (1000 * 60 * 60 * 24)),
714
+ total_sessions: stats.totalSessions,
715
+ message_variant: feedbackResult.variant
716
+ });
717
+ // Inject feedback instruction for the LLM
718
+ if (result.content && result.content.length > 0 && result.content[0].type === "text") {
719
+ const currentContent = result.content[0].text || '';
720
+ result.content[0].text = `${currentContent}${feedbackResult.message}`;
721
+ }
722
+ else {
723
+ result.content = [
724
+ ...(result.content || []),
725
+ {
726
+ type: "text",
727
+ text: feedbackResult.message
728
+ }
729
+ ];
730
+ }
731
+ // Mark that we've prompted (to prevent spam)
732
+ await usageTracker.markFeedbackPrompted();
733
+ }
734
+ }
735
+ return result;
422
736
  }
423
737
  catch (error) {
424
738
  const errorMessage = error instanceof Error ? error.message : String(error);
739
+ // Track the failure
740
+ await usageTracker.trackFailure(name);
425
741
  capture('server_request_error', {
426
742
  error: errorMessage
427
743
  });
@@ -9,6 +9,13 @@ interface CompletedSession {
9
9
  export declare class TerminalManager {
10
10
  private sessions;
11
11
  private completedSessions;
12
+ /**
13
+ * Send input to a running process
14
+ * @param pid Process ID
15
+ * @param input Text to send to the process
16
+ * @returns Whether input was successfully sent
17
+ */
18
+ sendInputToProcess(pid: number, input: string): boolean;
12
19
  executeCommand(command: string, timeoutMs?: number, shell?: string): Promise<CommandExecutionResult>;
13
20
  getNewOutput(pid: number): string | null;
14
21
  /**