diff --git a/Dockerfile b/Dockerfile index 3dd7f577..b7c09ff4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile FROM node:lts-alpine +ENV MCP_CLIENT_DOCKER=true + # Create app directory WORKDIR /usr/src/app diff --git a/package.json b/package.json index 658c48bf..0ed07b8d 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "setup": "npm install && npm run build && node setup-claude-server.js", "setup:debug": "npm install && npm run build && node setup-claude-server.js --debug", "prepare": "npm run build", + "clean": "shx rm -rf dist", "test": "node test/run-all-tests.js", "test:debug": "node --inspect test/run-all-tests.js", "link:local": "npm run build && npm link", diff --git a/src/config-manager.ts b/src/config-manager.ts index 777c690c..3bad1164 100644 --- a/src/config-manager.ts +++ b/src/config-manager.ts @@ -122,7 +122,7 @@ class ConfigManager { "cipher", // Encrypt/decrypt files or wipe data "takeown" // Take ownership of files ], - defaultShell: os.platform() === 'win32' ? 'powershell.exe' : 'bash', + defaultShell: os.platform() === 'win32' ? 'powershell.exe' : '/bin/sh', allowedDirectories: [], telemetryEnabled: true, // Default to opt-out approach (telemetry on by default) fileWriteLineLimit: 50, // Default line limit for file write operations (changed from 100) diff --git a/src/tools/config.ts b/src/tools/config.ts index e594f250..fa572320 100644 --- a/src/tools/config.ts +++ b/src/tools/config.ts @@ -12,6 +12,17 @@ export async function getConfig() { // Add system information to the config response const systemInfo = getSystemInfo(); + + // Determine host client + let hostClientIdentifier: string | undefined = 'unknown'; + if (process.env.CURSOR_TRACE_ID) { + hostClientIdentifier = 'cursor'; + } else if (process.env.CLAUDE_MCP_TOKEN) { + hostClientIdentifier = 'claude'; + } else if (process.env.MCP_CLIENT_DOCKER === 'true') { + hostClientIdentifier = 'docker'; + } + const configWithSystemInfo = { ...config, systemInfo: { @@ -22,6 +33,7 @@ export async function getConfig() { isWindows: systemInfo.isWindows, isMacOS: systemInfo.isMacOS, isLinux: systemInfo.isLinux, + hostClient: hostClientIdentifier, examplePaths: systemInfo.examplePaths } }; diff --git a/src/tools/improved-process-tools.ts b/src/tools/improved-process-tools.ts index 47a1b1d7..20f6e38c 100644 --- a/src/tools/improved-process-tools.ts +++ b/src/tools/improved-process-tools.ts @@ -5,6 +5,8 @@ import { capture } from "../utils/capture.js"; import { ServerResult } from '../types.js'; import { analyzeProcessState, cleanProcessOutput, formatProcessStateMessage, ProcessState } from '../utils/process-detection.js'; import { getSystemInfo } from '../utils/system-info.js'; +import * as os from 'os'; +import { configManager } from '../config-manager.js'; /** * Start a new process (renamed from execute_command) @@ -40,10 +42,28 @@ export async function startProcess(args: unknown): Promise { }; } + let shellUsed: string | undefined = parsed.data.shell; + + if (!shellUsed) { + const config = await configManager.getConfig(); + if (config.defaultShell) { + shellUsed = config.defaultShell; + } else { + const isWindows = os.platform() === 'win32'; + if (isWindows && process.env.COMSPEC) { + shellUsed = process.env.COMSPEC; + } else if (!isWindows && process.env.SHELL) { + shellUsed = process.env.SHELL; + } else { + shellUsed = isWindows ? 'cmd.exe' : '/bin/sh'; + } + } + } + const result = await terminalManager.executeCommand( parsed.data.command, parsed.data.timeout_ms, - parsed.data.shell + shellUsed ); if (result.pid === -1) { @@ -56,10 +76,6 @@ export async function startProcess(args: unknown): Promise { // Analyze the process state to detect if it's waiting for input const processState = analyzeProcessState(result.output, result.pid); - // Get system info for shell information - const systemInfo = getSystemInfo(); - const shellUsed = parsed.data.shell || systemInfo.defaultShell; - let statusMessage = ''; if (processState.isWaitingForInput) { statusMessage = `\nšŸ”„ ${formatProcessStateMessage(processState, result.pid)}`; diff --git a/src/utils/capture.ts b/src/utils/capture.ts index 588bc138..4130b17b 100644 --- a/src/utils/capture.ts +++ b/src/utils/capture.ts @@ -1,4 +1,5 @@ import {platform} from 'os'; + import {randomUUID} from 'crypto'; import * as https from 'https'; import {configManager} from '../config-manager.js'; @@ -124,13 +125,26 @@ export const captureBase = async (captureURL: string, event: string, properties? } // Prepare standard properties - const baseProperties = { + interface BaseProperties { + timestamp: string; + platform: NodeJS.Platform; + app_version: string; + engagement_time_msec: string; + host_client?: string; + } + + const baseProperties: BaseProperties = { timestamp: new Date().toISOString(), platform: platform(), app_version: VERSION, engagement_time_msec: "100" }; + const hostClient = await configManager.getValue('hostClient'); + if (hostClient) { + (baseProperties as any).host_client = hostClient; + } + // Combine with sanitized properties const eventProperties = { ...baseProperties, diff --git a/test/test-enhanced-repl.js b/test/test-enhanced-repl.js index 937dc0cc..9867abf2 100644 --- a/test/test-enhanced-repl.js +++ b/test/test-enhanced-repl.js @@ -1,17 +1,43 @@ import assert from 'assert'; +import { execSync } from 'child_process'; import { startProcess, readProcessOutput, forceTerminate, interactWithProcess } from '../dist/tools/improved-process-tools.js'; +/** + * Determines the correct python command to use + * @returns {string} 'python3' or 'python' + */ +function getPythonCommand() { + try { + // Prefer python3 if available + execSync('command -v python3', { stdio: 'ignore' }); + return 'python3'; + } catch (e) { + // Fallback to python + try { + execSync('command -v python', { stdio: 'ignore' }); + return 'python'; + } catch (error) { + throw new Error('Neither python3 nor python command is available in the PATH'); + } + } +} + + /** * Test enhanced REPL functionality */ async function testEnhancedREPL() { console.log('Testing enhanced REPL functionality...'); + const pythonCommand = getPythonCommand(); + console.log(`Using python command: ${pythonCommand}`); + // Start Python in interactive mode console.log('Starting Python REPL...'); const result = await startProcess({ - command: 'python -i', - timeout_ms: 10000 + command: `${pythonCommand} -i`, + timeout_ms: 10000, + shell: '/bin/bash' }); console.log('Result from start_process:', result); diff --git a/test/test-repl-tools.js b/test/test-repl-tools.js.obsolete similarity index 100% rename from test/test-repl-tools.js rename to test/test-repl-tools.js.obsolete diff --git a/test/test_improved_search_truncation.js b/test/test_improved_search_truncation.js index c48998fd..acc87be3 100644 --- a/test/test_improved_search_truncation.js +++ b/test/test_improved_search_truncation.js @@ -7,7 +7,7 @@ async function testImprovedSearchTruncation() { // Test search that will produce many results to trigger truncation const searchArgs = { - path: '/Users/eduardruzga/work/ClaudeServerCommander', + path: '.', pattern: '.', // Match almost every line - this should be a lot of results maxResults: 50000, // Very high limit to get lots of results, but capped at 5000 ignoreCase: true diff --git a/test/test_output/node_repl_debug.txt b/test/test_output/node_repl_debug.txt index 8bfffea1..cf503055 100644 --- a/test/test_output/node_repl_debug.txt +++ b/test/test_output/node_repl_debug.txt @@ -1,16 +1,16 @@ Starting Node.js REPL... Waiting for Node.js startup... -[STDOUT] Welcome to Node.js v23.8.0. +[STDOUT] Welcome to Node.js v22.15.0. Type ".help" for more information. [STDOUT] > -Initial output buffer: Welcome to Node.js v23.8.0. +Initial output buffer: Welcome to Node.js v22.15.0. Type ".help" for more information. >  Sending simple command... [STDOUT] Hello from Node.js! [STDOUT] undefined [STDOUT] > -Output after first command: Welcome to Node.js v23.8.0. +Output after first command: Welcome to Node.js v22.15.0. Type ".help" for more information. > Hello from Node.js! undefined @@ -35,12 +35,12 @@ for (let i = 0; i < 3; i++) { [STDOUT] ... [STDOUT] ... [STDOUT] Hello, User 0! -[STDOUT] Hello, User 1! -Hello, User 2! +[STDOUT] Hello, User 1! +[STDOUT] Hello, User 2! [STDOUT] undefined [STDOUT] > [STDOUT] > -Final output buffer: Welcome to Node.js v23.8.0. +Final output buffer: Welcome to Node.js v22.15.0. Type ".help" for more information. > Hello from Node.js! undefined diff --git a/test/test_output/repl_test_output.txt b/test/test_output/repl_test_output.txt index d3d7acdc..85990e9a 100644 --- a/test/test_output/repl_test_output.txt +++ b/test/test_output/repl_test_output.txt @@ -1,15 +1,15 @@ Python REPL output: -Python 3.11.0 (main, Jan 19 2024, 19:53:39) [Clang 15.0.0 (clang-1500.0.40.1)] on darwin +Python 3.13.3 (main, Apr 8 2025, 13:54:08) [Clang 16.0.0 (clang-1600.0.26.6)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> STARTING PYTHON TEST ->>> REPL_TEST_VALUE: 52 +>>> REPL_TEST_VALUE: 44 >>> Node.js REPL output: -Welcome to Node.js v23.8.0. +Welcome to Node.js v22.15.0. Type ".help" for more information. > STARTING NODE TEST undefined -> NODE_REPL_TEST_VALUE: 81 +> NODE_REPL_TEST_VALUE: 54 undefined > \ No newline at end of file diff --git a/test/test_search_truncation.js b/test/test_search_truncation.js index 98f2319d..949ab10b 100644 --- a/test/test_search_truncation.js +++ b/test/test_search_truncation.js @@ -7,7 +7,7 @@ async function testSearchTruncation() { // Test search that will produce many results const searchArgs = { - path: '/Users/eduardruzga/work/ClaudeServerCommander', + path: '.', pattern: 'function|const|let|var', // This should match many lines maxResults: 50000, // Very high limit to get lots of results ignoreCase: true