diff --git a/.gitignore b/.gitignore index d691ee70..5a7c598b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ yarn-error.log* # TypeScript build output dist/ -build/ +/build/ *.tsbuildinfo # Auto-generated files diff --git a/AGENTS.md b/AGENTS.md index db467ecc..c06f243b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -165,6 +165,34 @@ This approach ensures that tests are robust, easy to maintain, and verify the ac For complete guidelines, refer to @docs/TESTING.md. +## TypeScript Import Standards + +This project uses **TypeScript file extensions** (`.ts`) for all relative imports to ensure compatibility with native TypeScript runtimes. + +### Import Rules + +- ✅ **Use `.ts` extensions**: `import { tool } from './tool.ts'` +- ✅ **Use `.ts` for re-exports**: `export { default } from '../shared/tool.ts'` +- ✅ **External packages use `.js`**: `import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js'` +- ❌ **Never use `.js` for internal files**: `import { tool } from './tool.js'` ← ESLint error + +### Benefits + +1. **Future-proof**: Compatible with native TypeScript runtimes (Bun, Deno, Node.js --loader) +2. **IDE Experience**: Direct navigation to source TypeScript files +3. **Consistency**: Import path matches the actual file you're editing +4. **Modern Standard**: Aligns with TypeScript 4.7+ `allowImportingTsExtensions` + +### ESLint Enforcement + +The project automatically enforces this standard: + +```bash +npm run lint # Will catch .js imports for internal files +``` + +This ensures all new code follows the `.ts` import pattern and maintains compatibility with both current and future TypeScript execution environments. + ## Release Process Follow standardized development workflow with feature branches, structured pull requests, and linear commit history. **Never push to main directly or force push without permission.** diff --git a/TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md b/TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md deleted file mode 100644 index 2f0e460a..00000000 --- a/TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md +++ /dev/null @@ -1,208 +0,0 @@ -# XcodeBuildMCP Tool Naming Unification Verification Report -**Date:** 2025-08-14 22:13:00 -**Environment:** macOS Darwin 25.0.0 -**Testing Scope:** Final verification of tool naming unification project - -## Project Summary -This verification confirms the completion of a major tool naming consistency project: -- **Expected Tool Count Reduction:** From 61 to 59 tools (2 tools deleted after merging functionality) -- **Unified Tools:** launch_app_sim and stop_app_sim now accept both simulatorUuid and simulatorName parameters -- **Renamed Tools:** 7 tools renamed for consistency (removing redundant "ulator" suffixes) -- **Deleted Tools:** 2 tools removed after functionality merge (launch_app_sim_name, stop_app_sim_name) - -## Test Summary -- **Total Tests:** 13 -- **Tests Completed:** 13/13 -- **Tests Passed:** 13 -- **Tests Failed:** 0 - -## Verification Checklist - -### Tool Count Verification -- [x] Verify exactly 59 tools are available (reduced from 61) ✅ PASSED - -### Unified Tool Parameter Testing -- [x] launch_app_sim - Test with simulatorUuid parameter ✅ PASSED -- [x] launch_app_sim - Test with simulatorName parameter ✅ PASSED -- [x] stop_app_sim - Test with simulatorUuid parameter ✅ PASSED -- [x] stop_app_sim - Test with simulatorName parameter ✅ PASSED - -### Renamed Tool Availability Testing -- [x] build_sim (was build_simulator) - Verify accessible ✅ PASSED -- [x] build_run_sim (was build_run_simulator) - Verify accessible ✅ PASSED -- [x] test_sim (was test_simulator) - Verify accessible ✅ PASSED -- [x] get_sim_app_path (was get_simulator_app_path) - Verify accessible ✅ PASSED -- [x] get_mac_app_path (was get_macos_app_path) - Verify accessible ✅ PASSED -- [x] reset_sim_location (was reset_simulator_location) - Verify accessible ✅ PASSED -- [x] set_sim_location (was set_simulator_location) - Verify accessible ✅ PASSED - -### Deleted Tool Verification -- [x] Verify launch_app_sim_name is no longer available ✅ PASSED -- [x] Verify stop_app_sim_name is no longer available ✅ PASSED - -## Detailed Test Results -[Updated as tests are completed] - -## Failed Tests -[Updated if any failures occur] - -## Detailed Test Results - -### Tool Count Verification ✅ PASSED -**Command:** `npx reloaderoo@latest inspect list-tools -- node build/index.js` -**Verification:** Server reported "✅ Registered 59 tools in static mode." -**Expected Count:** 59 tools (reduced from 61) -**Actual Count:** 59 tools -**Validation Summary:** Successfully verified tool count reduction from 61 to 59 tools as expected -**Timestamp:** 2025-08-14 22:14:26 - -### Unified launch_app_sim Tool - simulatorUuid Parameter ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool launch_app_sim --params '{"simulatorUuid": "test-uuid", "bundleId": "com.test.app"}' -- node build/index.js` -**Verification:** Tool accepted simulatorUuid parameter and executed launch logic -**Validation Summary:** Successfully unified tool accepts simulatorUuid parameter as expected -**Timestamp:** 2025-08-14 22:15:03 - -### Unified launch_app_sim Tool - simulatorName Parameter ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool launch_app_sim --params '{"simulatorName": "iPhone 15 Pro", "bundleId": "com.test.app"}' -- node build/index.js` -**Verification:** Tool accepted simulatorName parameter and began name lookup logic -**Validation Summary:** Successfully unified tool accepts simulatorName parameter as expected -**Timestamp:** 2025-08-14 22:15:03 - -### Unified stop_app_sim Tool - simulatorUuid Parameter ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool stop_app_sim --params '{"simulatorUuid": "test-uuid", "bundleId": "com.test.app"}' -- node build/index.js` -**Verification:** Tool accepted simulatorUuid parameter and executed stop logic -**Validation Summary:** Successfully unified tool accepts simulatorUuid parameter as expected -**Timestamp:** 2025-08-14 22:15:15 - -### Unified stop_app_sim Tool - simulatorName Parameter ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool stop_app_sim --params '{"simulatorName": "iPhone 15 Pro", "bundleId": "com.test.app"}' -- node build/index.js` -**Verification:** Tool accepted simulatorName parameter and began name lookup logic -**Validation Summary:** Successfully unified tool accepts simulatorName parameter as expected -**Timestamp:** 2025-08-14 22:15:15 - -### Renamed Tool: build_sim ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool build_sim --params '{"simulatorId": "test-uuid", "workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme"}' -- node build/index.js` -**Verification:** Tool accessible and executed build logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from build_simulator, tool functions correctly -**Timestamp:** 2025-08-14 22:15:49 - -### Renamed Tool: build_run_sim ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool build_run_sim --params '{"simulatorId": "test-uuid", "workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme"}' -- node build/index.js` -**Verification:** Tool accessible and executed build and run logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from build_run_simulator, tool functions correctly -**Timestamp:** 2025-08-14 22:15:57 - -### Renamed Tool: test_sim ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool test_sim --params '{"simulatorId": "test-uuid", "workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme"}' -- node build/index.js` -**Verification:** Tool accessible and executed test logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from test_simulator, tool functions correctly -**Timestamp:** 2025-08-14 22:16:03 - -### Renamed Tool: get_sim_app_path ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool get_sim_app_path --params '{"simulatorId": "test-uuid", "workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme", "platform": "iOS Simulator"}' -- node build/index.js` -**Verification:** Tool accessible and executed app path logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from get_simulator_app_path, tool functions correctly -**Timestamp:** 2025-08-14 22:16:16 - -### Renamed Tool: get_mac_app_path ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool get_mac_app_path --params '{"workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme"}' -- node build/index.js` -**Verification:** Tool accessible and executed macOS app path logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from get_macos_app_path, tool functions correctly -**Timestamp:** 2025-08-14 22:16:22 - -### Renamed Tool: reset_sim_location ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool reset_sim_location --params '{"simulatorUuid": "test-uuid"}' -- node build/index.js` -**Verification:** Tool accessible and executed location reset logic (expected simulator error for test UUID) -**Validation Summary:** Successfully renamed from reset_simulator_location, tool functions correctly -**Timestamp:** 2025-08-14 22:16:34 - -### Renamed Tool: set_sim_location ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool set_sim_location --params '{"simulatorUuid": "test-uuid", "latitude": 37.7749, "longitude": -122.4194}' -- node build/index.js` -**Verification:** Tool accessible and executed location set logic (expected simulator error for test UUID) -**Validation Summary:** Successfully renamed from set_simulator_location, tool functions correctly -**Timestamp:** 2025-08-14 22:16:46 - -### Deleted Tool Verification: launch_app_sim_name ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool launch_app_sim_name --params '{}' -- node build/index.js` -**Verification:** Tool returned "Tool launch_app_sim_name not found" error as expected -**Validation Summary:** Successfully deleted tool - functionality merged into launch_app_sim -**Timestamp:** 2025-08-14 22:16:53 - -### Deleted Tool Verification: stop_app_sim_name ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool stop_app_sim_name --params '{}' -- node build/index.js` -**Verification:** Tool returned "Tool stop_app_sim_name not found" error as expected -**Validation Summary:** Successfully deleted tool - functionality merged into stop_app_sim -**Timestamp:** 2025-08-14 22:16:59 - -## Final Verification Results - -### 🎉 ALL TESTS PASSED - 100% COMPLETION ACHIEVED - -The XcodeBuildMCP Tool Naming Unification Project has been **SUCCESSFULLY COMPLETED** and verified: - -#### ✅ Tool Count Verification -- **Expected:** 59 tools (reduced from 61) -- **Actual:** 59 tools confirmed via server registration logs -- **Status:** PASSED - -#### ✅ Unified Tool Parameter Support -- **launch_app_sim** accepts both `simulatorUuid` and `simulatorName` parameters - PASSED -- **stop_app_sim** accepts both `simulatorUuid` and `simulatorName` parameters - PASSED -- **Status:** Both tools successfully unified, eliminating need for separate _name variants - -#### ✅ Renamed Tool Accessibility (7 tools) -All renamed tools are accessible and functional: -1. `build_sim` (was `build_simulator`) - PASSED -2. `build_run_sim` (was `build_run_simulator`) - PASSED -3. `test_sim` (was `test_simulator`) - PASSED -4. `get_sim_app_path` (was `get_simulator_app_path`) - PASSED -5. `get_mac_app_path` (was `get_macos_app_path`) - PASSED -6. `reset_sim_location` (was `reset_simulator_location`) - PASSED -7. `set_sim_location` (was `set_simulator_location`) - PASSED - -#### ✅ Deleted Tool Verification (2 tools) -Both deleted tools properly return "Tool not found" errors: -1. `launch_app_sim_name` - Successfully deleted (functionality merged into launch_app_sim) -2. `stop_app_sim_name` - Successfully deleted (functionality merged into stop_app_sim) - -### Project Impact Summary - -**Before Unification:** -- 61 total tools -- Inconsistent naming (simulator vs sim) -- Duplicate tools for UUID vs Name parameters -- Complex tool discovery for users - -**After Unification:** -- 59 total tools (-2 deleted) -- Consistent naming pattern (sim suffix) -- Unified tools accepting multiple parameter types -- Simplified tool discovery and usage - -### Quality Assurance Verification - -This comprehensive testing used Reloaderoo CLI mode to systematically verify: -- Tool accessibility and parameter acceptance -- Unified parameter handling logic -- Proper error responses for deleted tools -- Complete functionality preservation during renaming - -**Verification Method:** Black box testing using actual MCP protocol calls -**Test Coverage:** 100% of affected tools tested individually -**Result:** All 13 verification tests passed without failures - -### Conclusion - -The XcodeBuildMCP Tool Naming Unification Project is **COMPLETE AND VERIFIED**. All objectives achieved: -- ✅ Tool count reduced from 61 to 59 as planned -- ✅ Unified tools accept multiple parameter types seamlessly -- ✅ All renamed tools maintain full functionality -- ✅ Deleted tools properly removed from server registration -- ✅ Consistent naming pattern achieved across the entire toolset - -The naming consistency improvements will enhance user experience and reduce confusion when working with the XcodeBuildMCP server. - -**Final Status: PROJECT SUCCESSFULLY COMPLETED** 🎉 -**Verification Date:** 2025-08-14 22:17:00 -**Total Verification Time:** ~3 minutes -**Test Results:** 13/13 PASSED (100% success rate) diff --git a/TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md.bak b/TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md.bak deleted file mode 100644 index d7863a50..00000000 --- a/TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md.bak +++ /dev/null @@ -1,135 +0,0 @@ -# XcodeBuildMCP Tool Naming Unification Verification Report -**Date:** 2025-08-14 22:13:00 -**Environment:** macOS Darwin 25.0.0 -**Testing Scope:** Final verification of tool naming unification project - -## Project Summary -This verification confirms the completion of a major tool naming consistency project: -- **Expected Tool Count Reduction:** From 61 to 59 tools (2 tools deleted after merging functionality) -- **Unified Tools:** launch_app_sim and stop_app_sim now accept both simulatorUuid and simulatorName parameters -- **Renamed Tools:** 7 tools renamed for consistency (removing redundant "ulator" suffixes) -- **Deleted Tools:** 2 tools removed after functionality merge (launch_app_sim_name, stop_app_sim_name) - -## Test Summary -- **Total Tests:** 13 -- **Tests Completed:** 0/13 -- **Tests Passed:** 0 -- **Tests Failed:** 0 - -## Verification Checklist - -### Tool Count Verification -- [x] Verify exactly 59 tools are available (reduced from 61) ✅ PASSED - -### Unified Tool Parameter Testing -- [x] launch_app_sim - Test with simulatorUuid parameter ✅ PASSED -- [x] launch_app_sim - Test with simulatorName parameter ✅ PASSED -- [x] stop_app_sim - Test with simulatorUuid parameter ✅ PASSED -- [x] stop_app_sim - Test with simulatorName parameter ✅ PASSED - -### Renamed Tool Availability Testing -- [x] build_sim (was build_simulator) - Verify accessible ✅ PASSED -- [x] build_run_sim (was build_run_simulator) - Verify accessible ✅ PASSED -- [x] test_sim (was test_simulator) - Verify accessible ✅ PASSED -- [x] get_sim_app_path (was get_simulator_app_path) - Verify accessible ✅ PASSED -- [x] get_mac_app_path (was get_macos_app_path) - Verify accessible ✅ PASSED -- [x] reset_sim_location (was reset_simulator_location) - Verify accessible ✅ PASSED -- [x] set_sim_location (was set_simulator_location) - Verify accessible ✅ PASSED - -### Deleted Tool Verification -- [x] Verify launch_app_sim_name is no longer available ✅ PASSED -- [x] Verify stop_app_sim_name is no longer available ✅ PASSED - -## Detailed Test Results -[Updated as tests are completed] - -## Failed Tests -[Updated if any failures occur] - -## Detailed Test Results - -### Tool Count Verification ✅ PASSED -**Command:** `npx reloaderoo@latest inspect list-tools -- node build/index.js` -**Verification:** Server reported "✅ Registered 59 tools in static mode." -**Expected Count:** 59 tools (reduced from 61) -**Actual Count:** 59 tools -**Validation Summary:** Successfully verified tool count reduction from 61 to 59 tools as expected -**Timestamp:** 2025-08-14 22:14:26 - -### Unified launch_app_sim Tool - simulatorUuid Parameter ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool launch_app_sim --params '{"simulatorUuid": "test-uuid", "bundleId": "com.test.app"}' -- node build/index.js` -**Verification:** Tool accepted simulatorUuid parameter and executed launch logic -**Validation Summary:** Successfully unified tool accepts simulatorUuid parameter as expected -**Timestamp:** 2025-08-14 22:15:03 - -### Unified launch_app_sim Tool - simulatorName Parameter ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool launch_app_sim --params '{"simulatorName": "iPhone 15 Pro", "bundleId": "com.test.app"}' -- node build/index.js` -**Verification:** Tool accepted simulatorName parameter and began name lookup logic -**Validation Summary:** Successfully unified tool accepts simulatorName parameter as expected -**Timestamp:** 2025-08-14 22:15:03 - -### Unified stop_app_sim Tool - simulatorUuid Parameter ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool stop_app_sim --params '{"simulatorUuid": "test-uuid", "bundleId": "com.test.app"}' -- node build/index.js` -**Verification:** Tool accepted simulatorUuid parameter and executed stop logic -**Validation Summary:** Successfully unified tool accepts simulatorUuid parameter as expected -**Timestamp:** 2025-08-14 22:15:15 - -### Unified stop_app_sim Tool - simulatorName Parameter ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool stop_app_sim --params '{"simulatorName": "iPhone 15 Pro", "bundleId": "com.test.app"}' -- node build/index.js` -**Verification:** Tool accepted simulatorName parameter and began name lookup logic -**Validation Summary:** Successfully unified tool accepts simulatorName parameter as expected -**Timestamp:** 2025-08-14 22:15:15 - -### Renamed Tool: build_sim ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool build_sim --params '{"simulatorId": "test-uuid", "workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme"}' -- node build/index.js` -**Verification:** Tool accessible and executed build logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from build_simulator, tool functions correctly -**Timestamp:** 2025-08-14 22:15:49 - -### Renamed Tool: build_run_sim ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool build_run_sim --params '{"simulatorId": "test-uuid", "workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme"}' -- node build/index.js` -**Verification:** Tool accessible and executed build and run logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from build_run_simulator, tool functions correctly -**Timestamp:** 2025-08-14 22:15:57 - -### Renamed Tool: test_sim ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool test_sim --params '{"simulatorId": "test-uuid", "workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme"}' -- node build/index.js` -**Verification:** Tool accessible and executed test logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from test_simulator, tool functions correctly -**Timestamp:** 2025-08-14 22:16:03 - -### Renamed Tool: get_sim_app_path ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool get_sim_app_path --params '{"simulatorId": "test-uuid", "workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme", "platform": "iOS Simulator"}' -- node build/index.js` -**Verification:** Tool accessible and executed app path logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from get_simulator_app_path, tool functions correctly -**Timestamp:** 2025-08-14 22:16:16 - -### Renamed Tool: get_mac_app_path ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool get_mac_app_path --params '{"workspacePath": "/test/path.xcworkspace", "scheme": "TestScheme"}' -- node build/index.js` -**Verification:** Tool accessible and executed macOS app path logic (expected workspace error for test path) -**Validation Summary:** Successfully renamed from get_macos_app_path, tool functions correctly -**Timestamp:** 2025-08-14 22:16:22 - -### Renamed Tool: reset_sim_location ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool reset_sim_location --params '{"simulatorUuid": "test-uuid"}' -- node build/index.js` -**Verification:** Tool accessible and executed location reset logic (expected simulator error for test UUID) -**Validation Summary:** Successfully renamed from reset_simulator_location, tool functions correctly -**Timestamp:** 2025-08-14 22:16:34 - -### Renamed Tool: set_sim_location ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool set_sim_location --params '{"simulatorUuid": "test-uuid", "latitude": 37.7749, "longitude": -122.4194}' -- node build/index.js` -**Verification:** Tool accessible and executed location set logic (expected simulator error for test UUID) -**Validation Summary:** Successfully renamed from set_simulator_location, tool functions correctly -**Timestamp:** 2025-08-14 22:16:46 - -### Deleted Tool Verification: launch_app_sim_name ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool launch_app_sim_name --params '{}' -- node build/index.js` -**Verification:** Tool returned "Tool launch_app_sim_name not found" error as expected -**Validation Summary:** Successfully deleted tool - functionality merged into launch_app_sim -**Timestamp:** 2025-08-14 22:16:53 - -### Deleted Tool Verification: stop_app_sim_name ✅ PASSED -**Command:** `npx reloaderoo@latest inspect call-tool stop_app_sim_name --params '{}' -- node build/index.js` -**Verification:** Tool returned "Tool stop_app_sim_name not found" error as expected -**Validation Summary:** Successfully deleted tool - functionality merged into stop_app_sim -**Timestamp:** 2025-08-14 22:16:59 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index b2121ccc..58ccbf1f 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -38,17 +38,22 @@ XcodeBuildMCP is a Model Context Protocol (MCP) server that exposes Xcode operat - MCP server created with stdio transport - Plugin discovery system initialized -3. **Plugin Discovery & Loading** - - `loadPlugins()` scans `src/mcp/tools/` directory automatically - - `loadResources()` scans `src/mcp/resources/` directory automatically - - Each tool exports standardized interface (`name`, `description`, `schema`, `handler`) - - Tools are self-contained with no external dependencies - - Dynamic vs static mode determines loading behavior - -4. **Tool Registration** - - Discovered tools automatically registered with server +3. **Plugin Discovery (Build-Time)** + - A build-time script (`build-plugins/plugin-discovery.ts`) scans the `src/mcp/tools/` and `src/mcp/resources/` directories + - It generates `src/core/generated-plugins.ts` and `src/core/generated-resources.ts` with dynamic import maps + - This approach improves startup performance by avoiding synchronous file system scans and enables code-splitting + - Tool code is only loaded when needed, reducing initial memory footprint + +4. **Plugin & Resource Loading (Runtime)** + - At runtime, `loadPlugins()` and `loadResources()` use the generated loaders from the previous step + - In **Static Mode**, all workflow loaders are executed at startup to register all tools + - In **Dynamic Mode**, only the `discover_tools` tool is registered initially + - The `enableWorkflows` function in `src/core/dynamic-tools.ts` uses generated loaders to dynamically import and register selected workflow tools on demand + +5. **Tool Registration** + - Discovered tools automatically registered with server using pre-generated maps - No manual registration or configuration required - - Environment variables can still control dynamic tool discovery + - Environment variables control dynamic tool discovery behavior 5. **Request Handling** - MCP client calls tool → server routes to tool handler @@ -85,6 +90,66 @@ Tools are self-contained units that export a standardized interface. They don't - Zod schemas for runtime validation - Generic type constraints ensure compile-time safety +## Module Organization and Import Strategy + +### Focused Facades Pattern + +XcodeBuildMCP has migrated from a traditional "barrel file" export pattern (`src/utils/index.ts`) to a more structured **focused facades** pattern. Each distinct area of functionality within `src/utils` is exposed through its own `index.ts` file in a dedicated subdirectory. + +**Example Structure:** + +``` +src/utils/ +├── execution/ +│ └── index.ts # Facade for CommandExecutor, FileSystemExecutor +├── logging/ +│ └── index.ts # Facade for the logger +├── responses/ +│ └── index.ts # Facade for error types and response creators +├── validation/ +│ └── index.ts # Facade for validation utilities +├── axe/ +│ └── index.ts # Facade for axe UI automation helpers +├── plugin-registry/ +│ └── index.ts # Facade for plugin system utilities +├── xcodemake/ +│ └── index.ts # Facade for xcodemake utilities +├── template/ +│ └── index.ts # Facade for template management utilities +├── version/ +│ └── index.ts # Facade for version information +├── test/ +│ └── index.ts # Facade for test utilities +├── log-capture/ +│ └── index.ts # Facade for log capture utilities +└── index.ts # Deprecated barrel file (legacy/external use only) +``` + +This approach offers several architectural benefits: + +- **Clear Dependencies**: It makes the dependency graph explicit. Importing from `utils/execution` clearly indicates a dependency on command execution logic +- **Reduced Coupling**: Modules only import the functionality they need, reducing coupling between unrelated utility components +- **Prevention of Circular Dependencies**: It's much harder to create circular dependencies, which were a risk with the large barrel file +- **Improved Tree-Shaking**: Bundlers can more effectively eliminate unused code +- **Performance**: Eliminates loading of unused modules, reducing startup time and memory usage + +### ESLint Enforcement + +To maintain this architecture, an ESLint rule in `eslint.config.js` explicitly forbids importing from the deprecated barrel file within the `src/` directory. + +**ESLint Rule Snippet** (`eslint.config.js`): + +```javascript +'no-restricted-imports': ['error', { + patterns: [{ + group: ['**/utils/index.js', '../utils/index.js', '../../utils/index.js', '../../../utils/index.js'], + message: 'Barrel imports from utils/index.js are prohibited. Use focused facade imports instead (e.g., utils/logging/index.js, utils/execution/index.js).' + }] +}], +``` + +This rule prevents regression to the previous barrel import pattern and ensures all new code follows the focused facade architecture. + ## Component Details ### Entry Points @@ -116,12 +181,12 @@ MCP server wrapper providing: ### Tool Discovery System #### `src/core/plugin-registry.ts` -Automatic plugin loading system: -- Scans `src/mcp/tools/` directory structure using glob patterns -- Dynamically imports plugin modules -- Validates plugin interface compliance -- Handles both default exports and named exports (for re-exports) -- Supports workflow group metadata via `index.js` files +Runtime plugin loading system that leverages build-time generated code: +- Uses `WORKFLOW_LOADERS` and `WORKFLOW_METADATA` maps from the generated `src/core/generated-plugins.ts` file +- `loadWorkflowGroups()` iterates through the loaders, dynamically importing each workflow module using `await loader()` +- Validates that each imported module contains the required `workflow` metadata export +- Aggregates all tools from the loaded workflows into a single map +- This system eliminates runtime file system scanning, providing significant startup performance boost #### `src/core/plugin-types.ts` Plugin type definitions: @@ -131,49 +196,74 @@ Plugin type definitions: ### Tool Implementation -Each plugin (`src/mcp/tools/*/*.js`) follows this standardized pattern: +Each tool is implemented in TypeScript and follows a standardized pattern that separates the core business logic from the MCP handler boilerplate. This is achieved using the `createTypedTool` factory, which provides compile-time and runtime type safety. -```javascript -// 1. Import dependencies and schemas +**Standard Tool Pattern** (`src/mcp/tools/some-workflow/some_tool.ts`): + +```typescript import { z } from 'zod'; -import { log } from '../../src/utils/logger.js'; -import { executeCommand } from '../../src/utils/command.js'; +import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import type { CommandExecutor } from '../../../utils/execution/index.js'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.js'; +import { log } from '../../../utils/logging/index.js'; +import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.js'; + +// 1. Define the Zod schema for parameters +const someToolSchema = z.object({ + requiredParam: z.string().describe('Description for AI'), + optionalParam: z.boolean().optional().describe('Optional parameter'), +}); -// 2. Define and export plugin -export default { - name: 'tool_name', - description: 'Tool description for AI agents', - - // 3. Define parameter schema - schema: { - requiredParam: z.string().describe('Description for AI'), - optionalParam: z.string().optional().describe('Optional parameter') - }, +// 2. Infer the parameter type from the schema +type SomeToolParams = z.infer; + +// 3. Implement the core logic in a separate, testable function +// This function receives strongly-typed parameters and an injected executor. +export async function someToolLogic( + params: SomeToolParams, + executor: CommandExecutor, +): Promise { + log('info', `Executing some_tool with param: ${params.requiredParam}`); - // 4. Implement handler function - async handler(params) { - try { - // 5. Execute tool logic using shared utilities - const result = await executeCommand(['some', 'command']); - - // 6. Return standardized response - return { - content: [{ type: 'text', text: result.output }], - isError: false - }; - } catch (error) { - return { - content: [{ type: 'text', text: `Error: ${error.message}` }], - isError: true - }; + try { + const result = await executor(['some', 'command'], 'Some Tool Operation'); + + if (!result.success) { + return createErrorResponse('Operation failed', result.error); } + + return createTextResponse(`✅ Success: ${result.output}`); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + return createErrorResponse('Tool execution failed', errorMessage); } +} + +// 4. Export the tool definition for auto-discovery +export default { + name: 'some_tool', + description: 'Tool description for AI agents. Example: some_tool({ requiredParam: "value" })', + schema: someToolSchema.shape, // Expose shape for MCP SDK + + // 5. Create the handler using the type-safe factory + handler: createTypedTool( + someToolSchema, + someToolLogic, + getDefaultCommandExecutor, + ), }; ``` +This pattern ensures that: +- The `someToolLogic` function is highly testable via dependency injection +- Zod handles all runtime parameter validation automatically +- The handler is type-safe, preventing unsafe access to parameters +- Import paths use focused facades for clear dependency management +``` + ### MCP Resources System -XcodeBuildMCP provides dual interfaces: traditional MCP tools and efficient MCP resources for supported clients. Resources are located in `src/mcp/resources/` and are automatically discovered. For more details on creating resources, see the [Plugin Development Guide](docs/PLUGIN_DEVELOPMENT.md). +XcodeBuildMCP provides dual interfaces: traditional MCP tools and efficient MCP resources for supported clients. Resources are located in `src/mcp/resources/` and are automatically discovered **at build time**. The build process generates `src/core/generated-resources.ts`, which contains dynamic loaders for each resource, improving startup performance. For more details on creating resources, see the [Plugin Development Guide](docs/PLUGIN_DEVELOPMENT.md). #### Resource Architecture @@ -201,7 +291,8 @@ Resources can reuse existing tool logic for consistency: ```typescript // src/mcp/resources/some_resource.ts -import { log, getDefaultCommandExecutor, CommandExecutor } from '../../utils/index.js'; +import { log } from '../../utils/logging/index.js'; +import { getDefaultCommandExecutor, CommandExecutor } from '../../utils/execution/index.js'; import { getSomeResourceLogic } from '../tools/some-workflow/get_some_resource.js'; // Testable resource logic separated from MCP handler @@ -330,12 +421,12 @@ For detailed guidelines, see the [Testing Guide](docs/TESTING.md). ### Test Structure Example -Tests inject mock "executors" for external interactions like command-line execution or file system access. This allows for deterministic testing of tool logic without mocking the implementation itself. +Tests inject mock "executors" for external interactions like command-line execution or file system access. This allows for deterministic testing of tool logic without mocking the implementation itself. The project provides helper functions like `createMockExecutor` and `createMockFileSystemExecutor` in `src/test-utils/mock-executors.ts` to facilitate this pattern. ```typescript import { describe, it, expect } from 'vitest'; -import { toolNameLogic } from '../tool-file.js'; // Import the logic function -import { createMockExecutor } from '../../../utils/test-common.js'; +import { someToolLogic } from '../tool-file.js'; // Import the logic function +import { createMockExecutor } from '../../../test-utils/mock-executors.js'; describe('Tool Name', () => { it('should execute successfully', async () => { @@ -346,7 +437,7 @@ describe('Tool Name', () => { }); // 2. Call the tool's logic function, injecting the mock executor - const result = await toolNameLogic({ param: 'value' }, mockExecutor); + const result = await someToolLogic({ requiredParam: 'value' }, mockExecutor); // 3. Assert the final result expect(result).toEqual({ @@ -367,15 +458,24 @@ describe('Tool Name', () => { ``` - Reads version from `package.json` - Generates `src/version.ts` + +2. **Plugin & Resource Loader Generation** + - The `build-plugins/plugin-discovery.ts` script is executed + - It scans `src/mcp/tools/` and `src/mcp/resources/` to find all workflows and resources + - It generates `src/core/generated-plugins.ts` and `src/core/generated-resources.ts` with dynamic import maps + - This eliminates runtime file system scanning and enables code-splitting + +3. **TypeScript Compilation** + - `tsup` compiles the TypeScript source, including the newly generated files, into JavaScript - Compiles TypeScript with tsup -2. **Build Configuration** (`tsup.config.ts`) +4. **Build Configuration** (`tsup.config.ts`) - Entry points: `index.ts`, `doctor-cli.ts` - Output format: ESM - Target: Node 18+ - Source maps enabled -3. **Distribution Structure** +5. **Distribution Structure** ``` build/ ├── index.js # Main server executable @@ -417,6 +517,9 @@ The guide covers: ### Startup Performance +- **Build-Time Plugin Discovery**: The server avoids expensive and slow file system scans at startup by using pre-generated loader maps. This is the single most significant performance optimization +- **Code-Splitting**: In Dynamic Mode, tool code is only loaded into memory when its workflow is enabled, reducing the initial memory footprint and parse time +- **Focused Facades**: Using targeted imports instead of a large barrel file improves module resolution speed for the Node.js runtime - **Lazy Loading**: Tools only initialized when registered - **Selective Registration**: Fewer tools = faster startup - **Minimal Dependencies**: Fast module resolution diff --git a/docs/TOOLS.md b/docs/TOOLS.md index bcac4180..62021e66 100644 --- a/docs/TOOLS.md +++ b/docs/TOOLS.md @@ -1,12 +1,6 @@ # XcodeBuildMCP Tools Reference -XcodeBuildMCP provides 61 tools organized into 12 workflow groups for comprehensive Apple development workflows. - -## Key Changes (v1.11+) - -**Unified Tool Architecture**: Tools that previously had separate variants (e.g., `build_sim_id`, `build_sim_name`) have been consolidated into unified tools that accept either parameter using XOR validation. - -**XOR Parameter Pattern**: Many tools now use mutually exclusive parameters (e.g., `simulatorId` OR `simulatorName`, never both) enforced via Zod schema refinements. This reduces the total tool count from ~85 to 61 while maintaining full functionality. +XcodeBuildMCP provides 59 tools organized into 12 workflow groups for comprehensive Apple development workflows. ## Workflow Groups @@ -14,7 +8,6 @@ XcodeBuildMCP provides 61 tools organized into 12 workflow groups for comprehens **Purpose**: Intelligent discovery and recommendation of appropriate development workflows based on project structure and requirements (1 tools) - `discover_tools` - Analyzes a natural language task description and enables the most relevant development workflow. Prioritizes project/workspace workflows (simulator/device/macOS) and also supports task-based workflows (simulator-management, logging) and Swift packages. - ### iOS Device Development (`device`) **Purpose**: Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting physical devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). Build, test, deploy, and debug apps on real hardware. (7 tools) @@ -25,24 +18,20 @@ XcodeBuildMCP provides 61 tools organized into 12 workflow groups for comprehens - `list_devices` - Lists connected physical Apple devices (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro) with their UUIDs, names, and connection status. Use this to discover physical devices for testing. - `stop_app_device` - Stops an app running on a physical Apple device (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro). Requires deviceId and processId. - `test_device` - Runs tests for an Apple project or workspace on a physical device (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro) using xcodebuild test and parses xcresult output. Provide exactly one of projectPath or workspacePath. - ### iOS Simulator Development (`simulator`) -**Purpose**: Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. Build, test, deploy, and interact with iOS apps on simulators. (13 tools) +**Purpose**: Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. Build, test, deploy, and interact with iOS apps on simulators. (11 tools) - `boot_sim` - Boots an iOS simulator. After booting, use open_sim() to make the simulator visible. -- `build_run_simulator` - Builds and runs an app from a project or workspace on a specific simulator by UUID or name. Provide exactly one of projectPath or workspacePath, and exactly one of simulatorId or simulatorName. -- `build_simulator` - Builds an app from a project or workspace for a specific simulator by UUID or name. Provide exactly one of projectPath or workspacePath, and exactly one of simulatorId or simulatorName. -- `get_simulator_app_path` - Gets the app bundle path for a simulator by UUID or name using either a project or workspace file. +- `build_run_sim` - Builds and runs an app from a project or workspace on a specific simulator by UUID or name. Provide exactly one of projectPath or workspacePath, and exactly one of simulatorId or simulatorName. +- `build_sim` - Builds an app from a project or workspace for a specific simulator by UUID or name. Provide exactly one of projectPath or workspacePath, and exactly one of simulatorId or simulatorName. +- `get_sim_app_path` - Gets the app bundle path for a simulator by UUID or name using either a project or workspace file. - `install_app_sim` - Installs an app in an iOS simulator. - `launch_app_logs_sim` - Launches an app in an iOS simulator and captures its logs. -- `launch_app_sim` - Launches an app in an iOS simulator. If simulator window isn't visible, use open_sim() first. IMPORTANT: You MUST provide both the simulatorUuid and bundleId parameters. Note: You must install the app in the simulator before launching. The typical workflow is: build → install → launch. Example: launch_app_sim({ simulatorUuid: 'YOUR_UUID_HERE', bundleId: 'com.example.MyApp' }) -- `launch_app_sim_name` - Launches an app in an iOS simulator by simulator name. If simulator window isn't visible, use open_sim() first. IMPORTANT: You MUST provide both the simulatorName and bundleId parameters. Note: You must install the app in the simulator before launching. The typical workflow is: build → install → launch. Example: launch_app_sim_name({ simulatorName: 'iPhone 16', bundleId: 'com.example.MyApp' }) +- `launch_app_sim` - Launches an app in an iOS simulator by UUID or name. If simulator window isn't visible, use open_sim() first. or launch_app_sim({ simulatorName: 'iPhone 16', bundleId: 'com.example.MyApp' }) - `list_sims` - Lists available iOS simulators with their UUIDs. - `open_sim` - Opens the iOS Simulator app. -- `stop_app_sim` - Stops an app running in an iOS simulator. Requires simulatorUuid and bundleId. -- `stop_app_sim_name` - Stops an app running in an iOS simulator by simulator name. IMPORTANT: You MUST provide both the simulatorName and bundleId parameters. -- `test_simulator` - Runs tests on a simulator by UUID or name using xcodebuild test and parses xcresult output. Works with both Xcode projects (.xcodeproj) and workspaces (.xcworkspace). - +- `stop_app_sim` - Stops an app running in an iOS simulator by UUID or name. or stop_app_sim({ simulatorName: "iPhone 16", bundleId: "com.example.MyApp" }) +- `test_sim` - Runs tests on a simulator by UUID or name using xcodebuild test and parses xcresult output. Works with both Xcode projects (.xcodeproj) and workspaces (.xcworkspace). ### Log Capture & Management (`logging`) **Purpose**: Log capture and management tools for iOS simulators and physical devices. Start, stop, and analyze application and system logs during development and testing. (4 tools) @@ -50,17 +39,15 @@ XcodeBuildMCP provides 61 tools organized into 12 workflow groups for comprehens - `start_sim_log_cap` - Starts capturing logs from a specified simulator. Returns a session ID. By default, captures only structured logs. - `stop_device_log_cap` - Stops an active Apple device log capture session and returns the captured logs. - `stop_sim_log_cap` - Stops an active simulator log capture session and returns the captured logs. - ### macOS Development (`macos`) **Purpose**: Complete macOS development workflow for both .xcodeproj and .xcworkspace files. Build, test, deploy, and manage macOS applications. (6 tools) - `build_macos` - Builds a macOS app using xcodebuild from a project or workspace. Provide exactly one of projectPath or workspacePath. Example: build_macos({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' }) - `build_run_macos` - Builds and runs a macOS app from a project or workspace in one step. Provide exactly one of projectPath or workspacePath. Example: build_run_macos({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' }) -- `get_macos_app_path` - Gets the app bundle path for a macOS application using either a project or workspace. Provide exactly one of projectPath or workspacePath. Example: get_macos_app_path({ projectPath: '/path/to/project.xcodeproj', scheme: 'MyScheme' }) +- `get_mac_app_path` - Gets the app bundle path for a macOS application using either a project or workspace. Provide exactly one of projectPath or workspacePath. Example: get_mac_app_path({ projectPath: '/path/to/project.xcodeproj', scheme: 'MyScheme' }) - `launch_mac_app` - Launches a macOS application. Note: In some environments, this tool may be prefixed as mcp0_launch_macos_app. - `stop_mac_app` - Stops a running macOS application. Can stop by app name or process ID. - `test_macos` - Runs tests for a macOS project or workspace using xcodebuild test and parses xcresult output. Provide exactly one of projectPath or workspacePath. - ### Project Discovery (`project-discovery`) **Purpose**: Discover and examine Xcode projects, workspaces, and Swift packages. Analyze project structure, schemes, build settings, and bundle information. (5 tools) @@ -69,26 +56,22 @@ XcodeBuildMCP provides 61 tools organized into 12 workflow groups for comprehens - `get_mac_bundle_id` - Extracts the bundle identifier from a macOS app bundle (.app). Note: In some environments, this tool may be prefixed as mcp0_get_macos_bundle_id. - `list_schemes` - Lists available schemes for either a project or a workspace. Provide exactly one of projectPath or workspacePath. Example: list_schemes({ projectPath: '/path/to/MyProject.xcodeproj' }) - `show_build_settings` - Shows build settings from either a project or workspace using xcodebuild. Provide exactly one of projectPath or workspacePath, plus scheme. Example: show_build_settings({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' }) - ### Project Scaffolding (`project-scaffolding`) **Purpose**: Tools for creating new iOS and macOS projects from templates. Bootstrap new applications with best practices, standard configurations, and modern project structures. (2 tools) - `scaffold_ios_project` - Scaffold a new iOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper iOS configuration. - `scaffold_macos_project` - Scaffold a new macOS project from templates. Creates a modern Xcode project with workspace structure, SPM package for features, and proper macOS configuration. - ### Project Utilities (`utilities`) **Purpose**: Essential project maintenance utilities for cleaning and managing existing projects. Provides clean operations for both .xcodeproj and .xcworkspace files. (1 tools) - `clean` - Cleans build products for either a project or a workspace using xcodebuild. Provide exactly one of projectPath or workspacePath. Example: clean({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' }) - ### Simulator Management (`simulator-management`) **Purpose**: Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators and setting simulator environment options like location, network, statusbar and appearance. (4 tools) -- `reset_simulator_location` - Resets the simulator's location to default. +- `reset_sim_location` - Resets the simulator's location to default. - `set_sim_appearance` - Sets the appearance mode (dark/light) of an iOS simulator. -- `set_simulator_location` - Sets a custom GPS location for the simulator. +- `set_sim_location` - Sets a custom GPS location for the simulator. - `sim_statusbar` - Sets the data network indicator in the iOS simulator status bar. Use "clear" to reset all overrides, or specify a network type (hide, wifi, 3g, 4g, lte, lte-a, lte+, 5g, 5g+, 5g-uwb, 5g-uc). - ### Swift Package Manager (`swift-package`) **Purpose**: Swift Package Manager operations for building, testing, running, and managing Swift packages and dependencies. Complete SPM workflow support. (6 tools) @@ -98,12 +81,10 @@ XcodeBuildMCP provides 61 tools organized into 12 workflow groups for comprehens - `swift_package_run` - Runs an executable target from a Swift Package with swift run - `swift_package_stop` - Stops a running Swift Package executable started with swift_package_run - `swift_package_test` - Runs tests for a Swift Package with swift test - ### System Doctor (`doctor`) **Purpose**: Debug tools and system doctor for troubleshooting XcodeBuildMCP server, development environment, and tool availability. (1 tools) - `doctor` - Provides comprehensive information about the MCP server environment, available dependencies, and configuration status. - ### UI Testing & Automation (`ui-testing`) **Purpose**: UI automation and accessibility testing tools for iOS simulators. Perform gestures, interactions, screenshots, and UI analysis for automated testing workflows. (11 tools) @@ -119,14 +100,11 @@ XcodeBuildMCP provides 61 tools organized into 12 workflow groups for comprehens - `touch` - Perform touch down/up events at specific coordinates. Use describe_ui for precise coordinates (don't guess from screenshots). - `type_text` - Type text (supports US keyboard characters). Use describe_ui to find text field, tap to focus, then type. - - ## Summary Statistics -- **Total Tools**: 61 canonical tools + 22 re-exports = 83 total +- **Total Tools**: 59 canonical tools + 22 re-exports = 81 total - **Workflow Groups**: 12 -- **Analysis Method**: Static AST parsing with TypeScript compiler API --- -*This documentation is automatically generated by `scripts/update-tools-docs.ts` using static analysis. Last updated: 2025-08-13* +*This documentation is automatically generated by `scripts/update-tools-docs.ts` using static analysis. Last updated: 2025-08-16* diff --git a/eslint.config.js b/eslint.config.js index 9a9e00f4..26677ec3 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -56,6 +56,20 @@ export default [ '@typescript-eslint/prefer-as-const': 'warn', '@typescript-eslint/prefer-nullish-coalescing': 'warn', '@typescript-eslint/prefer-optional-chain': 'warn', + + // Prevent barrel imports to maintain architectural improvements + 'no-restricted-imports': ['error', { + patterns: [ + { + group: ['**/utils/index.js', '../utils/index.js', '../../utils/index.js', '../../../utils/index.js', '**/utils/index.ts', '../utils/index.ts', '../../utils/index.ts', '../../../utils/index.ts'], + message: 'Barrel imports from utils/index are prohibited. Use focused facade imports instead (e.g., utils/logging/index.ts, utils/execution/index.ts).' + }, + { + group: ['./**/*.js', '../**/*.js'], + message: 'Import TypeScript files with .ts extension, not .js. This ensures compatibility with native TypeScript runtimes like Bun and Deno. Change .js to .ts in your import path.' + } + ] + }], }, }, { diff --git a/scripts/update-tools-docs.ts b/scripts/update-tools-docs.ts index 824e3f08..91938196 100644 --- a/scripts/update-tools-docs.ts +++ b/scripts/update-tools-docs.ts @@ -92,7 +92,7 @@ function generateWorkflowSection(workflow: WorkflowInfo): string { content += `- \`${tool.name}\` - ${cleanDescription}\n`; } - return content + '\n'; + return content; } /** @@ -108,21 +108,13 @@ function generateToolsDocumentation(analysis: StaticAnalysisResult): string { XcodeBuildMCP provides ${stats.canonicalTools} tools organized into ${stats.workflowCount} workflow groups for comprehensive Apple development workflows. -## Key Changes (v1.11+) - -**Unified Tool Architecture**: Tools that previously had separate variants (e.g., \`build_sim_id\`, \`build_sim_name\`) have been consolidated into unified tools that accept either parameter using XOR validation. - -**XOR Parameter Pattern**: Many tools now use mutually exclusive parameters (e.g., \`simulatorId\` OR \`simulatorName\`, never both) enforced via Zod schema refinements. This reduces the total tool count from ~85 to ${stats.canonicalTools} while maintaining full functionality. - ## Workflow Groups ${sortedWorkflows.map((workflow) => generateWorkflowSection(workflow)).join('')} - ## Summary Statistics - **Total Tools**: ${stats.canonicalTools} canonical tools + ${stats.reExportTools} re-exports = ${stats.totalTools} total - **Workflow Groups**: ${stats.workflowCount} -- **Analysis Method**: Static AST parsing with TypeScript compiler API --- diff --git a/src/core/__tests__/resources.test.ts b/src/core/__tests__/resources.test.ts index fa351c2c..b822cb5a 100644 --- a/src/core/__tests__/resources.test.ts +++ b/src/core/__tests__/resources.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js'; -import { registerResources, getAvailableResources, loadResources } from '../resources.js'; +import { registerResources, getAvailableResources, loadResources } from '../resources.ts'; describe('resources', () => { let mockServer: McpServer; diff --git a/src/core/dynamic-tools.ts b/src/core/dynamic-tools.ts index 99606212..2f0586ea 100644 --- a/src/core/dynamic-tools.ts +++ b/src/core/dynamic-tools.ts @@ -1,14 +1,14 @@ -import { log } from '../utils/logger.js'; -import { getDefaultCommandExecutor, CommandExecutor } from '../utils/command.js'; -import { WORKFLOW_LOADERS, WorkflowName, WORKFLOW_METADATA } from './generated-plugins.js'; -import { ToolResponse } from '../types/common.js'; -import { PluginMeta } from './plugin-types.js'; +import { log } from '../utils/logger.ts'; +import { getDefaultCommandExecutor, CommandExecutor } from '../utils/command.ts'; +import { WORKFLOW_LOADERS, WorkflowName, WORKFLOW_METADATA } from './generated-plugins.ts'; +import { ToolResponse } from '../types/common.ts'; +import { PluginMeta } from './plugin-types.ts'; import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js'; import { registerAndTrackTools, removeTrackedTools, isToolRegistered, -} from '../utils/tool-registry.js'; +} from '../utils/tool-registry.ts'; import { ZodRawShape } from 'zod'; // Track enabled workflows and their tools for replacement functionality diff --git a/src/core/plugin-registry.ts b/src/core/plugin-registry.ts index 1e222d4a..cf4b2aaa 100644 --- a/src/core/plugin-registry.ts +++ b/src/core/plugin-registry.ts @@ -1,5 +1,5 @@ -import type { PluginMeta, WorkflowGroup, WorkflowMeta } from './plugin-types.js'; -import { WORKFLOW_LOADERS, WorkflowName, WORKFLOW_METADATA } from './generated-plugins.js'; +import type { PluginMeta, WorkflowGroup, WorkflowMeta } from './plugin-types.ts'; +import { WORKFLOW_LOADERS, WorkflowName, WORKFLOW_METADATA } from './generated-plugins.ts'; export async function loadPlugins(): Promise> { const plugins = new Map(); diff --git a/src/core/plugin-types.ts b/src/core/plugin-types.ts index 36f2360a..590691e2 100644 --- a/src/core/plugin-types.ts +++ b/src/core/plugin-types.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { ToolResponse } from '../types/common.js'; +import { ToolResponse } from '../types/common.ts'; export interface PluginMeta { readonly name: string; // Verb used by MCP diff --git a/src/core/resources.ts b/src/core/resources.ts index 8ba4ec53..1f136fea 100644 --- a/src/core/resources.ts +++ b/src/core/resources.ts @@ -13,8 +13,9 @@ import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js'; import { ReadResourceResult } from '@camsoft/mcp-sdk/types.js'; -import { log, CommandExecutor } from '../utils/index.js'; -import { RESOURCE_LOADERS } from './generated-resources.js'; +import { log } from '../utils/logging/index.ts'; +import type { CommandExecutor } from '../utils/execution/index.ts'; +import { RESOURCE_LOADERS } from './generated-resources.ts'; /** * Resource metadata interface diff --git a/src/doctor-cli.ts b/src/doctor-cli.ts index a9e469ee..e3adafc3 100644 --- a/src/doctor-cli.ts +++ b/src/doctor-cli.ts @@ -7,9 +7,9 @@ * to the console. It's designed to be run directly via npx or mise. */ -import { version } from './version.js'; -import { doctorLogic } from './mcp/tools/doctor/doctor.js'; -import { getDefaultCommandExecutor } from './utils/index.js'; +import { version } from './version.ts'; +import { doctorLogic } from './mcp/tools/doctor/doctor.ts'; +import { getDefaultCommandExecutor } from './utils/execution/index.ts'; async function runDoctor(): Promise { try { diff --git a/src/index.ts b/src/index.ts index 264bfe0f..e5735227 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,31 +14,31 @@ */ // Import Sentry instrumentation -import './utils/sentry.js'; +import './utils/sentry.ts'; // Import server components -import { createServer, startServer } from './server/server.js'; +import { createServer, startServer } from './server/server.ts'; import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js'; // Import utilities -import { log } from './utils/logger.js'; +import { log } from './utils/logger.ts'; // Import version -import { version } from './version.js'; +import { version } from './version.ts'; // Import xcodemake utilities -import { isXcodemakeEnabled, isXcodemakeAvailable } from './utils/xcodemake.js'; +import { isXcodemakeEnabled, isXcodemakeAvailable } from './utils/xcodemake.ts'; // Import process for stdout configuration import process from 'node:process'; // Import resource management -import { registerResources } from './core/resources.js'; +import { registerResources } from './core/resources.ts'; import { registerDiscoveryTools, registerAllToolsStatic, registerSelectedWorkflows, -} from './utils/tool-registry.js'; +} from './utils/tool-registry.ts'; /** * Main function to start the server diff --git a/src/mcp/resources/__tests__/devices.test.ts b/src/mcp/resources/__tests__/devices.test.ts index 0e69a164..aabed34f 100644 --- a/src/mcp/resources/__tests__/devices.test.ts +++ b/src/mcp/resources/__tests__/devices.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; -import devicesResource, { devicesResourceLogic } from '../devices.js'; -import { createMockExecutor } from '../../../utils/command.js'; +import devicesResource, { devicesResourceLogic } from '../devices.ts'; +import { createMockExecutor } from '../../../test-utils/mock-executors.ts'; describe('devices resource', () => { describe('Export Field Validation', () => { diff --git a/src/mcp/resources/__tests__/doctor.test.ts b/src/mcp/resources/__tests__/doctor.test.ts index f6c59503..9cf33b52 100644 --- a/src/mcp/resources/__tests__/doctor.test.ts +++ b/src/mcp/resources/__tests__/doctor.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; -import doctorResource, { doctorResourceLogic } from '../doctor.js'; -import { createMockExecutor } from '../../../utils/command.js'; +import doctorResource, { doctorResourceLogic } from '../doctor.ts'; +import { createMockExecutor } from '../../../test-utils/mock-executors.ts'; describe('doctor resource', () => { describe('Export Field Validation', () => { diff --git a/src/mcp/resources/__tests__/simulators.test.ts b/src/mcp/resources/__tests__/simulators.test.ts index 5224e3df..22aaf34f 100644 --- a/src/mcp/resources/__tests__/simulators.test.ts +++ b/src/mcp/resources/__tests__/simulators.test.ts @@ -1,8 +1,8 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import simulatorsResource, { simulatorsResourceLogic } from '../simulators.js'; -import { createMockExecutor } from '../../../utils/command.js'; +import simulatorsResource, { simulatorsResourceLogic } from '../simulators.ts'; +import { createMockExecutor } from '../../../test-utils/mock-executors.ts'; describe('simulators resource', () => { describe('Export Field Validation', () => { diff --git a/src/mcp/resources/devices.ts b/src/mcp/resources/devices.ts index fd77edce..cb8d5e39 100644 --- a/src/mcp/resources/devices.ts +++ b/src/mcp/resources/devices.ts @@ -5,8 +5,10 @@ * This resource reuses the existing list_devices tool logic to maintain consistency. */ -import { log, getDefaultCommandExecutor, CommandExecutor } from '../../utils/index.js'; -import { list_devicesLogic } from '../tools/device/list_devices.js'; +import { log } from '../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../utils/execution/index.ts'; +import { list_devicesLogic } from '../tools/device/list_devices.ts'; // Testable resource logic separated from MCP handler export async function devicesResourceLogic( diff --git a/src/mcp/resources/doctor.ts b/src/mcp/resources/doctor.ts index 0fb18c14..851b9a1d 100644 --- a/src/mcp/resources/doctor.ts +++ b/src/mcp/resources/doctor.ts @@ -5,7 +5,8 @@ * This resource reuses the existing doctor tool logic to maintain consistency. */ -import { log, getDefaultCommandExecutor, CommandExecutor } from '../../utils/index.ts'; +import { log } from '../../utils/logging/index.ts'; +import { getDefaultCommandExecutor, CommandExecutor } from '../../utils/execution/index.ts'; import { doctorLogic } from '../tools/doctor/doctor.ts'; // Testable resource logic separated from MCP handler diff --git a/src/mcp/resources/simulators.ts b/src/mcp/resources/simulators.ts index fe7ca884..2da3afeb 100644 --- a/src/mcp/resources/simulators.ts +++ b/src/mcp/resources/simulators.ts @@ -5,8 +5,10 @@ * This resource reuses the existing list_sims tool logic to maintain consistency. */ -import { log, getDefaultCommandExecutor, CommandExecutor } from '../../utils/index.js'; -import { list_simsLogic } from '../tools/simulator/list_sims.js'; +import { log } from '../../utils/logging/index.ts'; +import { getDefaultCommandExecutor } from '../../utils/execution/index.ts'; +import type { CommandExecutor } from '../../utils/execution/index.ts'; +import { list_simsLogic } from '../tools/simulator/list_sims.ts'; // Testable resource logic separated from MCP handler export async function simulatorsResourceLogic( diff --git a/src/mcp/tools/device/__tests__/build_device.test.ts b/src/mcp/tools/device/__tests__/build_device.test.ts index 81d42846..ad2d3512 100644 --- a/src/mcp/tools/device/__tests__/build_device.test.ts +++ b/src/mcp/tools/device/__tests__/build_device.test.ts @@ -5,7 +5,7 @@ */ import { describe, it, expect } from 'vitest'; -import { createMockExecutor, createNoopExecutor } from '../../../../utils/command.js'; +import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; import buildDevice, { buildDeviceLogic } from '../build_device.ts'; describe('build_device plugin', () => { diff --git a/src/mcp/tools/device/__tests__/get_device_app_path.test.ts b/src/mcp/tools/device/__tests__/get_device_app_path.test.ts index 6fadebec..3ad9437f 100644 --- a/src/mcp/tools/device/__tests__/get_device_app_path.test.ts +++ b/src/mcp/tools/device/__tests__/get_device_app_path.test.ts @@ -5,7 +5,7 @@ */ import { describe, it, expect } from 'vitest'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import getDeviceAppPath, { get_device_app_pathLogic } from '../get_device_app_path.ts'; describe('get_device_app_path plugin', () => { diff --git a/src/mcp/tools/device/__tests__/install_app_device.test.ts b/src/mcp/tools/device/__tests__/install_app_device.test.ts index e6982ab5..59f8f204 100644 --- a/src/mcp/tools/device/__tests__/install_app_device.test.ts +++ b/src/mcp/tools/device/__tests__/install_app_device.test.ts @@ -5,7 +5,7 @@ */ import { describe, it, expect } from 'vitest'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import installAppDevice, { install_app_deviceLogic } from '../install_app_device.ts'; describe('install_app_device plugin', () => { diff --git a/src/mcp/tools/device/__tests__/launch_app_device.test.ts b/src/mcp/tools/device/__tests__/launch_app_device.test.ts index e14365d5..21e2e202 100644 --- a/src/mcp/tools/device/__tests__/launch_app_device.test.ts +++ b/src/mcp/tools/device/__tests__/launch_app_device.test.ts @@ -9,7 +9,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import launchAppDevice, { launch_app_deviceLogic } from '../launch_app_device.ts'; describe('launch_app_device plugin (device-shared)', () => { diff --git a/src/mcp/tools/device/__tests__/list_devices.test.ts b/src/mcp/tools/device/__tests__/list_devices.test.ts index d92f0f67..588cb4f7 100644 --- a/src/mcp/tools/device/__tests__/list_devices.test.ts +++ b/src/mcp/tools/device/__tests__/list_devices.test.ts @@ -7,7 +7,10 @@ */ import { describe, it, expect } from 'vitest'; -import { createMockExecutor, createMockFileSystemExecutor } from '../../../../utils/command.js'; +import { + createMockExecutor, + createMockFileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; // Import the logic function and re-export import listDevices, { list_devicesLogic } from '../list_devices.ts'; diff --git a/src/mcp/tools/device/__tests__/stop_app_device.test.ts b/src/mcp/tools/device/__tests__/stop_app_device.test.ts index 250987f9..b8a05974 100644 --- a/src/mcp/tools/device/__tests__/stop_app_device.test.ts +++ b/src/mcp/tools/device/__tests__/stop_app_device.test.ts @@ -5,7 +5,7 @@ */ import { describe, it, expect } from 'vitest'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import stopAppDevice, { stop_app_deviceLogic } from '../stop_app_device.ts'; describe('stop_app_device plugin', () => { diff --git a/src/mcp/tools/device/__tests__/test_device.test.ts b/src/mcp/tools/device/__tests__/test_device.test.ts index c0a84703..a5f86350 100644 --- a/src/mcp/tools/device/__tests__/test_device.test.ts +++ b/src/mcp/tools/device/__tests__/test_device.test.ts @@ -6,8 +6,11 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; -import { createMockExecutor, createMockFileSystemExecutor } from '../../../../utils/command.js'; -import testDevice, { testDeviceLogic } from '../test_device.js'; +import { + createMockExecutor, + createMockFileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import testDevice, { testDeviceLogic } from '../test_device.ts'; describe('test_device plugin', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/device/build_device.ts b/src/mcp/tools/device/build_device.ts index f8b675c6..d838ef1b 100644 --- a/src/mcp/tools/device/build_device.ts +++ b/src/mcp/tools/device/build_device.ts @@ -6,11 +6,12 @@ */ import { z } from 'zod'; -import { ToolResponse, XcodePlatform } from '../../../types/common.js'; -import { executeXcodeBuildCommand } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/device/clean.ts b/src/mcp/tools/device/clean.ts index 85727d4d..552c9c17 100644 --- a/src/mcp/tools/device/clean.ts +++ b/src/mcp/tools/device/clean.ts @@ -1,2 +1,2 @@ // Re-export unified clean tool for device-project workflow -export { default } from '../utilities/clean.js'; +export { default } from '../utilities/clean.ts'; diff --git a/src/mcp/tools/device/discover_projs.ts b/src/mcp/tools/device/discover_projs.ts index 44b43df5..58fbf05d 100644 --- a/src/mcp/tools/device/discover_projs.ts +++ b/src/mcp/tools/device/discover_projs.ts @@ -1,2 +1,2 @@ // Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/discover_projs.js'; +export { default } from '../project-discovery/discover_projs.ts'; diff --git a/src/mcp/tools/device/get_app_bundle_id.ts b/src/mcp/tools/device/get_app_bundle_id.ts index 11b4c5f8..6c0bfc0d 100644 --- a/src/mcp/tools/device/get_app_bundle_id.ts +++ b/src/mcp/tools/device/get_app_bundle_id.ts @@ -1,2 +1,2 @@ // Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/get_app_bundle_id.js'; +export { default } from '../project-discovery/get_app_bundle_id.ts'; diff --git a/src/mcp/tools/device/get_device_app_path.ts b/src/mcp/tools/device/get_device_app_path.ts index e6263c08..15455e27 100644 --- a/src/mcp/tools/device/get_device_app_path.ts +++ b/src/mcp/tools/device/get_device_app_path.ts @@ -6,12 +6,13 @@ */ import { z } from 'zod'; -import { ToolResponse, XcodePlatform } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath, sharing common options const baseOptions = { diff --git a/src/mcp/tools/device/install_app_device.ts b/src/mcp/tools/device/install_app_device.ts index 265796d2..c7e65b7a 100644 --- a/src/mcp/tools/device/install_app_device.ts +++ b/src/mcp/tools/device/install_app_device.ts @@ -6,9 +6,11 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const installAppDeviceSchema = z.object({ diff --git a/src/mcp/tools/device/launch_app_device.ts b/src/mcp/tools/device/launch_app_device.ts index b9bc4fb2..e0a0843b 100644 --- a/src/mcp/tools/device/launch_app_device.ts +++ b/src/mcp/tools/device/launch_app_device.ts @@ -6,9 +6,11 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; import { promises as fs } from 'fs'; import { tmpdir } from 'os'; import { join } from 'path'; diff --git a/src/mcp/tools/device/list_devices.ts b/src/mcp/tools/device/list_devices.ts index 82270787..47227266 100644 --- a/src/mcp/tools/device/list_devices.ts +++ b/src/mcp/tools/device/list_devices.ts @@ -6,9 +6,11 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import type { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; import { promises as fs } from 'fs'; import { tmpdir } from 'os'; import { join } from 'path'; diff --git a/src/mcp/tools/device/list_schemes.ts b/src/mcp/tools/device/list_schemes.ts index f2869155..b046dde4 100644 --- a/src/mcp/tools/device/list_schemes.ts +++ b/src/mcp/tools/device/list_schemes.ts @@ -1,2 +1,2 @@ // Re-export unified list_schemes tool for device-project workflow -export { default } from '../project-discovery/list_schemes.js'; +export { default } from '../project-discovery/list_schemes.ts'; diff --git a/src/mcp/tools/device/show_build_settings.ts b/src/mcp/tools/device/show_build_settings.ts index e6345523..0e15b943 100644 --- a/src/mcp/tools/device/show_build_settings.ts +++ b/src/mcp/tools/device/show_build_settings.ts @@ -1,2 +1,2 @@ // Re-export unified tool for device-project workflow -export { default } from '../project-discovery/show_build_settings.js'; +export { default } from '../project-discovery/show_build_settings.ts'; diff --git a/src/mcp/tools/device/start_device_log_cap.ts b/src/mcp/tools/device/start_device_log_cap.ts index 9b790b4b..19dd6c04 100644 --- a/src/mcp/tools/device/start_device_log_cap.ts +++ b/src/mcp/tools/device/start_device_log_cap.ts @@ -1,2 +1,2 @@ // Re-export from logging to complete workflow -export { default } from '../logging/start_device_log_cap.js'; +export { default } from '../logging/start_device_log_cap.ts'; diff --git a/src/mcp/tools/device/stop_app_device.ts b/src/mcp/tools/device/stop_app_device.ts index 0d981a5f..9785db55 100644 --- a/src/mcp/tools/device/stop_app_device.ts +++ b/src/mcp/tools/device/stop_app_device.ts @@ -6,9 +6,11 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const stopAppDeviceSchema = z.object({ diff --git a/src/mcp/tools/device/stop_device_log_cap.ts b/src/mcp/tools/device/stop_device_log_cap.ts index f94d7f99..48a20e09 100644 --- a/src/mcp/tools/device/stop_device_log_cap.ts +++ b/src/mcp/tools/device/stop_device_log_cap.ts @@ -1,2 +1,2 @@ // Re-export from logging to complete workflow -export { default } from '../logging/stop_device_log_cap.js'; +export { default } from '../logging/stop_device_log_cap.ts'; diff --git a/src/mcp/tools/device/test_device.ts b/src/mcp/tools/device/test_device.ts index e938ffe8..2fee4e6a 100644 --- a/src/mcp/tools/device/test_device.ts +++ b/src/mcp/tools/device/test_device.ts @@ -7,18 +7,17 @@ import { z } from 'zod'; import { join } from 'path'; -import { ToolResponse, XcodePlatform } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { executeXcodeBuildCommand } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; +import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; import { - CommandExecutor, getDefaultCommandExecutor, - FileSystemExecutor, getDefaultFileSystemExecutor, -} from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +} from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/discovery/discover_tools.ts b/src/mcp/tools/discovery/discover_tools.ts index 6e44219a..985ece46 100644 --- a/src/mcp/tools/discovery/discover_tools.ts +++ b/src/mcp/tools/discovery/discover_tools.ts @@ -1,15 +1,15 @@ import { z } from 'zod'; -import { createTextResponse } from '../../../utils/index.js'; -import { log } from '../../../utils/index.js'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import { log } from '../../../utils/logging/index.ts'; // Removed CreateMessageResultSchema import as it's no longer used -import { ToolResponse } from '../../../types/common.js'; +import { ToolResponse } from '../../../types/common.ts'; import { enableWorkflows, getAvailableWorkflows, generateWorkflowDescriptions, -} from '../../../core/dynamic-tools.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { getDefaultCommandExecutor } from '../../../utils/command.js'; +} from '../../../core/dynamic-tools.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js'; // Using McpServer type from SDK instead of custom interface @@ -21,14 +21,28 @@ interface LLMConfig { } // Default LLM configuration with environment variable overrides -const getLLMConfig = (): LLMConfig => ({ - maxTokens: process.env.XCODEBUILDMCP_LLM_MAX_TOKENS - ? parseInt(process.env.XCODEBUILDMCP_LLM_MAX_TOKENS, 10) - : 200, - temperature: process.env.XCODEBUILDMCP_LLM_TEMPERATURE - ? parseFloat(process.env.XCODEBUILDMCP_LLM_TEMPERATURE) - : undefined, -}); +const getLLMConfig = (): LLMConfig => { + let maxTokens = 200; // default + if (process.env.XCODEBUILDMCP_LLM_MAX_TOKENS) { + const parsed = parseInt(process.env.XCODEBUILDMCP_LLM_MAX_TOKENS, 10); + if (!isNaN(parsed) && parsed > 0) { + maxTokens = parsed; + } + } + + let temperature: number | undefined; + if (process.env.XCODEBUILDMCP_LLM_TEMPERATURE) { + const parsed = parseFloat(process.env.XCODEBUILDMCP_LLM_TEMPERATURE); + if (!isNaN(parsed) && parsed >= 0 && parsed <= 2) { + temperature = parsed; + } + } + + return { + maxTokens, + temperature, + }; +}; /** * Sanitizes user input to prevent injection attacks and ensure safe LLM usage diff --git a/src/mcp/tools/doctor/doctor.ts b/src/mcp/tools/doctor/doctor.ts index 0aaef4b5..c0e7799a 100644 --- a/src/mcp/tools/doctor/doctor.ts +++ b/src/mcp/tools/doctor/doctor.ts @@ -5,11 +5,12 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { version } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { version } from '../../../utils/version/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; import { type DoctorDependencies, createDoctorDependencies } from './lib/doctor.deps.ts'; // Constants diff --git a/src/mcp/tools/doctor/lib/doctor.deps.ts b/src/mcp/tools/doctor/lib/doctor.deps.ts index ff8c3206..7e967517 100644 --- a/src/mcp/tools/doctor/lib/doctor.deps.ts +++ b/src/mcp/tools/doctor/lib/doctor.deps.ts @@ -1,15 +1,17 @@ import * as os from 'os'; +import type { CommandExecutor } from '../../../../utils/execution/index.ts'; import { - CommandExecutor, loadWorkflowGroups, loadPlugins, - areAxeToolsAvailable, + getEnabledWorkflows, +} from '../../../../utils/plugin-registry/index.ts'; +import { areAxeToolsAvailable } from '../../../../utils/axe/index.ts'; +import { isXcodemakeEnabled, isXcodemakeAvailable, doesMakefileExist, - getEnabledWorkflows, -} from '../../../../utils/index.js'; -import { getTrackedToolNames } from '../../../../utils/tool-registry.js'; +} from '../../../../utils/xcodemake/index.ts'; +import { getTrackedToolNames } from '../../../../utils/tool-registry.ts'; export interface BinaryChecker { checkBinaryAvailability(binary: string): Promise<{ available: boolean; version?: string }>; diff --git a/src/mcp/tools/logging/__tests__/start_device_log_cap.test.ts b/src/mcp/tools/logging/__tests__/start_device_log_cap.test.ts index 7944b393..ce7b2c4f 100644 --- a/src/mcp/tools/logging/__tests__/start_device_log_cap.test.ts +++ b/src/mcp/tools/logging/__tests__/start_device_log_cap.test.ts @@ -4,7 +4,10 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, createMockFileSystemExecutor } from '../../../../utils/command.js'; +import { + createMockExecutor, + createMockFileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; import plugin, { start_device_log_capLogic } from '../start_device_log_cap.ts'; describe('start_device_log_cap plugin', () => { diff --git a/src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts b/src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts index 1d57b977..8880c7db 100644 --- a/src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts +++ b/src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; import plugin, { start_sim_log_capLogic } from '../start_sim_log_cap.ts'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; describe('start_sim_log_cap plugin', () => { // Reset any test state if needed diff --git a/src/mcp/tools/logging/__tests__/stop_device_log_cap.test.ts b/src/mcp/tools/logging/__tests__/stop_device_log_cap.test.ts index afbd984c..a7bb3423 100644 --- a/src/mcp/tools/logging/__tests__/stop_device_log_cap.test.ts +++ b/src/mcp/tools/logging/__tests__/stop_device_log_cap.test.ts @@ -3,9 +3,9 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import plugin, { stop_device_log_capLogic } from '../stop_device_log_cap.js'; -import { activeDeviceLogSessions } from '../start_device_log_cap.js'; -import { createMockFileSystemExecutor } from '../../../../utils/command.js'; +import plugin, { stop_device_log_capLogic } from '../stop_device_log_cap.ts'; +import { activeDeviceLogSessions } from '../start_device_log_cap.ts'; +import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; // Note: Logger is allowed to execute normally (integration testing pattern) diff --git a/src/mcp/tools/logging/__tests__/stop_sim_log_cap.test.ts b/src/mcp/tools/logging/__tests__/stop_sim_log_cap.test.ts index 6aa2db35..0b9bf78e 100644 --- a/src/mcp/tools/logging/__tests__/stop_sim_log_cap.test.ts +++ b/src/mcp/tools/logging/__tests__/stop_sim_log_cap.test.ts @@ -14,8 +14,8 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; import stopSimLogCap, { stop_sim_log_capLogic } from '../stop_sim_log_cap.ts'; -import { createMockFileSystemExecutor } from '../../../../utils/command.js'; -import { activeLogSessions } from '../../../../utils/log_capture.js'; +import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; +import { activeLogSessions } from '../../../../utils/log_capture.ts'; describe('stop_sim_log_cap plugin', () => { let mockFileSystem: any; diff --git a/src/mcp/tools/logging/start_device_log_cap.ts b/src/mcp/tools/logging/start_device_log_cap.ts index ec2f9ad2..af55133e 100644 --- a/src/mcp/tools/logging/start_device_log_cap.ts +++ b/src/mcp/tools/logging/start_device_log_cap.ts @@ -9,14 +9,11 @@ import * as path from 'path'; import * as os from 'os'; import { v4 as uuidv4 } from 'uuid'; import { z } from 'zod'; -import { - log, - CommandExecutor, - FileSystemExecutor, - getDefaultCommandExecutor, -} from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; /** * Log file retention policy for device logs: diff --git a/src/mcp/tools/logging/start_sim_log_cap.ts b/src/mcp/tools/logging/start_sim_log_cap.ts index 645a6654..2a0ea651 100644 --- a/src/mcp/tools/logging/start_sim_log_cap.ts +++ b/src/mcp/tools/logging/start_sim_log_cap.ts @@ -5,10 +5,10 @@ */ import { z } from 'zod'; -import { startLogCapture } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { ToolResponse, createTextContent } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { startLogCapture } from '../../../utils/log-capture/index.ts'; +import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.ts'; +import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const startSimLogCapSchema = z.object({ diff --git a/src/mcp/tools/logging/stop_device_log_cap.ts b/src/mcp/tools/logging/stop_device_log_cap.ts index 8582ee57..9c6f13e0 100644 --- a/src/mcp/tools/logging/stop_device_log_cap.ts +++ b/src/mcp/tools/logging/stop_device_log_cap.ts @@ -7,15 +7,12 @@ import * as fs from 'fs'; import type { ChildProcess } from 'child_process'; import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { activeDeviceLogSessions } from './start_device_log_cap.js'; -import { ToolResponse } from '../../../types/common.js'; -import { - FileSystemExecutor, - getDefaultFileSystemExecutor, - getDefaultCommandExecutor, -} from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { log } from '../../../utils/logging/index.ts'; +import { activeDeviceLogSessions } from './start_device_log_cap.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { getDefaultFileSystemExecutor, getDefaultCommandExecutor } from '../../../utils/command.ts'; +import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; interface DeviceLogSession { process: diff --git a/src/mcp/tools/logging/stop_sim_log_cap.ts b/src/mcp/tools/logging/stop_sim_log_cap.ts index e38533df..b90e3e63 100644 --- a/src/mcp/tools/logging/stop_sim_log_cap.ts +++ b/src/mcp/tools/logging/stop_sim_log_cap.ts @@ -5,10 +5,10 @@ */ import { z } from 'zod'; -import { stopLogCapture as _stopLogCapture } from '../../../utils/index.js'; -import { ToolResponse, createTextContent } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { getDefaultCommandExecutor } from '../../../utils/command.js'; +import { stopLogCapture as _stopLogCapture } from '../../../utils/log-capture/index.ts'; +import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { getDefaultCommandExecutor } from '../../../utils/command.ts'; // Define schema as ZodObject const stopSimLogCapSchema = z.object({ diff --git a/src/mcp/tools/macos/__tests__/build_macos.test.ts b/src/mcp/tools/macos/__tests__/build_macos.test.ts index 909c3cb5..55a89810 100644 --- a/src/mcp/tools/macos/__tests__/build_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_macos.test.ts @@ -7,8 +7,8 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; -import buildMacOS, { buildMacOSLogic } from '../build_macos.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import buildMacOS, { buildMacOSLogic } from '../build_macos.ts'; describe('build_macos plugin', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts index 66a3a43d..c250ec58 100644 --- a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; -import tool, { buildRunMacOSLogic } from '../build_run_macos.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import tool, { buildRunMacOSLogic } from '../build_run_macos.ts'; describe('build_run_macos', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts b/src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts index e72c9ac6..42cb8bc6 100644 --- a/src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts +++ b/src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts @@ -5,8 +5,8 @@ */ import { describe, it, expect } from 'vitest'; -import { createMockExecutor, type CommandExecutor } from '../../../../utils/command.js'; -import getMacAppPath, { get_mac_app_pathLogic } from '../get_mac_app_path.js'; +import { createMockExecutor, type CommandExecutor } from '../../../../test-utils/mock-executors.ts'; +import getMacAppPath, { get_mac_app_pathLogic } from '../get_mac_app_path.ts'; describe('get_mac_app_path plugin', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/macos/__tests__/launch_mac_app.test.ts b/src/mcp/tools/macos/__tests__/launch_mac_app.test.ts index db445ee2..23fb6889 100644 --- a/src/mcp/tools/macos/__tests__/launch_mac_app.test.ts +++ b/src/mcp/tools/macos/__tests__/launch_mac_app.test.ts @@ -9,7 +9,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockFileSystemExecutor } from '../../../../utils/command.js'; +import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; import launchMacApp, { launch_mac_appLogic } from '../launch_mac_app.ts'; describe('launch_mac_app plugin', () => { diff --git a/src/mcp/tools/macos/__tests__/test_macos.test.ts b/src/mcp/tools/macos/__tests__/test_macos.test.ts index 60a1d235..8bfa3965 100644 --- a/src/mcp/tools/macos/__tests__/test_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/test_macos.test.ts @@ -5,7 +5,7 @@ */ import { describe, it, expect } from 'vitest'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import testMacos, { testMacosLogic } from '../test_macos.ts'; describe('test_macos plugin (unified)', () => { diff --git a/src/mcp/tools/macos/build_macos.ts b/src/mcp/tools/macos/build_macos.ts index 0abe28c6..9cbc081e 100644 --- a/src/mcp/tools/macos/build_macos.ts +++ b/src/mcp/tools/macos/build_macos.ts @@ -6,12 +6,13 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { executeXcodeBuildCommand } from '../../../utils/index.js'; -import { ToolResponse, XcodePlatform } from '../../../types/common.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { log } from '../../../utils/logging/index.ts'; +import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; +import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Types for dependency injection export interface BuildUtilsDependencies { diff --git a/src/mcp/tools/macos/build_run_macos.ts b/src/mcp/tools/macos/build_run_macos.ts index 9d6bdc78..19d5f2f0 100644 --- a/src/mcp/tools/macos/build_run_macos.ts +++ b/src/mcp/tools/macos/build_run_macos.ts @@ -6,13 +6,14 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { executeXcodeBuildCommand } from '../../../utils/index.js'; -import { ToolResponse, XcodePlatform } from '../../../types/common.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { log } from '../../../utils/logging/index.ts'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; +import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/macos/clean.ts b/src/mcp/tools/macos/clean.ts index 59dc6f0c..5af33211 100644 --- a/src/mcp/tools/macos/clean.ts +++ b/src/mcp/tools/macos/clean.ts @@ -1,2 +1,2 @@ // Re-export unified clean tool for macos-project workflow -export { default } from '../utilities/clean.js'; +export { default } from '../utilities/clean.ts'; diff --git a/src/mcp/tools/macos/discover_projs.ts b/src/mcp/tools/macos/discover_projs.ts index 44b43df5..58fbf05d 100644 --- a/src/mcp/tools/macos/discover_projs.ts +++ b/src/mcp/tools/macos/discover_projs.ts @@ -1,2 +1,2 @@ // Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/discover_projs.js'; +export { default } from '../project-discovery/discover_projs.ts'; diff --git a/src/mcp/tools/macos/get_mac_app_path.ts b/src/mcp/tools/macos/get_mac_app_path.ts index 581c344e..9e32d3ea 100644 --- a/src/mcp/tools/macos/get_mac_app_path.ts +++ b/src/mcp/tools/macos/get_mac_app_path.ts @@ -6,11 +6,12 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath, sharing common options const baseOptions = { diff --git a/src/mcp/tools/macos/get_mac_bundle_id.ts b/src/mcp/tools/macos/get_mac_bundle_id.ts index 68f3c6aa..9935d53e 100644 --- a/src/mcp/tools/macos/get_mac_bundle_id.ts +++ b/src/mcp/tools/macos/get_mac_bundle_id.ts @@ -1,2 +1,2 @@ // Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/get_mac_bundle_id.js'; +export { default } from '../project-discovery/get_mac_bundle_id.ts'; diff --git a/src/mcp/tools/macos/launch_mac_app.ts b/src/mcp/tools/macos/launch_mac_app.ts index 0851a4f6..f3174943 100644 --- a/src/mcp/tools/macos/launch_mac_app.ts +++ b/src/mcp/tools/macos/launch_mac_app.ts @@ -6,15 +6,12 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { validateFileExists } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { - CommandExecutor, - FileSystemExecutor, - getDefaultCommandExecutor, -} from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { log } from '../../../utils/logging/index.ts'; +import { validateFileExists } from '../../../utils/validation/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const launchMacAppSchema = z.object({ diff --git a/src/mcp/tools/macos/list_schemes.ts b/src/mcp/tools/macos/list_schemes.ts index c5f6f78b..67519898 100644 --- a/src/mcp/tools/macos/list_schemes.ts +++ b/src/mcp/tools/macos/list_schemes.ts @@ -1,2 +1,2 @@ // Re-export unified list_schemes tool for macos-project workflow -export { default } from '../project-discovery/list_schemes.js'; +export { default } from '../project-discovery/list_schemes.ts'; diff --git a/src/mcp/tools/macos/show_build_settings.ts b/src/mcp/tools/macos/show_build_settings.ts index c8b76aa5..77db451b 100644 --- a/src/mcp/tools/macos/show_build_settings.ts +++ b/src/mcp/tools/macos/show_build_settings.ts @@ -1,2 +1,2 @@ // Re-export unified tool for macos-project workflow -export { default } from '../project-discovery/show_build_settings.js'; +export { default } from '../project-discovery/show_build_settings.ts'; diff --git a/src/mcp/tools/macos/stop_mac_app.ts b/src/mcp/tools/macos/stop_mac_app.ts index 51454c1c..2fdfc75f 100644 --- a/src/mcp/tools/macos/stop_mac_app.ts +++ b/src/mcp/tools/macos/stop_mac_app.ts @@ -1,8 +1,9 @@ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { log } from '../../../utils/logging/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const stopMacAppSchema = z.object({ diff --git a/src/mcp/tools/macos/test_macos.ts b/src/mcp/tools/macos/test_macos.ts index d4cda273..40728853 100644 --- a/src/mcp/tools/macos/test_macos.ts +++ b/src/mcp/tools/macos/test_macos.ts @@ -7,18 +7,17 @@ import { z } from 'zod'; import { join } from 'path'; -import { ToolResponse, XcodePlatform } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { executeXcodeBuildCommand } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; +import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; import { - CommandExecutor, getDefaultCommandExecutor, - FileSystemExecutor, getDefaultFileSystemExecutor, -} from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +} from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/project-discovery/__tests__/discover_projs.test.ts b/src/mcp/tools/project-discovery/__tests__/discover_projs.test.ts index d4db2c7b..07dcfef6 100644 --- a/src/mcp/tools/project-discovery/__tests__/discover_projs.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/discover_projs.test.ts @@ -10,7 +10,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; import plugin, { discover_projsLogic } from '../discover_projs.ts'; -import { createMockFileSystemExecutor } from '../../../../utils/index.js'; +import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts'; describe('discover_projs plugin', () => { let mockFileSystemExecutor: any; diff --git a/src/mcp/tools/project-discovery/__tests__/get_app_bundle_id.test.ts b/src/mcp/tools/project-discovery/__tests__/get_app_bundle_id.test.ts index 781bb250..43d81ae3 100644 --- a/src/mcp/tools/project-discovery/__tests__/get_app_bundle_id.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/get_app_bundle_id.test.ts @@ -14,7 +14,7 @@ import plugin, { get_app_bundle_idLogic } from '../get_app_bundle_id.ts'; import { createMockFileSystemExecutor, createCommandMatchingMockExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; describe('get_app_bundle_id plugin', () => { // Helper function to create mock executor for command matching diff --git a/src/mcp/tools/project-discovery/__tests__/get_mac_bundle_id.test.ts b/src/mcp/tools/project-discovery/__tests__/get_mac_bundle_id.test.ts index 876dc7b3..4d7a747b 100644 --- a/src/mcp/tools/project-discovery/__tests__/get_mac_bundle_id.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/get_mac_bundle_id.test.ts @@ -4,7 +4,7 @@ import plugin, { get_mac_bundle_idLogic } from '../get_mac_bundle_id.ts'; import { createMockFileSystemExecutor, createCommandMatchingMockExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; describe('get_mac_bundle_id plugin', () => { // Helper function to create mock executor for command matching diff --git a/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts b/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts index 815487f2..de2f2425 100644 --- a/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts @@ -6,8 +6,8 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; -import plugin, { listSchemesLogic } from '../list_schemes.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import plugin, { listSchemesLogic } from '../list_schemes.ts'; describe('list_schemes plugin', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts b/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts index 588d23ec..5fd7f186 100644 --- a/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import plugin, { showBuildSettingsLogic } from '../show_build_settings.ts'; describe('show_build_settings plugin', () => { diff --git a/src/mcp/tools/project-discovery/discover_projs.ts b/src/mcp/tools/project-discovery/discover_projs.ts index ba7604bd..1d5b2115 100644 --- a/src/mcp/tools/project-discovery/discover_projs.ts +++ b/src/mcp/tools/project-discovery/discover_projs.ts @@ -7,14 +7,11 @@ import { z } from 'zod'; import * as path from 'node:path'; -import { log } from '../../../utils/index.js'; -import { ToolResponse, createTextContent } from '../../../types/common.js'; -import { - FileSystemExecutor, - getDefaultFileSystemExecutor, - getDefaultCommandExecutor, -} from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { log } from '../../../utils/logging/index.ts'; +import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import { getDefaultFileSystemExecutor, getDefaultCommandExecutor } from '../../../utils/command.ts'; +import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Constants const DEFAULT_MAX_DEPTH = 5; diff --git a/src/mcp/tools/project-discovery/get_app_bundle_id.ts b/src/mcp/tools/project-discovery/get_app_bundle_id.ts index 026dae1b..35d22519 100644 --- a/src/mcp/tools/project-discovery/get_app_bundle_id.ts +++ b/src/mcp/tools/project-discovery/get_app_bundle_id.ts @@ -6,15 +6,15 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; +import { log } from '../../../utils/logging/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; import { CommandExecutor, - FileSystemExecutor, getDefaultFileSystemExecutor, getDefaultCommandExecutor, -} from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/command.ts'; +import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const getAppBundleIdSchema = z.object({ diff --git a/src/mcp/tools/project-discovery/get_mac_bundle_id.ts b/src/mcp/tools/project-discovery/get_mac_bundle_id.ts index 19f3d8c6..92c67742 100644 --- a/src/mcp/tools/project-discovery/get_mac_bundle_id.ts +++ b/src/mcp/tools/project-discovery/get_mac_bundle_id.ts @@ -5,15 +5,15 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; +import { log } from '../../../utils/logging/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; import { CommandExecutor, - FileSystemExecutor, getDefaultFileSystemExecutor, getDefaultCommandExecutor, -} from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/command.ts'; +import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; /** * Sync wrapper for CommandExecutor to handle synchronous commands diff --git a/src/mcp/tools/project-discovery/list_schemes.ts b/src/mcp/tools/project-discovery/list_schemes.ts index 8deda84c..56102e66 100644 --- a/src/mcp/tools/project-discovery/list_schemes.ts +++ b/src/mcp/tools/project-discovery/list_schemes.ts @@ -6,12 +6,13 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/project-discovery/show_build_settings.ts b/src/mcp/tools/project-discovery/show_build_settings.ts index 312ef54c..4a8f0fb4 100644 --- a/src/mcp/tools/project-discovery/show_build_settings.ts +++ b/src/mcp/tools/project-discovery/show_build_settings.ts @@ -6,12 +6,13 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/project-scaffolding/__tests__/index.test.ts b/src/mcp/tools/project-scaffolding/__tests__/index.test.ts index e606a8be..66651d2b 100644 --- a/src/mcp/tools/project-scaffolding/__tests__/index.test.ts +++ b/src/mcp/tools/project-scaffolding/__tests__/index.test.ts @@ -2,7 +2,7 @@ * Tests for project-scaffolding workflow metadata */ import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.js'; +import { workflow } from '../index.ts'; describe('project-scaffolding workflow metadata', () => { describe('Workflow Structure', () => { diff --git a/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts b/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts index c79af222..647c5842 100644 --- a/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts +++ b/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts @@ -10,7 +10,10 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { z } from 'zod'; import scaffoldIosProject, { scaffold_ios_projectLogic } from '../scaffold_ios_project.ts'; -import { createMockExecutor, createMockFileSystemExecutor } from '../../../../utils/index.js'; +import { + createMockExecutor, + createMockFileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; describe('scaffold_ios_project plugin', () => { let mockCommandExecutor: any; diff --git a/src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts b/src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts index 741540d3..06624662 100644 --- a/src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts +++ b/src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts @@ -14,9 +14,9 @@ import { createMockFileSystemExecutor, createNoopExecutor, createMockExecutor, -} from '../../../../utils/command.js'; -import plugin, { scaffold_macos_projectLogic } from '../scaffold_macos_project.js'; -import { TemplateManager } from '../../../../utils/index.js'; +} from '../../../../test-utils/mock-executors.ts'; +import plugin, { scaffold_macos_projectLogic } from '../scaffold_macos_project.ts'; +import { TemplateManager } from '../../../../utils/template/index.ts'; // ONLY ALLOWED MOCKING: createMockFileSystemExecutor @@ -192,7 +192,7 @@ describe('scaffold_macos_project plugin', () => { // Restore original TemplateManager for command generation tests const { TemplateManager: OriginalTemplateManager } = await import( - '../../../../utils/index.js' + '../../../../utils/template/index.ts' ); (TemplateManager as any).getTemplatePath = OriginalTemplateManager.getTemplatePath; (TemplateManager as any).cleanup = OriginalTemplateManager.cleanup; diff --git a/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts b/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts index c0c82801..4e7bfb18 100644 --- a/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts +++ b/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts @@ -6,16 +6,15 @@ import { z } from 'zod'; import { join, dirname, basename } from 'path'; -import { log } from '../../../utils/index.js'; -import { ValidationError } from '../../../utils/index.js'; -import { TemplateManager } from '../../../utils/index.js'; +import { log } from '../../../utils/logging/index.ts'; +import { ValidationError } from '../../../utils/responses/index.ts'; +import { TemplateManager } from '../../../utils/template/index.ts'; +import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; import { - CommandExecutor, - FileSystemExecutor, getDefaultCommandExecutor, getDefaultFileSystemExecutor, -} from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; +} from '../../../utils/execution/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; // Common base schema for both iOS and macOS const BaseScaffoldSchema = z.object({ @@ -457,11 +456,8 @@ async function scaffoldProject( // Get template path from TemplateManager let templatePath; try { - // Import the default command executor if not provided - if (!commandExecutor) { - const { getDefaultCommandExecutor } = await import('../../../utils/index.js'); - commandExecutor = getDefaultCommandExecutor(); - } + // Use the default command executor if not provided + commandExecutor ??= getDefaultCommandExecutor(); templatePath = await TemplateManager.getTemplatePath( platform, diff --git a/src/mcp/tools/project-scaffolding/scaffold_macos_project.ts b/src/mcp/tools/project-scaffolding/scaffold_macos_project.ts index 22f1b0c5..062364ca 100644 --- a/src/mcp/tools/project-scaffolding/scaffold_macos_project.ts +++ b/src/mcp/tools/project-scaffolding/scaffold_macos_project.ts @@ -6,16 +6,16 @@ import { z } from 'zod'; import { join, dirname, basename } from 'path'; -import { log } from '../../../utils/index.js'; -import { ValidationError } from '../../../utils/index.js'; -import { TemplateManager } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; +import { log } from '../../../utils/logging/index.ts'; +import { ValidationError } from '../../../utils/responses/index.ts'; +import { TemplateManager } from '../../../utils/template/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; import { CommandExecutor, - FileSystemExecutor, getDefaultCommandExecutor, getDefaultFileSystemExecutor, -} from '../../../utils/command.js'; +} from '../../../utils/command.ts'; +import { FileSystemExecutor } from '../../../utils/FileSystemExecutor.ts'; // Common base schema for both iOS and macOS const BaseScaffoldSchema = z.object({ diff --git a/src/mcp/tools/simulator-management/__tests__/index.test.ts b/src/mcp/tools/simulator-management/__tests__/index.test.ts index 1d7ec9e7..3cdf064c 100644 --- a/src/mcp/tools/simulator-management/__tests__/index.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/index.test.ts @@ -2,7 +2,7 @@ * Tests for simulator-management workflow metadata */ import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.js'; +import { workflow } from '../index.ts'; describe('simulator-management workflow metadata', () => { describe('Workflow Structure', () => { diff --git a/src/mcp/tools/simulator-management/__tests__/reset_sim_location.test.ts b/src/mcp/tools/simulator-management/__tests__/reset_sim_location.test.ts index 71d82206..38d51f07 100644 --- a/src/mcp/tools/simulator-management/__tests__/reset_sim_location.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/reset_sim_location.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; import resetSimLocationPlugin, { reset_sim_locationLogic } from '../reset_sim_location.ts'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; describe('reset_sim_location plugin', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/simulator-management/__tests__/set_sim_appearance.test.ts b/src/mcp/tools/simulator-management/__tests__/set_sim_appearance.test.ts index 89d61137..1866a9f2 100644 --- a/src/mcp/tools/simulator-management/__tests__/set_sim_appearance.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/set_sim_appearance.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; import setSimAppearancePlugin, { set_sim_appearanceLogic } from '../set_sim_appearance.ts'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; describe('set_sim_appearance plugin', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/simulator-management/__tests__/set_sim_location.test.ts b/src/mcp/tools/simulator-management/__tests__/set_sim_location.test.ts index 47398e09..606fae5d 100644 --- a/src/mcp/tools/simulator-management/__tests__/set_sim_location.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/set_sim_location.test.ts @@ -6,7 +6,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../utils/command.js'; +import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; import setSimLocation, { set_sim_locationLogic } from '../set_sim_location.ts'; describe('set_sim_location tool', () => { diff --git a/src/mcp/tools/simulator-management/__tests__/sim_statusbar.test.ts b/src/mcp/tools/simulator-management/__tests__/sim_statusbar.test.ts index 77ef7ced..ba364d85 100644 --- a/src/mcp/tools/simulator-management/__tests__/sim_statusbar.test.ts +++ b/src/mcp/tools/simulator-management/__tests__/sim_statusbar.test.ts @@ -6,7 +6,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, type CommandExecutor } from '../../../../utils/command.js'; +import { createMockExecutor, type CommandExecutor } from '../../../../test-utils/mock-executors.ts'; import simStatusbar, { sim_statusbarLogic } from '../sim_statusbar.ts'; describe('sim_statusbar tool', () => { diff --git a/src/mcp/tools/simulator-management/boot_sim.ts b/src/mcp/tools/simulator-management/boot_sim.ts index 079d8aa6..174a6c68 100644 --- a/src/mcp/tools/simulator-management/boot_sim.ts +++ b/src/mcp/tools/simulator-management/boot_sim.ts @@ -1,2 +1,2 @@ // Re-export from simulator to avoid duplication -export { default } from '../simulator/boot_sim.js'; +export { default } from '../simulator/boot_sim.ts'; diff --git a/src/mcp/tools/simulator-management/list_sims.ts b/src/mcp/tools/simulator-management/list_sims.ts index b14bd8a1..3c5a2ff0 100644 --- a/src/mcp/tools/simulator-management/list_sims.ts +++ b/src/mcp/tools/simulator-management/list_sims.ts @@ -1,2 +1,2 @@ // Re-export from simulator to avoid duplication -export { default } from '../simulator/list_sims.js'; +export { default } from '../simulator/list_sims.ts'; diff --git a/src/mcp/tools/simulator-management/open_sim.ts b/src/mcp/tools/simulator-management/open_sim.ts index e71b63c0..43a8857f 100644 --- a/src/mcp/tools/simulator-management/open_sim.ts +++ b/src/mcp/tools/simulator-management/open_sim.ts @@ -1,2 +1,2 @@ // Re-export from simulator to avoid duplication -export { default } from '../simulator/open_sim.js'; +export { default } from '../simulator/open_sim.ts'; diff --git a/src/mcp/tools/simulator-management/reset_sim_location.ts b/src/mcp/tools/simulator-management/reset_sim_location.ts index 9b17171c..f6c55fc7 100644 --- a/src/mcp/tools/simulator-management/reset_sim_location.ts +++ b/src/mcp/tools/simulator-management/reset_sim_location.ts @@ -1,7 +1,7 @@ import { z } from 'zod'; import { ToolResponse } from '../../../types/common.ts'; -import { log } from '../../../utils/index.ts'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject diff --git a/src/mcp/tools/simulator-management/set_sim_appearance.ts b/src/mcp/tools/simulator-management/set_sim_appearance.ts index 4fae1ba5..b8d65eeb 100644 --- a/src/mcp/tools/simulator-management/set_sim_appearance.ts +++ b/src/mcp/tools/simulator-management/set_sim_appearance.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; import { ToolResponse } from '../../../types/common.ts'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject diff --git a/src/mcp/tools/simulator-management/set_sim_location.ts b/src/mcp/tools/simulator-management/set_sim_location.ts index 306935e0..df2047b2 100644 --- a/src/mcp/tools/simulator-management/set_sim_location.ts +++ b/src/mcp/tools/simulator-management/set_sim_location.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; import { ToolResponse } from '../../../types/common.ts'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject diff --git a/src/mcp/tools/simulator-management/sim_statusbar.ts b/src/mcp/tools/simulator-management/sim_statusbar.ts index aa7148c3..261ea800 100644 --- a/src/mcp/tools/simulator-management/sim_statusbar.ts +++ b/src/mcp/tools/simulator-management/sim_statusbar.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; import { ToolResponse } from '../../../types/common.ts'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject diff --git a/src/mcp/tools/simulator/__tests__/boot_sim.test.ts b/src/mcp/tools/simulator/__tests__/boot_sim.test.ts index 53c55fb6..4f258031 100644 --- a/src/mcp/tools/simulator/__tests__/boot_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/boot_sim.test.ts @@ -10,7 +10,7 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; import bootSim, { boot_simLogic } from '../boot_sim.ts'; describe('boot_sim tool', () => { diff --git a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts index 3361ed03..ac2fec8f 100644 --- a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts @@ -5,8 +5,11 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, createMockFileSystemExecutor } from '../../../../utils/command.js'; -import buildRunSim, { build_run_simLogic } from '../build_run_sim.js'; +import { + createMockExecutor, + createMockFileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; +import buildRunSim, { build_run_simLogic } from '../build_run_sim.ts'; describe('build_run_sim tool', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/simulator/__tests__/build_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_sim.test.ts index 0f1b31f7..af69244c 100644 --- a/src/mcp/tools/simulator/__tests__/build_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_sim.test.ts @@ -1,9 +1,9 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; // Import the plugin and logic function -import buildSim, { build_simLogic } from '../build_sim.js'; +import buildSim, { build_simLogic } from '../build_sim.ts'; describe('build_sim tool', () => { // Only clear any remaining mocks if needed diff --git a/src/mcp/tools/simulator/__tests__/index.test.ts b/src/mcp/tools/simulator/__tests__/index.test.ts index 2a7f5685..a23f9593 100644 --- a/src/mcp/tools/simulator/__tests__/index.test.ts +++ b/src/mcp/tools/simulator/__tests__/index.test.ts @@ -2,7 +2,7 @@ * Tests for simulator-project workflow metadata */ import { describe, it, expect } from 'vitest'; -import { workflow } from '../index.js'; +import { workflow } from '../index.ts'; describe('simulator-project workflow metadata', () => { describe('Workflow Structure', () => { diff --git a/src/mcp/tools/simulator/__tests__/install_app_sim.test.ts b/src/mcp/tools/simulator/__tests__/install_app_sim.test.ts index 77d07342..173ec986 100644 --- a/src/mcp/tools/simulator/__tests__/install_app_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/install_app_sim.test.ts @@ -4,7 +4,7 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; import installAppSim, { install_app_simLogic } from '../install_app_sim.ts'; describe('install_app_sim tool', () => { diff --git a/src/mcp/tools/simulator/__tests__/launch_app_logs_sim.test.ts b/src/mcp/tools/simulator/__tests__/launch_app_logs_sim.test.ts index 359ade09..27cfcff3 100644 --- a/src/mcp/tools/simulator/__tests__/launch_app_logs_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/launch_app_logs_sim.test.ts @@ -10,7 +10,7 @@ import launchAppLogsSim, { launch_app_logs_simLogic, LogCaptureFunction, } from '../launch_app_logs_sim.ts'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; describe('launch_app_logs_sim tool', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts b/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts index 704dbb48..4ad74fd6 100644 --- a/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; -import launchAppSim, { launch_app_simLogic } from '../launch_app_sim.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; +import launchAppSim, { launch_app_simLogic } from '../launch_app_sim.ts'; describe('launch_app_sim tool', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/simulator/__tests__/list_sims.test.ts b/src/mcp/tools/simulator/__tests__/list_sims.test.ts index 08d26c9e..00dfb84d 100644 --- a/src/mcp/tools/simulator/__tests__/list_sims.test.ts +++ b/src/mcp/tools/simulator/__tests__/list_sims.test.ts @@ -1,6 +1,9 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, createMockFileSystemExecutor } from '../../../../utils/command.js'; +import { + createMockExecutor, + createMockFileSystemExecutor, +} from '../../../../test-utils/mock-executors.ts'; // Import the plugin and logic function import listSims, { list_simsLogic } from '../list_sims.ts'; diff --git a/src/mcp/tools/simulator/__tests__/open_sim.test.ts b/src/mcp/tools/simulator/__tests__/open_sim.test.ts index 2e18669c..fc82b1de 100644 --- a/src/mcp/tools/simulator/__tests__/open_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/open_sim.test.ts @@ -6,7 +6,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, CommandExecutor } from '../../../../utils/command.js'; +import { createMockExecutor, type CommandExecutor } from '../../../../test-utils/mock-executors.ts'; import openSim, { open_simLogic } from '../open_sim.ts'; describe('open_sim tool', () => { diff --git a/src/mcp/tools/simulator/__tests__/screenshot.test.ts b/src/mcp/tools/simulator/__tests__/screenshot.test.ts index c23f9a74..244c445f 100644 --- a/src/mcp/tools/simulator/__tests__/screenshot.test.ts +++ b/src/mcp/tools/simulator/__tests__/screenshot.test.ts @@ -10,7 +10,8 @@ import { createMockExecutor, createMockFileSystemExecutor, createCommandMatchingMockExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; +import { SystemError } from '../../../../utils/responses/index.ts'; import screenshotPlugin, { screenshotLogic } from '../../ui-testing/screenshot.ts'; describe('screenshot plugin', () => { @@ -444,7 +445,6 @@ describe('screenshot plugin', () => { }); it('should handle SystemError exceptions', async () => { - const { SystemError } = await import('../../../../utils/index.js'); const mockExecutor = createMockExecutor(new SystemError('System error occurred')); const mockPathDeps = { diff --git a/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts b/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts index a90867f4..4147ca6d 100644 --- a/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts @@ -4,8 +4,8 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; -import plugin, { stop_app_simLogic } from '../stop_app_sim.js'; +} from '../../../../test-utils/mock-executors.ts'; +import plugin, { stop_app_simLogic } from '../stop_app_sim.ts'; describe('stop_app_sim plugin', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/simulator/boot_sim.ts b/src/mcp/tools/simulator/boot_sim.ts index 1b991455..5c036211 100644 --- a/src/mcp/tools/simulator/boot_sim.ts +++ b/src/mcp/tools/simulator/boot_sim.ts @@ -1,7 +1,9 @@ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const bootSimSchema = z.object({ diff --git a/src/mcp/tools/simulator/build_run_sim.ts b/src/mcp/tools/simulator/build_run_sim.ts index 0d3f410a..3241d592 100644 --- a/src/mcp/tools/simulator/build_run_sim.ts +++ b/src/mcp/tools/simulator/build_run_sim.ts @@ -7,16 +7,14 @@ */ import { z } from 'zod'; -import { ToolResponse, SharedBuildParams, XcodePlatform } from '../../../types/common.js'; -import { - log, - getDefaultCommandExecutor, - createTextResponse, - executeXcodeBuildCommand, - CommandExecutor, -} from '../../../utils/index.js'; -import { determineSimulatorUuid } from '../../../utils/simulator-utils.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { ToolResponse, SharedBuildParams, XcodePlatform } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { determineSimulatorUuid } from '../../../utils/simulator-utils.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath, and XOR between simulatorId and simulatorName const baseOptions = { diff --git a/src/mcp/tools/simulator/build_sim.ts b/src/mcp/tools/simulator/build_sim.ts index 5ceb878a..41145c78 100644 --- a/src/mcp/tools/simulator/build_sim.ts +++ b/src/mcp/tools/simulator/build_sim.ts @@ -7,11 +7,12 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { executeXcodeBuildCommand } from '../../../utils/index.js'; -import { ToolResponse, XcodePlatform } from '../../../types/common.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { log } from '../../../utils/logging/index.ts'; +import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; +import { ToolResponse, XcodePlatform } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath, and XOR between simulatorId and simulatorName const baseOptions = { diff --git a/src/mcp/tools/simulator/clean.ts b/src/mcp/tools/simulator/clean.ts index 917e6338..76494c98 100644 --- a/src/mcp/tools/simulator/clean.ts +++ b/src/mcp/tools/simulator/clean.ts @@ -1,2 +1,2 @@ // Re-export unified clean tool for simulator-project workflow -export { default } from '../utilities/clean.js'; +export { default } from '../utilities/clean.ts'; diff --git a/src/mcp/tools/simulator/describe_ui.ts b/src/mcp/tools/simulator/describe_ui.ts index 24b24163..3208a22d 100644 --- a/src/mcp/tools/simulator/describe_ui.ts +++ b/src/mcp/tools/simulator/describe_ui.ts @@ -1,2 +1,2 @@ // Re-export from ui-testing to avoid duplication -export { default } from '../ui-testing/describe_ui.js'; +export { default } from '../ui-testing/describe_ui.ts'; diff --git a/src/mcp/tools/simulator/discover_projs.ts b/src/mcp/tools/simulator/discover_projs.ts index 44b43df5..58fbf05d 100644 --- a/src/mcp/tools/simulator/discover_projs.ts +++ b/src/mcp/tools/simulator/discover_projs.ts @@ -1,2 +1,2 @@ // Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/discover_projs.js'; +export { default } from '../project-discovery/discover_projs.ts'; diff --git a/src/mcp/tools/simulator/get_app_bundle_id.ts b/src/mcp/tools/simulator/get_app_bundle_id.ts index 11b4c5f8..6c0bfc0d 100644 --- a/src/mcp/tools/simulator/get_app_bundle_id.ts +++ b/src/mcp/tools/simulator/get_app_bundle_id.ts @@ -1,2 +1,2 @@ // Re-export from project-discovery to complete workflow -export { default } from '../project-discovery/get_app_bundle_id.js'; +export { default } from '../project-discovery/get_app_bundle_id.ts'; diff --git a/src/mcp/tools/simulator/get_sim_app_path.ts b/src/mcp/tools/simulator/get_sim_app_path.ts index eadbcf23..44a1df75 100644 --- a/src/mcp/tools/simulator/get_sim_app_path.ts +++ b/src/mcp/tools/simulator/get_sim_app_path.ts @@ -7,12 +7,13 @@ */ import { z } from 'zod'; -import { log, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { CommandExecutor } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { log } from '../../../utils/logging/index.ts'; +import { createTextResponse } from '../../../utils/responses/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; const XcodePlatform = { macOS: 'macOS', diff --git a/src/mcp/tools/simulator/install_app_sim.ts b/src/mcp/tools/simulator/install_app_sim.ts index e961a0ae..b1a58750 100644 --- a/src/mcp/tools/simulator/install_app_sim.ts +++ b/src/mcp/tools/simulator/install_app_sim.ts @@ -1,13 +1,10 @@ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { - log, - validateFileExists, - CommandExecutor, - FileSystemExecutor, - getDefaultCommandExecutor, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { validateFileExists } from '../../../utils/validation/index.ts'; +import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const installAppSimSchema = z.object({ diff --git a/src/mcp/tools/simulator/launch_app_logs_sim.ts b/src/mcp/tools/simulator/launch_app_logs_sim.ts index 05b7adf7..4cdb81a7 100644 --- a/src/mcp/tools/simulator/launch_app_logs_sim.ts +++ b/src/mcp/tools/simulator/launch_app_logs_sim.ts @@ -1,9 +1,10 @@ import { z } from 'zod'; -import { ToolResponse, createTextContent } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { startLogCapture } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { startLogCapture } from '../../../utils/log-capture/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; /** * Log capture function type for dependency injection diff --git a/src/mcp/tools/simulator/launch_app_sim.ts b/src/mcp/tools/simulator/launch_app_sim.ts index 474babc0..c4548441 100644 --- a/src/mcp/tools/simulator/launch_app_sim.ts +++ b/src/mcp/tools/simulator/launch_app_sim.ts @@ -1,8 +1,9 @@ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between simulatorUuid and simulatorName const baseOptions = { diff --git a/src/mcp/tools/simulator/list_schemes.ts b/src/mcp/tools/simulator/list_schemes.ts index fee2bd9f..1ecdf67f 100644 --- a/src/mcp/tools/simulator/list_schemes.ts +++ b/src/mcp/tools/simulator/list_schemes.ts @@ -1,2 +1,2 @@ // Re-export unified list_schemes tool for simulator-project workflow -export { default } from '../project-discovery/list_schemes.js'; +export { default } from '../project-discovery/list_schemes.ts'; diff --git a/src/mcp/tools/simulator/list_sims.ts b/src/mcp/tools/simulator/list_sims.ts index a5483bf9..6dfcd6e4 100644 --- a/src/mcp/tools/simulator/list_sims.ts +++ b/src/mcp/tools/simulator/list_sims.ts @@ -1,7 +1,9 @@ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import type { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const listSimsSchema = z.object({ diff --git a/src/mcp/tools/simulator/open_sim.ts b/src/mcp/tools/simulator/open_sim.ts index 9a6a867f..1970895c 100644 --- a/src/mcp/tools/simulator/open_sim.ts +++ b/src/mcp/tools/simulator/open_sim.ts @@ -1,7 +1,9 @@ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const openSimSchema = z.object({}); diff --git a/src/mcp/tools/simulator/screenshot.ts b/src/mcp/tools/simulator/screenshot.ts index 69ebf506..a4cde5a0 100644 --- a/src/mcp/tools/simulator/screenshot.ts +++ b/src/mcp/tools/simulator/screenshot.ts @@ -1,2 +1,2 @@ // Re-export from ui-testing to avoid duplication -export { default } from '../ui-testing/screenshot.js'; +export { default } from '../ui-testing/screenshot.ts'; diff --git a/src/mcp/tools/simulator/show_build_settings.ts b/src/mcp/tools/simulator/show_build_settings.ts index 1490a8fd..14d779c0 100644 --- a/src/mcp/tools/simulator/show_build_settings.ts +++ b/src/mcp/tools/simulator/show_build_settings.ts @@ -1,2 +1,2 @@ // Re-export unified tool for simulator-project workflow -export { default } from '../project-discovery/show_build_settings.js'; +export { default } from '../project-discovery/show_build_settings.ts'; diff --git a/src/mcp/tools/simulator/stop_app_sim.ts b/src/mcp/tools/simulator/stop_app_sim.ts index 1800bdb7..45bb88bd 100644 --- a/src/mcp/tools/simulator/stop_app_sim.ts +++ b/src/mcp/tools/simulator/stop_app_sim.ts @@ -1,7 +1,9 @@ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between simulatorUuid and simulatorName const baseOptions = { diff --git a/src/mcp/tools/simulator/test_sim.ts b/src/mcp/tools/simulator/test_sim.ts index 54701b06..728aac57 100644 --- a/src/mcp/tools/simulator/test_sim.ts +++ b/src/mcp/tools/simulator/test_sim.ts @@ -7,11 +7,13 @@ */ import { z } from 'zod'; -import { handleTestLogic, log } from '../../../utils/index.js'; -import { XcodePlatform } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { handleTestLogic } from '../../../utils/test/index.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { XcodePlatform } from '../../../types/common.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Define base schema object with all fields const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/swift-package/__tests__/active-processes.test.ts b/src/mcp/tools/swift-package/__tests__/active-processes.test.ts index f649e208..e10114a5 100644 --- a/src/mcp/tools/swift-package/__tests__/active-processes.test.ts +++ b/src/mcp/tools/swift-package/__tests__/active-processes.test.ts @@ -11,7 +11,7 @@ import { removeProcess, clearAllProcesses, type ProcessInfo, -} from '../active-processes.js'; +} from '../active-processes.ts'; describe('active-processes module', () => { // Clear the map before each test diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_build.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_build.test.ts index d5d6c85c..180d4baa 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_build.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_build.test.ts @@ -9,7 +9,7 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; import swiftPackageBuild, { swift_package_buildLogic } from '../swift_package_build.ts'; describe('swift_package_build plugin', () => { diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_clean.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_clean.test.ts index 5f5122e9..d443a1b1 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_clean.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_clean.test.ts @@ -9,7 +9,7 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; import swiftPackageClean, { swift_package_cleanLogic } from '../swift_package_clean.ts'; describe('swift_package_clean plugin', () => { diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_list.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_list.test.ts index a7140efd..b4f1d4be 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_list.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_list.test.ts @@ -5,7 +5,7 @@ */ import { describe, it, expect, beforeEach } from 'vitest'; -import swiftPackageList, { swift_package_listLogic } from '../swift_package_list.js'; +import swiftPackageList, { swift_package_listLogic } from '../swift_package_list.ts'; describe('swift_package_list plugin', () => { // No mocks to clear with pure dependency injection diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_run.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_run.test.ts index c2e86b65..e59357a8 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_run.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_run.test.ts @@ -6,7 +6,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../utils/command.js'; +import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; import swiftPackageRun, { swift_package_runLogic } from '../swift_package_run.ts'; describe('swift_package_run plugin', () => { diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_stop.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_stop.test.ts index 29cbc0bc..7d8586d1 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_stop.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_stop.test.ts @@ -10,7 +10,7 @@ import swiftPackageStop, { createMockProcessManager, swift_package_stopLogic, type ProcessManager, -} from '../swift_package_stop.js'; +} from '../swift_package_stop.ts'; /** * Mock process implementation for testing diff --git a/src/mcp/tools/swift-package/__tests__/swift_package_test.test.ts b/src/mcp/tools/swift-package/__tests__/swift_package_test.test.ts index 5aac2970..4ad8c6c3 100644 --- a/src/mcp/tools/swift-package/__tests__/swift_package_test.test.ts +++ b/src/mcp/tools/swift-package/__tests__/swift_package_test.test.ts @@ -9,7 +9,7 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; import swiftPackageTest, { swift_package_testLogic } from '../swift_package_test.ts'; describe('swift_package_test plugin', () => { diff --git a/src/mcp/tools/swift-package/swift_package_build.ts b/src/mcp/tools/swift-package/swift_package_build.ts index a0163703..51d14427 100644 --- a/src/mcp/tools/swift-package/swift_package_build.ts +++ b/src/mcp/tools/swift-package/swift_package_build.ts @@ -1,13 +1,11 @@ import { z } from 'zod'; import path from 'node:path'; -import { - createErrorResponse, - log, - CommandExecutor, - getDefaultCommandExecutor, -} from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { createErrorResponse } from '../../../utils/responses/index.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const swiftPackageBuildSchema = z.object({ diff --git a/src/mcp/tools/swift-package/swift_package_clean.ts b/src/mcp/tools/swift-package/swift_package_clean.ts index 35236636..f268dee2 100644 --- a/src/mcp/tools/swift-package/swift_package_clean.ts +++ b/src/mcp/tools/swift-package/swift_package_clean.ts @@ -1,10 +1,11 @@ import { z } from 'zod'; import path from 'node:path'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createErrorResponse } from '../../../utils/index.js'; -import { log } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createErrorResponse } from '../../../utils/responses/index.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const swiftPackageCleanSchema = z.object({ diff --git a/src/mcp/tools/swift-package/swift_package_list.ts b/src/mcp/tools/swift-package/swift_package_list.ts index adcb2737..f72fcf89 100644 --- a/src/mcp/tools/swift-package/swift_package_list.ts +++ b/src/mcp/tools/swift-package/swift_package_list.ts @@ -4,9 +4,9 @@ // Import the shared activeProcesses map from swift_package_run // This maintains the same behavior as the original implementation import { z } from 'zod'; -import { ToolResponse, createTextContent } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { getDefaultCommandExecutor } from '../../../utils/command.js'; +import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import { getDefaultCommandExecutor } from '../../../utils/command.ts'; interface ProcessInfo { executableName?: string; diff --git a/src/mcp/tools/swift-package/swift_package_run.ts b/src/mcp/tools/swift-package/swift_package_run.ts index 8e6436ed..e5ba55a9 100644 --- a/src/mcp/tools/swift-package/swift_package_run.ts +++ b/src/mcp/tools/swift-package/swift_package_run.ts @@ -1,12 +1,12 @@ import { z } from 'zod'; import path from 'node:path'; -import { createTextResponse } from '../../../utils/index.js'; -import { createErrorResponse } from '../../../utils/index.js'; -import { log } from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { ToolResponse, createTextContent } from '../../../types/common.js'; -import { addProcess } from './active-processes.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; +import { log } from '../../../utils/logging/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { ToolResponse, createTextContent } from '../../../types/common.ts'; +import { addProcess } from './active-processes.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const swiftPackageRunSchema = z.object({ diff --git a/src/mcp/tools/swift-package/swift_package_stop.ts b/src/mcp/tools/swift-package/swift_package_stop.ts index 2777f1d4..be0f5602 100644 --- a/src/mcp/tools/swift-package/swift_package_stop.ts +++ b/src/mcp/tools/swift-package/swift_package_stop.ts @@ -1,8 +1,7 @@ import { z } from 'zod'; -import { createTextResponse } from '../../../utils/index.js'; -import { createErrorResponse } from '../../../utils/index.js'; -import { getProcess, removeProcess, type ProcessInfo } from './active-processes.js'; -import { ToolResponse } from '../../../types/common.js'; +import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; +import { getProcess, removeProcess, type ProcessInfo } from './active-processes.ts'; +import { ToolResponse } from '../../../types/common.ts'; // Define schema as ZodObject const swiftPackageStopSchema = z.object({ diff --git a/src/mcp/tools/swift-package/swift_package_test.ts b/src/mcp/tools/swift-package/swift_package_test.ts index 7f1a66cb..a5386fa1 100644 --- a/src/mcp/tools/swift-package/swift_package_test.ts +++ b/src/mcp/tools/swift-package/swift_package_test.ts @@ -1,11 +1,11 @@ import { z } from 'zod'; import path from 'node:path'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { createErrorResponse } from '../../../utils/index.js'; -import { log } from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const swiftPackageTestSchema = z.object({ diff --git a/src/mcp/tools/ui-testing/__tests__/button.test.ts b/src/mcp/tools/ui-testing/__tests__/button.test.ts index b7ecdd92..4d350b9a 100644 --- a/src/mcp/tools/ui-testing/__tests__/button.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/button.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../utils/command.js'; +import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; import buttonPlugin, { buttonLogic } from '../button.ts'; describe('Button Plugin', () => { diff --git a/src/mcp/tools/ui-testing/__tests__/describe_ui.test.ts b/src/mcp/tools/ui-testing/__tests__/describe_ui.test.ts index 2e9af9e8..8004555e 100644 --- a/src/mcp/tools/ui-testing/__tests__/describe_ui.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/describe_ui.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../utils/command.js'; +import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; import describeUIPlugin, { describe_uiLogic } from '../describe_ui.ts'; describe('Describe UI Plugin', () => { diff --git a/src/mcp/tools/ui-testing/__tests__/gesture.test.ts b/src/mcp/tools/ui-testing/__tests__/gesture.test.ts index d2a1e4fc..7a683f1a 100644 --- a/src/mcp/tools/ui-testing/__tests__/gesture.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/gesture.test.ts @@ -8,7 +8,7 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; import gesturePlugin, { gestureLogic } from '../gesture.ts'; describe('Gesture Plugin', () => { diff --git a/src/mcp/tools/ui-testing/__tests__/key_press.test.ts b/src/mcp/tools/ui-testing/__tests__/key_press.test.ts index e839fc6b..26c28a21 100644 --- a/src/mcp/tools/ui-testing/__tests__/key_press.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/key_press.test.ts @@ -8,8 +8,8 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; -import keyPressPlugin, { key_pressLogic } from '../key_press.js'; +} from '../../../../test-utils/mock-executors.ts'; +import keyPressPlugin, { key_pressLogic } from '../key_press.ts'; describe('Key Press Plugin', () => { describe('Export Field Validation (Literal)', () => { diff --git a/src/mcp/tools/ui-testing/__tests__/key_sequence.test.ts b/src/mcp/tools/ui-testing/__tests__/key_sequence.test.ts index 4a42e8bc..1921c0e4 100644 --- a/src/mcp/tools/ui-testing/__tests__/key_sequence.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/key_sequence.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../utils/command.js'; +import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; import keySequencePlugin, { key_sequenceLogic } from '../key_sequence.ts'; describe('Key Sequence Plugin', () => { diff --git a/src/mcp/tools/ui-testing/__tests__/long_press.test.ts b/src/mcp/tools/ui-testing/__tests__/long_press.test.ts index 8ebaaea1..d14e730e 100644 --- a/src/mcp/tools/ui-testing/__tests__/long_press.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/long_press.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import longPressPlugin, { long_pressLogic } from '../long_press.ts'; describe('Long Press Plugin', () => { diff --git a/src/mcp/tools/ui-testing/__tests__/screenshot.test.ts b/src/mcp/tools/ui-testing/__tests__/screenshot.test.ts index 22a21428..78a10e21 100644 --- a/src/mcp/tools/ui-testing/__tests__/screenshot.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/screenshot.test.ts @@ -8,7 +8,8 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; +import { SystemError } from '../../../../utils/responses/index.ts'; import screenshotPlugin, { screenshotLogic } from '../screenshot.ts'; describe('Screenshot Plugin', () => { @@ -388,7 +389,6 @@ describe('Screenshot Plugin', () => { it('should handle SystemError from command execution', async () => { const mockExecutor = async () => { - const SystemError = (await import('../../../../utils/index.js')).SystemError; throw new SystemError('System error occurred'); }; diff --git a/src/mcp/tools/ui-testing/__tests__/swipe.test.ts b/src/mcp/tools/ui-testing/__tests__/swipe.test.ts index 2841faa8..e4fbf3f6 100644 --- a/src/mcp/tools/ui-testing/__tests__/swipe.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/swipe.test.ts @@ -4,8 +4,8 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor, createNoopExecutor } from '../../../../utils/command.js'; -import { SystemError, DependencyError } from '../../../../utils/index.js'; +import { createMockExecutor, createNoopExecutor } from '../../../../test-utils/mock-executors.ts'; +import { SystemError, DependencyError } from '../../../../utils/responses/index.ts'; // Import the plugin module to test import swipePlugin, { AxeHelpers, swipeLogic, SwipeParams } from '../swipe.ts'; diff --git a/src/mcp/tools/ui-testing/__tests__/tap.test.ts b/src/mcp/tools/ui-testing/__tests__/tap.test.ts index 14ebad48..e52fe6f7 100644 --- a/src/mcp/tools/ui-testing/__tests__/tap.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/tap.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import tapPlugin, { AxeHelpers, tapLogic } from '../tap.ts'; diff --git a/src/mcp/tools/ui-testing/__tests__/touch.test.ts b/src/mcp/tools/ui-testing/__tests__/touch.test.ts index 71b1b3fd..8efda141 100644 --- a/src/mcp/tools/ui-testing/__tests__/touch.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/touch.test.ts @@ -5,7 +5,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; import touchPlugin, { touchLogic } from '../touch.ts'; describe('Touch Plugin', () => { diff --git a/src/mcp/tools/ui-testing/__tests__/type_text.test.ts b/src/mcp/tools/ui-testing/__tests__/type_text.test.ts index ce420760..859f9466 100644 --- a/src/mcp/tools/ui-testing/__tests__/type_text.test.ts +++ b/src/mcp/tools/ui-testing/__tests__/type_text.test.ts @@ -8,7 +8,7 @@ import { createMockExecutor, createMockFileSystemExecutor, createNoopExecutor, -} from '../../../../utils/command.js'; +} from '../../../../test-utils/mock-executors.ts'; import typeTextPlugin, { type_textLogic } from '../type_text.ts'; // Mock axe helpers for dependency injection diff --git a/src/mcp/tools/ui-testing/button.ts b/src/mcp/tools/ui-testing/button.ts index c0c61947..acc1ccb9 100644 --- a/src/mcp/tools/ui-testing/button.ts +++ b/src/mcp/tools/ui-testing/button.ts @@ -1,26 +1,16 @@ -/** - * Hardware Button Plugin - * - * Press hardware buttons on iOS simulator including apple-pay, home, lock, side-button, and siri. - * Supports optional duration parameter for extended button presses. - */ - import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log, createTextResponse } from '../../../utils/index.js'; -import { - DependencyError, - AxeError, - SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +import type { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe-helpers.ts'; +import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const buttonSchema = z.object({ diff --git a/src/mcp/tools/ui-testing/describe_ui.ts b/src/mcp/tools/ui-testing/describe_ui.ts index 1b7bac0d..c384e887 100644 --- a/src/mcp/tools/ui-testing/describe_ui.ts +++ b/src/mcp/tools/ui-testing/describe_ui.ts @@ -1,19 +1,16 @@ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { - DependencyError, - AxeError, - SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { createErrorResponse } from '../../../utils/responses/index.ts'; +import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe-helpers.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const describeUiSchema = z.object({ diff --git a/src/mcp/tools/ui-testing/gesture.ts b/src/mcp/tools/ui-testing/gesture.ts index 9de593b2..fface159 100644 --- a/src/mcp/tools/ui-testing/gesture.ts +++ b/src/mcp/tools/ui-testing/gesture.ts @@ -6,22 +6,23 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; import { + createTextResponse, + createErrorResponse, DependencyError, AxeError, SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +} from '../../../utils/responses/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const gestureSchema = z.object({ diff --git a/src/mcp/tools/ui-testing/key_press.ts b/src/mcp/tools/ui-testing/key_press.ts index bffabd98..c386283d 100644 --- a/src/mcp/tools/ui-testing/key_press.ts +++ b/src/mcp/tools/ui-testing/key_press.ts @@ -1,20 +1,21 @@ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; import { + createTextResponse, + createErrorResponse, DependencyError, AxeError, SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +} from '../../../utils/responses/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const keyPressSchema = z.object({ diff --git a/src/mcp/tools/ui-testing/key_sequence.ts b/src/mcp/tools/ui-testing/key_sequence.ts index ed3503eb..f2904373 100644 --- a/src/mcp/tools/ui-testing/key_sequence.ts +++ b/src/mcp/tools/ui-testing/key_sequence.ts @@ -5,22 +5,23 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; import { + createTextResponse, + createErrorResponse, DependencyError, AxeError, SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +} from '../../../utils/responses/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const keySequenceSchema = z.object({ diff --git a/src/mcp/tools/ui-testing/long_press.ts b/src/mcp/tools/ui-testing/long_press.ts index 23e0b23c..fbd7ce0c 100644 --- a/src/mcp/tools/ui-testing/long_press.ts +++ b/src/mcp/tools/ui-testing/long_press.ts @@ -6,22 +6,23 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; import { + createTextResponse, + createErrorResponse, DependencyError, AxeError, SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +} from '../../../utils/responses/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const longPressSchema = z.object({ diff --git a/src/mcp/tools/ui-testing/screenshot.ts b/src/mcp/tools/ui-testing/screenshot.ts index 8e26800f..adcb52eb 100644 --- a/src/mcp/tools/ui-testing/screenshot.ts +++ b/src/mcp/tools/ui-testing/screenshot.ts @@ -5,17 +5,15 @@ import * as path from 'path'; import { tmpdir } from 'os'; import { z } from 'zod'; import { v4 as uuidv4 } from 'uuid'; -import { ToolResponse, createImageContent } from '../../../types/common.js'; +import { ToolResponse, createImageContent } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { createErrorResponse, SystemError } from '../../../utils/responses/index.ts'; +import type { CommandExecutor, FileSystemExecutor } from '../../../utils/execution/index.ts'; import { - log, - SystemError, - createErrorResponse, - CommandExecutor, - FileSystemExecutor, getDefaultFileSystemExecutor, getDefaultCommandExecutor, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/execution/index.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; const LOG_PREFIX = '[Screenshot]'; diff --git a/src/mcp/tools/ui-testing/swipe.ts b/src/mcp/tools/ui-testing/swipe.ts index dfb70e17..2b99635b 100644 --- a/src/mcp/tools/ui-testing/swipe.ts +++ b/src/mcp/tools/ui-testing/swipe.ts @@ -5,22 +5,18 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { - DependencyError, - AxeError, - SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; +import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe-helpers.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const swipeSchema = z.object({ diff --git a/src/mcp/tools/ui-testing/tap.ts b/src/mcp/tools/ui-testing/tap.ts index e0ef9c49..15f7aaf6 100644 --- a/src/mcp/tools/ui-testing/tap.ts +++ b/src/mcp/tools/ui-testing/tap.ts @@ -1,20 +1,16 @@ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { - DependencyError, - AxeError, - SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +import type { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe-helpers.ts'; +import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; export interface AxeHelpers { getAxePath: () => string | null; diff --git a/src/mcp/tools/ui-testing/touch.ts b/src/mcp/tools/ui-testing/touch.ts index 26c38fef..410812f4 100644 --- a/src/mcp/tools/ui-testing/touch.ts +++ b/src/mcp/tools/ui-testing/touch.ts @@ -6,22 +6,18 @@ */ import { z } from 'zod'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { - DependencyError, - AxeError, - SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +import { log } from '../../../utils/logging/index.ts'; +import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; +import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { ToolResponse } from '../../../types/common.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe-helpers.ts'; +import { ToolResponse } from '../../../types/common.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; // Define schema as ZodObject const touchSchema = z.object({ diff --git a/src/mcp/tools/ui-testing/type_text.ts b/src/mcp/tools/ui-testing/type_text.ts index 8a10ca53..45562d9a 100644 --- a/src/mcp/tools/ui-testing/type_text.ts +++ b/src/mcp/tools/ui-testing/type_text.ts @@ -6,22 +6,18 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../../../types/common.js'; -import { log } from '../../../utils/index.js'; -import { createTextResponse } from '../../../utils/index.js'; -import { - DependencyError, - AxeError, - SystemError, - createErrorResponse, -} from '../../../utils/index.js'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +import { ToolResponse } from '../../../types/common.ts'; +import { log } from '../../../utils/logging/index.ts'; +import { createTextResponse, createErrorResponse } from '../../../utils/responses/index.ts'; +import { DependencyError, AxeError, SystemError } from '../../../utils/errors.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; import { createAxeNotAvailableResponse, getAxePath, getBundledAxeEnvironment, -} from '../../../utils/index.js'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; +} from '../../../utils/axe-helpers.ts'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; const LOG_PREFIX = '[AXe]'; diff --git a/src/mcp/tools/utilities/__tests__/clean.test.ts b/src/mcp/tools/utilities/__tests__/clean.test.ts index f65699f5..3812b855 100644 --- a/src/mcp/tools/utilities/__tests__/clean.test.ts +++ b/src/mcp/tools/utilities/__tests__/clean.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import tool, { cleanLogic } from '../clean.ts'; -import { createMockExecutor } from '../../../../utils/command.js'; +import { createMockExecutor } from '../../../../test-utils/mock-executors.ts'; describe('clean (unified) tool', () => { it('exports correct name/description/schema/handler', () => { diff --git a/src/mcp/tools/utilities/clean.ts b/src/mcp/tools/utilities/clean.ts index a0e86fc0..5e0fa9a5 100644 --- a/src/mcp/tools/utilities/clean.ts +++ b/src/mcp/tools/utilities/clean.ts @@ -6,16 +6,13 @@ */ import { z } from 'zod'; -import { createTypedTool } from '../../../utils/typed-tool-factory.js'; -import { - CommandExecutor, - getDefaultCommandExecutor, - executeXcodeBuildCommand, -} from '../../../utils/index.js'; -import { XcodePlatform } from '../../../utils/index.js'; -import { ToolResponse, SharedBuildParams } from '../../../types/common.js'; -import { createErrorResponse } from '../../../utils/index.js'; -import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js'; +import { createTypedTool } from '../../../utils/typed-tool-factory.ts'; +import type { CommandExecutor } from '../../../utils/execution/index.ts'; +import { getDefaultCommandExecutor } from '../../../utils/execution/index.ts'; +import { executeXcodeBuildCommand } from '../../../utils/build/index.ts'; +import { ToolResponse, SharedBuildParams, XcodePlatform } from '../../../types/common.ts'; +import { createErrorResponse } from '../../../utils/responses/index.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; // Unified schema: XOR between projectPath and workspacePath, sharing common options const baseOptions = { diff --git a/src/server/server.ts b/src/server/server.ts index cd4bc8ce..e0e929e3 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -14,8 +14,8 @@ import { McpServer } from '@camsoft/mcp-sdk/server/mcp.js'; import { StdioServerTransport } from '@camsoft/mcp-sdk/server/stdio.js'; -import { log } from '../utils/logger.js'; -import { version } from '../version.js'; +import { log } from '../utils/logger.ts'; +import { version } from '../version.ts'; import * as Sentry from '@sentry/node'; /** diff --git a/src/test-utils/mock-executors.ts b/src/test-utils/mock-executors.ts new file mode 100644 index 00000000..f74e7777 --- /dev/null +++ b/src/test-utils/mock-executors.ts @@ -0,0 +1,267 @@ +/** + * Mock Executors for Testing - Dependency Injection Architecture + * + * This module provides mock implementations of CommandExecutor and FileSystemExecutor + * for testing purposes. These mocks are completely isolated from production dependencies + * to avoid import chains that could trigger native module loading issues in test environments. + * + * IMPORTANT: These are EXACT copies of the mock functions originally in utils/command.js + * to ensure zero behavioral changes during the file reorganization. + * + * Responsibilities: + * - Providing mock command execution for tests + * - Providing mock file system operations for tests + * - Maintaining exact behavior compatibility with original implementations + * - Avoiding any dependencies on production logging or instrumentation + */ + +import { ChildProcess } from 'child_process'; +import { CommandExecutor } from '../utils/CommandExecutor.ts'; +import { FileSystemExecutor } from '../utils/FileSystemExecutor.ts'; + +/** + * Create a mock executor for testing + * @param result Mock command result or error to throw + * @returns Mock executor function + */ +export function createMockExecutor( + result: + | { + success?: boolean; + output?: string; + error?: string; + process?: unknown; + } + | Error + | string, +): CommandExecutor { + // If result is Error or string, return executor that rejects + if (result instanceof Error || typeof result === 'string') { + return async () => { + throw result; + }; + } + + const mockProcess = { + pid: 12345, + stdout: null, + stderr: null, + stdin: null, + stdio: [null, null, null], + killed: false, + connected: false, + exitCode: result.success === false ? 1 : 0, + signalCode: null, + spawnargs: [], + spawnfile: 'sh', + } as unknown as ChildProcess; + + return async () => ({ + success: result.success ?? true, + output: result.output ?? '', + error: result.error, + process: (result.process ?? mockProcess) as ChildProcess, + }); +} + +/** + * Create a no-op executor that throws an error if called + * Use this for tests where an executor is required but should never be called + * @returns CommandExecutor that throws on invocation + */ +export function createNoopExecutor(): CommandExecutor { + return async (command) => { + throw new Error( + `🚨 NOOP EXECUTOR CALLED! 🚨\n` + + `Command: ${command.join(' ')}\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockExecutor() instead.`, + ); + }; +} + +/** + * Create a command-matching mock executor for testing multi-command scenarios + * Perfect for tools that execute multiple commands (like screenshot: simctl + sips) + * + * @param commandMap - Map of command patterns to their mock responses + * @returns CommandExecutor that matches commands and returns appropriate responses + * + * @example + * ```typescript + * const mockExecutor = createCommandMatchingMockExecutor({ + * 'xcrun simctl': { output: 'Screenshot saved' }, + * 'sips': { output: 'Image optimized' } + * }); + * ``` + */ +export function createCommandMatchingMockExecutor( + commandMap: Record< + string, + { + success?: boolean; + output?: string; + error?: string; + process?: unknown; + } + >, +): CommandExecutor { + return async (command) => { + const commandStr = command.join(' '); + + // Find matching command pattern + const matchedKey = Object.keys(commandMap).find((key) => commandStr.includes(key)); + + if (!matchedKey) { + throw new Error( + `🚨 UNEXPECTED COMMAND! 🚨\n` + + `Command: ${commandStr}\n` + + `Expected one of: ${Object.keys(commandMap).join(', ')}\n` + + `Available patterns: ${JSON.stringify(Object.keys(commandMap), null, 2)}`, + ); + } + + const result = commandMap[matchedKey]; + + const mockProcess = { + pid: 12345, + stdout: null, + stderr: null, + stdin: null, + stdio: [null, null, null], + killed: false, + connected: false, + exitCode: result.success === false ? 1 : 0, + signalCode: null, + spawnargs: [], + spawnfile: 'sh', + } as unknown as ChildProcess; + + return { + success: result.success ?? true, // Success by default (as discussed) + output: result.output ?? '', + error: result.error, + process: (result.process ?? mockProcess) as ChildProcess, + }; + }; +} + +/** + * Create a mock file system executor for testing + */ +export function createMockFileSystemExecutor( + overrides?: Partial, +): FileSystemExecutor { + return { + mkdir: async (): Promise => {}, + readFile: async (): Promise => 'mock file content', + writeFile: async (): Promise => {}, + cp: async (): Promise => {}, + readdir: async (): Promise => [], + rm: async (): Promise => {}, + existsSync: (): boolean => false, + stat: async (): Promise<{ isDirectory(): boolean }> => ({ isDirectory: (): boolean => true }), + mkdtemp: async (): Promise => '/tmp/mock-temp-123456', + tmpdir: (): string => '/tmp', + ...overrides, + }; +} + +/** + * Create a no-op file system executor that throws an error if called + * Use this for tests where an executor is required but should never be called + * @returns CommandExecutor that throws on invocation + */ +export function createNoopFileSystemExecutor(): FileSystemExecutor { + return { + mkdir: async (): Promise => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, + readFile: async (): Promise => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, + writeFile: async (): Promise => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, + cp: async (): Promise => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, + readdir: async (): Promise => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, + rm: async (): Promise => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, + existsSync: (): boolean => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, + stat: async (): Promise<{ isDirectory(): boolean }> => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, + mkdtemp: async (): Promise => { + throw new Error( + `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + + `This executor should never be called in this test context.\n` + + `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + + `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, + ); + }, + tmpdir: (): string => '/tmp', + }; +} + +/** + * Create a mock environment detector for testing + * @param options Mock options for environment detection + * @returns Mock environment detector + */ +export function createMockEnvironmentDetector( + options: { + isRunningUnderClaudeCode?: boolean; + } = {}, +): import('../utils/environment.js').EnvironmentDetector { + return { + isRunningUnderClaudeCode: () => options.isRunningUnderClaudeCode ?? false, + }; +} diff --git a/src/utils/CommandExecutor.ts b/src/utils/CommandExecutor.ts new file mode 100644 index 00000000..ff85881b --- /dev/null +++ b/src/utils/CommandExecutor.ts @@ -0,0 +1,22 @@ +import { ChildProcess } from 'child_process'; + +/** + * Command executor function type for dependency injection + */ +export type CommandExecutor = ( + command: string[], + logPrefix?: string, + useShell?: boolean, + env?: Record, + detached?: boolean, +) => Promise; +/** + * Command execution response interface + */ + +export interface CommandResponse { + success: boolean; + output: string; + error?: string; + process: ChildProcess; +} diff --git a/src/utils/FileSystemExecutor.ts b/src/utils/FileSystemExecutor.ts new file mode 100644 index 00000000..5c91258c --- /dev/null +++ b/src/utils/FileSystemExecutor.ts @@ -0,0 +1,16 @@ +/** + * File system executor interface for dependency injection + */ + +export interface FileSystemExecutor { + mkdir(path: string, options?: { recursive?: boolean }): Promise; + readFile(path: string, encoding?: BufferEncoding): Promise; + writeFile(path: string, content: string, encoding?: BufferEncoding): Promise; + cp(source: string, destination: string, options?: { recursive?: boolean }): Promise; + readdir(path: string, options?: { withFileTypes?: boolean }): Promise; + rm(path: string, options?: { recursive?: boolean; force?: boolean }): Promise; + existsSync(path: string): boolean; + stat(path: string): Promise<{ isDirectory(): boolean }>; + mkdtemp(prefix: string): Promise; + tmpdir(): string; +} diff --git a/src/utils/__tests__/simulator-utils.test.ts b/src/utils/__tests__/simulator-utils.test.ts index d83ee3e7..bdd3b140 100644 --- a/src/utils/__tests__/simulator-utils.test.ts +++ b/src/utils/__tests__/simulator-utils.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; -import { determineSimulatorUuid } from '../simulator-utils.js'; -import { createMockExecutor } from '../command.js'; +import { determineSimulatorUuid } from '../simulator-utils.ts'; +import { createMockExecutor } from '../../test-utils/mock-executors.ts'; describe('determineSimulatorUuid', () => { const mockSimulatorListOutput = JSON.stringify({ diff --git a/src/utils/__tests__/typed-tool-factory.test.ts b/src/utils/__tests__/typed-tool-factory.test.ts index e81c2148..5667443f 100644 --- a/src/utils/__tests__/typed-tool-factory.test.ts +++ b/src/utils/__tests__/typed-tool-factory.test.ts @@ -4,9 +4,9 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { createTypedTool } from '../typed-tool-factory.js'; -import { createMockExecutor } from '../command.js'; -import { ToolResponse } from '../../types/common.js'; +import { createTypedTool } from '../typed-tool-factory.ts'; +import { createMockExecutor } from '../../test-utils/mock-executors.ts'; +import { ToolResponse } from '../../types/common.ts'; // Test schema and types const testSchema = z.object({ diff --git a/src/utils/axe-helpers.ts b/src/utils/axe-helpers.ts index e1a3c03e..5326e2b5 100644 --- a/src/utils/axe-helpers.ts +++ b/src/utils/axe-helpers.ts @@ -8,8 +8,8 @@ import { existsSync } from 'fs'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; -import { createTextResponse } from './validation.js'; -import { ToolResponse } from '../types/common.js'; +import { createTextResponse } from './validation.ts'; +import { ToolResponse } from '../types/common.ts'; // Get bundled AXe path - always use the bundled version for consistency const __filename = fileURLToPath(import.meta.url); diff --git a/src/utils/axe/index.ts b/src/utils/axe/index.ts new file mode 100644 index 00000000..b3e39cc0 --- /dev/null +++ b/src/utils/axe/index.ts @@ -0,0 +1,6 @@ +export { + createAxeNotAvailableResponse, + getAxePath, + getBundledAxeEnvironment, + areAxeToolsAvailable, +} from '../axe-helpers.ts'; diff --git a/src/utils/build-utils.ts b/src/utils/build-utils.ts index dc22a2ab..545923cf 100644 --- a/src/utils/build-utils.ts +++ b/src/utils/build-utils.ts @@ -17,11 +17,11 @@ * while adding build-specific behavior, formatting, and error handling. */ -import { log } from './logger.js'; -import { XcodePlatform, constructDestinationString } from './xcode.js'; -import { CommandExecutor } from './command.js'; -import { ToolResponse, SharedBuildParams, PlatformBuildOptions } from '../types/common.js'; -import { createTextResponse, consolidateContentForClaudeCode } from './validation.js'; +import { log } from './logger.ts'; +import { XcodePlatform, constructDestinationString } from './xcode.ts'; +import { CommandExecutor } from './command.ts'; +import { ToolResponse, SharedBuildParams, PlatformBuildOptions } from '../types/common.ts'; +import { createTextResponse, consolidateContentForClaudeCode } from './validation.ts'; import { isXcodemakeEnabled, isXcodemakeAvailable, @@ -29,7 +29,7 @@ import { executeMakeCommand, doesMakefileExist, doesMakeLogFileExist, -} from './xcodemake.js'; +} from './xcodemake.ts'; import path from 'path'; /** diff --git a/src/utils/build/index.ts b/src/utils/build/index.ts new file mode 100644 index 00000000..61a6c70b --- /dev/null +++ b/src/utils/build/index.ts @@ -0,0 +1 @@ +export { executeXcodeBuildCommand } from '../build-utils.ts'; \ No newline at end of file diff --git a/src/utils/command.ts b/src/utils/command.ts index c25063a7..05ecf63e 100644 --- a/src/utils/command.ts +++ b/src/utils/command.ts @@ -9,47 +9,16 @@ * - Managing process spawning, output capture, and error handling */ -import { spawn, ChildProcess } from 'child_process'; +import { spawn } from 'child_process'; import { existsSync } from 'fs'; import { tmpdir as osTmpdir } from 'os'; -import { log } from './logger.js'; +import { log } from './logger.ts'; +import { FileSystemExecutor } from './FileSystemExecutor.ts'; +import { CommandExecutor, CommandResponse } from './CommandExecutor.ts'; -/** - * Command execution response interface - */ -export interface CommandResponse { - success: boolean; - output: string; - error?: string; - process: ChildProcess; -} - -/** - * Command executor function type for dependency injection - */ -export type CommandExecutor = ( - command: string[], - logPrefix?: string, - useShell?: boolean, - env?: Record, - detached?: boolean, -) => Promise; - -/** - * File system executor interface for dependency injection - */ -export interface FileSystemExecutor { - mkdir(path: string, options?: { recursive?: boolean }): Promise; - readFile(path: string, encoding?: BufferEncoding): Promise; - writeFile(path: string, content: string, encoding?: BufferEncoding): Promise; - cp(source: string, destination: string, options?: { recursive?: boolean }): Promise; - readdir(path: string, options?: { withFileTypes?: boolean }): Promise; - rm(path: string, options?: { recursive?: boolean; force?: boolean }): Promise; - existsSync(path: string): boolean; - stat(path: string): Promise<{ isDirectory(): boolean }>; - mkdtemp(prefix: string): Promise; - tmpdir(): string; -} +// Re-export types for backward compatibility +export { CommandExecutor, CommandResponse } from './CommandExecutor.ts'; +export { FileSystemExecutor } from './FileSystemExecutor.ts'; /** * Default executor implementation using spawn (current production behavior) @@ -74,11 +43,10 @@ async function defaultExecutor( // For shell execution, we need to format as ['sh', '-c', 'full command string'] const commandString = command .map((arg) => { - // If the argument contains spaces or special characters, wrap it in quotes - // Ensure existing quotes are escaped - if (/[\s,"'=]/.test(arg) && !/^".*"$/.test(arg)) { - // Check if needs quoting and isn't already quoted - return `"${arg.replace(/(["\\])/g, '\\$1')}"`; // Escape existing quotes and backslashes + // Shell metacharacters that require quoting: space, quotes, equals, dollar, backticks, semicolons, pipes, etc. + if (/[\s,"'=$`;&|<>(){}[\]\\*?~]/.test(arg) && !/^".*"$/.test(arg)) { + // Escape all quotes and backslashes, then wrap in double quotes + return `"${arg.replace(/(["\\])/g, '\\$1')}"`; } return arg; }) @@ -258,250 +226,3 @@ export function getDefaultFileSystemExecutor(): FileSystemExecutor { } return defaultFileSystemExecutor; } - -/** - * Create a mock executor for testing - * @param result Mock command result or error to throw - * @returns Mock executor function - */ -export function createMockExecutor( - result: - | { - success?: boolean; - output?: string; - error?: string; - process?: unknown; - } - | Error - | string, -): CommandExecutor { - // If result is Error or string, return executor that rejects - if (result instanceof Error || typeof result === 'string') { - return async () => { - throw result; - }; - } - - const mockProcess = { - pid: 12345, - stdout: null, - stderr: null, - stdin: null, - stdio: [null, null, null], - killed: false, - connected: false, - exitCode: result.success === false ? 1 : 0, - signalCode: null, - spawnargs: [], - spawnfile: 'sh', - } as unknown as ChildProcess; - - return async () => ({ - success: result.success ?? true, - output: result.output ?? '', - error: result.error, - process: (result.process ?? mockProcess) as ChildProcess, - }); -} - -/** - * Create a no-op executor that throws an error if called - * Use this for tests where an executor is required but should never be called - * @returns CommandExecutor that throws on invocation - */ -export function createNoopExecutor(): CommandExecutor { - return async (command) => { - throw new Error( - `🚨 NOOP EXECUTOR CALLED! 🚨\n` + - `Command: ${command.join(' ')}\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockExecutor() instead.`, - ); - }; -} - -/** - * Create a command-matching mock executor for testing multi-command scenarios - * Perfect for tools that execute multiple commands (like screenshot: simctl + sips) - * - * @param commandMap - Map of command patterns to their mock responses - * @returns CommandExecutor that matches commands and returns appropriate responses - * - * @example - * ```typescript - * const mockExecutor = createCommandMatchingMockExecutor({ - * 'xcrun simctl': { output: 'Screenshot saved' }, - * 'sips': { output: 'Image optimized' } - * }); - * ``` - */ -export function createCommandMatchingMockExecutor( - commandMap: Record< - string, - { - success?: boolean; - output?: string; - error?: string; - process?: unknown; - } - >, -): CommandExecutor { - return async (command) => { - const commandStr = command.join(' '); - - // Find matching command pattern - const matchedKey = Object.keys(commandMap).find((key) => commandStr.includes(key)); - - if (!matchedKey) { - throw new Error( - `🚨 UNEXPECTED COMMAND! 🚨\n` + - `Command: ${commandStr}\n` + - `Expected one of: ${Object.keys(commandMap).join(', ')}\n` + - `Available patterns: ${JSON.stringify(Object.keys(commandMap), null, 2)}`, - ); - } - - const result = commandMap[matchedKey]; - - const mockProcess = { - pid: 12345, - stdout: null, - stderr: null, - stdin: null, - stdio: [null, null, null], - killed: false, - connected: false, - exitCode: result.success === false ? 1 : 0, - signalCode: null, - spawnargs: [], - spawnfile: 'sh', - } as unknown as ChildProcess; - - return { - success: result.success ?? true, // Success by default (as discussed) - output: result.output ?? '', - error: result.error, - process: (result.process ?? mockProcess) as ChildProcess, - }; - }; -} - -/** - * Create a mock file system executor for testing - */ -export function createMockFileSystemExecutor( - overrides?: Partial, -): FileSystemExecutor { - return { - mkdir: async (): Promise => {}, - readFile: async (): Promise => 'mock file content', - writeFile: async (): Promise => {}, - cp: async (): Promise => {}, - readdir: async (): Promise => [], - rm: async (): Promise => {}, - existsSync: (): boolean => false, - stat: async (): Promise<{ isDirectory(): boolean }> => ({ isDirectory: (): boolean => true }), - mkdtemp: async (): Promise => '/tmp/mock-temp-123456', - tmpdir: (): string => '/tmp', - ...overrides, - }; -} - -/** - * Create a no-op file system executor that throws an error if called - * Use this for tests where an executor is required but should never be called - * @returns CommandExecutor that throws on invocation - */ -export function createNoopFileSystemExecutor(): FileSystemExecutor { - return { - mkdir: async (): Promise => { - throw new Error( - `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, - ); - }, - readFile: async (): Promise => { - throw new Error( - `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, - ); - }, - writeFile: async (): Promise => { - throw new Error( - `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, - ); - }, - cp: async (): Promise => { - throw new Error( - `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, - ); - }, - readdir: async (): Promise => { - throw new Error( - `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, - ); - }, - rm: async (): Promise => { - throw new Error( - `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, - ); - }, - existsSync: (): boolean => { - throw new Error( - `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, - ); - }, - stat: async (): Promise<{ isDirectory(): boolean }> => { - throw new Error( - `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, - ); - }, - mkdtemp: async (): Promise => { - throw new Error( - `🚨 NOOP FILESYSTEM EXECUTOR CALLED! 🚨\n` + - `This executor should never be called in this test context.\n` + - `If you see this error, it means the test is exercising a code path that wasn't expected.\n` + - `Either fix the test to avoid this code path, or use createMockFileSystemExecutor() instead.`, - ); - }, - tmpdir: (): string => '/tmp', - }; -} - -/** - * Create a mock environment detector for testing - * @param options Mock options for environment detection - * @returns Mock environment detector - */ -export function createMockEnvironmentDetector( - options: { - isRunningUnderClaudeCode?: boolean; - } = {}, -): import('./environment.js').EnvironmentDetector { - return { - isRunningUnderClaudeCode: () => options.isRunningUnderClaudeCode ?? false, - }; -} diff --git a/src/utils/environment.ts b/src/utils/environment.ts index ade5cb27..c610dd4b 100644 --- a/src/utils/environment.ts +++ b/src/utils/environment.ts @@ -6,7 +6,7 @@ */ import { execSync } from 'child_process'; -import { log } from './logger.js'; +import { log } from './logger.ts'; /** * Interface for environment detection abstraction diff --git a/src/utils/errors.ts b/src/utils/errors.ts index ec182fd0..359399cb 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,4 +1,4 @@ -import { ToolResponse } from '../types/common.js'; +import { ToolResponse } from '../types/common.ts'; /** * Error Utilities - Type-safe error hierarchy for the application diff --git a/src/utils/execution/index.ts b/src/utils/execution/index.ts new file mode 100644 index 00000000..8a8f8c42 --- /dev/null +++ b/src/utils/execution/index.ts @@ -0,0 +1,9 @@ +/** + * Focused execution facade. + * Prefer importing from 'utils/execution/index.js' instead of the legacy utils barrel. + */ +export { getDefaultCommandExecutor, getDefaultFileSystemExecutor } from '../command.ts'; + +// Types +export type { CommandExecutor, CommandResponse } from '../CommandExecutor.ts'; +export type { FileSystemExecutor } from '../FileSystemExecutor.ts'; diff --git a/src/utils/index.ts b/src/utils/index.ts deleted file mode 100644 index 8468926d..00000000 --- a/src/utils/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Export all utilities for plugin consumption -export * from './logger.js'; -export * from './command.js'; -export * from './validation.js'; -export * from './errors.js'; -export * from './build-utils.js'; -export * from './xcode.js'; -export * from './axe-helpers.js'; -export * from './log_capture.js'; -export * from './template-manager.js'; -export * from './test-common.js'; -export * from './xcodemake.js'; -export * from './environment.js'; -export * from './tool-registry.js'; -export * from '../version.js'; -export * from '../core/dynamic-tools.js'; -export * from '../core/plugin-registry.js'; diff --git a/src/utils/log-capture/index.ts b/src/utils/log-capture/index.ts new file mode 100644 index 00000000..986c525a --- /dev/null +++ b/src/utils/log-capture/index.ts @@ -0,0 +1 @@ +export { startLogCapture, stopLogCapture } from '../log_capture.ts'; diff --git a/src/utils/log_capture.ts b/src/utils/log_capture.ts index 6becf9f7..dadfb4bf 100644 --- a/src/utils/log_capture.ts +++ b/src/utils/log_capture.ts @@ -3,8 +3,8 @@ import * as path from 'path'; import * as os from 'os'; import type { ChildProcess } from 'child_process'; import { v4 as uuidv4 } from 'uuid'; -import { log } from '../utils/logger.js'; -import { CommandExecutor, getDefaultCommandExecutor } from './command.js'; +import { log } from '../utils/logger.ts'; +import { CommandExecutor, getDefaultCommandExecutor } from './command.ts'; /** * Log file retention policy: diff --git a/src/utils/logger.ts b/src/utils/logger.ts index aca0760a..6bb86141 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -17,12 +17,53 @@ * It's used by virtually all other modules for status reporting and error logging. */ -import * as Sentry from '@sentry/node'; +import { createRequire } from 'node:module'; +// Note: Removed "import * as Sentry from '@sentry/node'" to prevent native module loading at import time -const SENTRY_ENABLED = process.env.SENTRY_DISABLED !== 'true'; +const SENTRY_ENABLED = + process.env.SENTRY_DISABLED !== 'true' && process.env.XCODEBUILDMCP_SENTRY_DISABLED !== 'true'; + +function isTestEnv(): boolean { + return ( + process.env.VITEST === 'true' || + process.env.NODE_ENV === 'test' || + process.env.XCODEBUILDMCP_SILENCE_LOGS === 'true' + ); +} + +type SentryModule = typeof import('@sentry/node'); + +const require = createRequire(import.meta.url); +let cachedSentry: SentryModule | null = null; + +function loadSentrySync(): SentryModule | null { + if (!SENTRY_ENABLED || isTestEnv()) return null; + if (cachedSentry) return cachedSentry; + try { + cachedSentry = require('@sentry/node') as SentryModule; + return cachedSentry; + } catch { + // If @sentry/node is not installed in some environments, fail silently. + return null; + } +} + +function withSentry(cb: (s: SentryModule) => void): void { + const s = loadSentrySync(); + if (!s) return; + try { + cb(s); + } catch { + // no-op: avoid throwing inside logger + } +} if (!SENTRY_ENABLED) { - log('info', 'Sentry disabled due to SENTRY_DISABLED environment variable'); + if (process.env.SENTRY_DISABLED === 'true') { + log('info', 'Sentry disabled due to SENTRY_DISABLED environment variable'); + } else if (process.env.XCODEBUILDMCP_SENTRY_DISABLED === 'true') { + log('info', 'Sentry disabled due to XCODEBUILDMCP_SENTRY_DISABLED environment variable'); + } } /** @@ -44,7 +85,7 @@ export function log(level: string, message: string): void { const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`; if (level === 'error' && SENTRY_ENABLED) { - Sentry.captureMessage(logMessage); + withSentry((s) => s.captureMessage(logMessage)); } // It's important to use console.error here to ensure logs don't interfere with MCP protocol communication diff --git a/src/utils/logging/index.ts b/src/utils/logging/index.ts new file mode 100644 index 00000000..eaa25e7e --- /dev/null +++ b/src/utils/logging/index.ts @@ -0,0 +1,5 @@ +/** + * Focused logging facade. + * Prefer importing from 'utils/logging/index.js' instead of the legacy utils barrel. + */ +export { log } from '../logger.ts'; diff --git a/src/utils/plugin-registry/index.ts b/src/utils/plugin-registry/index.ts new file mode 100644 index 00000000..f3010767 --- /dev/null +++ b/src/utils/plugin-registry/index.ts @@ -0,0 +1,2 @@ +export { loadWorkflowGroups, loadPlugins } from '../../core/plugin-registry.ts'; +export { getEnabledWorkflows } from '../../core/dynamic-tools.ts'; diff --git a/src/utils/responses/index.ts b/src/utils/responses/index.ts new file mode 100644 index 00000000..ef740dcc --- /dev/null +++ b/src/utils/responses/index.ts @@ -0,0 +1,15 @@ +/** + * Focused responses facade. + * Prefer importing from 'utils/responses/index.js' instead of the legacy utils barrel. + */ +export { createTextResponse } from '../validation.ts'; +export { + createErrorResponse, + DependencyError, + AxeError, + SystemError, + ValidationError, +} from '../errors.ts'; + +// Types +export type { ToolResponse } from '../../types/common.ts'; diff --git a/src/utils/sentry.ts b/src/utils/sentry.ts index daf436b5..77cd33c5 100644 --- a/src/utils/sentry.ts +++ b/src/utils/sentry.ts @@ -6,7 +6,7 @@ */ import * as Sentry from '@sentry/node'; -import { version } from '../version.js'; +import { version } from '../version.ts'; import { execSync } from 'child_process'; // Inlined system info functions to avoid circular dependencies diff --git a/src/utils/simulator-utils.ts b/src/utils/simulator-utils.ts index c78c78d0..f595c49f 100644 --- a/src/utils/simulator-utils.ts +++ b/src/utils/simulator-utils.ts @@ -2,9 +2,10 @@ * Simulator utility functions for name to UUID resolution */ -import { CommandExecutor } from './command.js'; -import { ToolResponse } from '../types/common.js'; -import { createErrorResponse, log } from './index.js'; +import type { CommandExecutor } from './execution/index.ts'; +import { ToolResponse } from '../types/common.ts'; +import { log } from './logging/index.ts'; +import { createErrorResponse } from './responses/index.ts'; /** * UUID regex pattern to check if a string looks like a UUID diff --git a/src/utils/template-manager.ts b/src/utils/template-manager.ts index 72c6887e..f5e3e128 100644 --- a/src/utils/template-manager.ts +++ b/src/utils/template-manager.ts @@ -1,9 +1,10 @@ import { join } from 'path'; import { tmpdir } from 'os'; import { randomUUID } from 'crypto'; -import { log } from './logger.js'; -import { iOSTemplateVersion, macOSTemplateVersion } from '../version.js'; -import { CommandExecutor, FileSystemExecutor } from './command.js'; +import { log } from './logger.ts'; +import { iOSTemplateVersion, macOSTemplateVersion } from '../version.ts'; +import { CommandExecutor } from './command.ts'; +import { FileSystemExecutor } from './FileSystemExecutor.ts'; /** * Template manager for downloading and managing project templates diff --git a/src/utils/template/index.ts b/src/utils/template/index.ts new file mode 100644 index 00000000..926b729e --- /dev/null +++ b/src/utils/template/index.ts @@ -0,0 +1 @@ +export { TemplateManager } from '../template-manager.ts'; diff --git a/src/utils/test-common.ts b/src/utils/test-common.ts index 7f8b8a8d..68051635 100644 --- a/src/utils/test-common.ts +++ b/src/utils/test-common.ts @@ -17,12 +17,12 @@ import { exec } from 'child_process'; import { mkdtemp, rm } from 'fs/promises'; import { tmpdir } from 'os'; import { join } from 'path'; -import { log } from './logger.js'; -import { XcodePlatform } from './xcode.js'; -import { executeXcodeBuildCommand } from './build-utils.js'; -import { createTextResponse, consolidateContentForClaudeCode } from './validation.js'; -import { ToolResponse } from '../types/common.js'; -import { CommandExecutor, getDefaultCommandExecutor } from './command.js'; +import { log } from './logger.ts'; +import { XcodePlatform } from './xcode.ts'; +import { executeXcodeBuildCommand } from './build/index.ts'; +import { createTextResponse, consolidateContentForClaudeCode } from './validation.ts'; +import { ToolResponse } from '../types/common.ts'; +import { CommandExecutor, getDefaultCommandExecutor } from './command.ts'; /** * Type definition for test summary structure from xcresulttool diff --git a/src/utils/test/index.ts b/src/utils/test/index.ts new file mode 100644 index 00000000..30ff6fcb --- /dev/null +++ b/src/utils/test/index.ts @@ -0,0 +1 @@ +export { handleTestLogic } from '../test-common.ts'; diff --git a/src/utils/tool-registry.ts b/src/utils/tool-registry.ts index ea2b33d8..69bf974f 100644 --- a/src/utils/tool-registry.ts +++ b/src/utils/tool-registry.ts @@ -1,7 +1,7 @@ import { McpServer, RegisteredTool } from '@camsoft/mcp-sdk/server/mcp.js'; -import { loadPlugins } from '../core/plugin-registry.js'; -import { ToolResponse } from '../types/common.js'; -import { log } from './logger.js'; +import { loadPlugins } from '../core/plugin-registry.ts'; +import { ToolResponse } from '../types/common.ts'; +import { log } from './logger.ts'; // Global registry to track registered tools for cleanup const toolRegistry = new Map(); diff --git a/src/utils/typed-tool-factory.ts b/src/utils/typed-tool-factory.ts index 9c0fc443..1eddf16a 100644 --- a/src/utils/typed-tool-factory.ts +++ b/src/utils/typed-tool-factory.ts @@ -10,9 +10,9 @@ */ import { z } from 'zod'; -import { ToolResponse } from '../types/common.js'; -import { CommandExecutor } from './command.js'; -import { createErrorResponse } from './index.js'; +import { ToolResponse } from '../types/common.ts'; +import type { CommandExecutor } from './execution/index.ts'; +import { createErrorResponse } from './responses/index.ts'; /** * Creates a type-safe tool handler that validates parameters at runtime diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 1063341e..4c2a7f84 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -21,10 +21,10 @@ */ import * as fs from 'fs'; -import { log } from './logger.js'; -import { ToolResponse, ValidationResult } from '../types/common.js'; -import { FileSystemExecutor } from './command.js'; -import { getDefaultEnvironmentDetector } from './environment.js'; +import { log } from './logger.ts'; +import { ToolResponse, ValidationResult } from '../types/common.ts'; +import { FileSystemExecutor } from './FileSystemExecutor.ts'; +import { getDefaultEnvironmentDetector } from './environment.ts'; /** * Creates a text response with the given message diff --git a/src/utils/validation/index.ts b/src/utils/validation/index.ts new file mode 100644 index 00000000..8b1303dd --- /dev/null +++ b/src/utils/validation/index.ts @@ -0,0 +1,5 @@ +/** + * Focused validation facade. + * Prefer importing from 'utils/validation/index.js' instead of the legacy utils barrel. + */ +export * from '../validation.ts'; diff --git a/src/utils/version/index.ts b/src/utils/version/index.ts new file mode 100644 index 00000000..e5e1336f --- /dev/null +++ b/src/utils/version/index.ts @@ -0,0 +1 @@ +export { version } from '../../version.ts'; diff --git a/src/utils/xcode.ts b/src/utils/xcode.ts index 9dc663fd..b400ac27 100644 --- a/src/utils/xcode.ts +++ b/src/utils/xcode.ts @@ -12,8 +12,8 @@ * which build upon these core functions to provide higher-level abstractions. */ -import { log } from './logger.js'; -import { XcodePlatform } from '../types/common.js'; +import { log } from './logger.ts'; +import { XcodePlatform } from '../types/common.ts'; // Re-export XcodePlatform for use in other modules export { XcodePlatform }; diff --git a/src/utils/xcodemake.ts b/src/utils/xcodemake.ts index 1e369243..00a46f39 100644 --- a/src/utils/xcodemake.ts +++ b/src/utils/xcodemake.ts @@ -13,8 +13,8 @@ * - Auto-downloading xcodemake if enabled but not found */ -import { log } from './logger.js'; -import { CommandResponse, getDefaultCommandExecutor } from './command.js'; +import { log } from './logger.ts'; +import { CommandResponse, getDefaultCommandExecutor } from './command.ts'; import { existsSync, readdirSync } from 'fs'; import * as path from 'path'; import * as os from 'os'; diff --git a/src/utils/xcodemake/index.ts b/src/utils/xcodemake/index.ts new file mode 100644 index 00000000..cb24f210 --- /dev/null +++ b/src/utils/xcodemake/index.ts @@ -0,0 +1 @@ +export { isXcodemakeEnabled, isXcodemakeAvailable, doesMakefileExist } from '../xcodemake.ts'; diff --git a/vitest.config.ts b/vitest.config.ts index 6f09012d..7736bd83 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -21,10 +21,10 @@ export default defineConfig({ '**/__pycache__/**', '**/dist/**' ], - pool: 'vmThreads', + pool: 'threads', poolOptions: { - vmThreads: { - maxThreads: 1 + threads: { + maxThreads: 4 } }, env: {