@mxf-dev/core 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/adapters/FrameworkAdapter.js +1 -1
- package/dist/adapters/FrameworkAdapter.js.map +1 -1
- package/dist/config/ConfigManager.d.ts +9 -8
- package/dist/config/ConfigManager.d.ts.map +1 -1
- package/dist/config/ConfigManager.js +47 -17
- package/dist/config/ConfigManager.js.map +1 -1
- package/dist/config/memory-strata.config.d.ts +25 -0
- package/dist/config/memory-strata.config.d.ts.map +1 -0
- package/dist/config/memory-strata.config.js +107 -0
- package/dist/config/memory-strata.config.js.map +1 -0
- package/dist/constants/ToolNames.d.ts +1 -29
- package/dist/constants/ToolNames.d.ts.map +1 -1
- package/dist/constants/ToolNames.js +4 -17
- package/dist/constants/ToolNames.js.map +1 -1
- package/dist/database/DatabaseAdapterFactory.d.ts +14 -2
- package/dist/database/DatabaseAdapterFactory.d.ts.map +1 -1
- package/dist/database/DatabaseAdapterFactory.js +21 -6
- package/dist/database/DatabaseAdapterFactory.js.map +1 -1
- package/dist/database/adapters/mongodb/MongoBaseRepository.d.ts +7 -1
- package/dist/database/adapters/mongodb/MongoBaseRepository.d.ts.map +1 -1
- package/dist/database/adapters/mongodb/MongoBaseRepository.js +12 -2
- package/dist/database/adapters/mongodb/MongoBaseRepository.js.map +1 -1
- package/dist/events/ClientEventBus.d.ts +16 -77
- package/dist/events/ClientEventBus.d.ts.map +1 -1
- package/dist/events/ClientEventBus.js +38 -311
- package/dist/events/ClientEventBus.js.map +1 -1
- package/dist/events/EventBus.d.ts +1 -1
- package/dist/events/EventBus.d.ts.map +1 -1
- package/dist/events/EventBus.js +5 -4
- package/dist/events/EventBus.js.map +1 -1
- package/dist/events/EventBusBase.d.ts +139 -34
- package/dist/events/EventBusBase.d.ts.map +1 -1
- package/dist/events/EventBusBase.js +366 -88
- package/dist/events/EventBusBase.js.map +1 -1
- package/dist/events/EventNames.d.ts +10 -1
- package/dist/events/EventNames.d.ts.map +1 -1
- package/dist/events/EventNames.js +3 -0
- package/dist/events/EventNames.js.map +1 -1
- package/dist/events/PublicEvents.d.ts +6 -2
- package/dist/events/PublicEvents.d.ts.map +1 -1
- package/dist/events/PublicEvents.js +7 -4
- package/dist/events/PublicEvents.js.map +1 -1
- package/dist/events/ServerEventBus.d.ts +23 -112
- package/dist/events/ServerEventBus.d.ts.map +1 -1
- package/dist/events/ServerEventBus.js +58 -323
- package/dist/events/ServerEventBus.js.map +1 -1
- package/dist/events/event-definitions/LlmBudgetEvents.d.ts +68 -0
- package/dist/events/event-definitions/LlmBudgetEvents.d.ts.map +1 -0
- package/dist/events/event-definitions/LlmBudgetEvents.js +35 -0
- package/dist/events/event-definitions/LlmBudgetEvents.js.map +1 -0
- package/dist/interfaces/IMemoryPersistence.d.ts +41 -0
- package/dist/interfaces/IMemoryPersistence.d.ts.map +1 -0
- package/dist/interfaces/IMemoryPersistence.js +15 -0
- package/dist/interfaces/IMemoryPersistence.js.map +1 -0
- package/dist/middleware/MxpMiddleware.d.ts +8 -1
- package/dist/middleware/MxpMiddleware.d.ts.map +1 -1
- package/dist/middleware/MxpMiddleware.js +42 -20
- package/dist/middleware/MxpMiddleware.js.map +1 -1
- package/dist/models/channelKey.d.ts +1 -0
- package/dist/models/channelKey.d.ts.map +1 -1
- package/dist/models/channelKey.js +5 -0
- package/dist/models/channelKey.js.map +1 -1
- package/dist/models/memoryUtility.d.ts +39 -0
- package/dist/models/memoryUtility.d.ts.map +1 -0
- package/dist/models/memoryUtility.js +39 -0
- package/dist/models/memoryUtility.js.map +1 -0
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +23 -1
- package/dist/models/user.js.map +1 -1
- package/dist/mxp/BinaryProtocolLayer.d.ts +24 -8
- package/dist/mxp/BinaryProtocolLayer.d.ts.map +1 -1
- package/dist/mxp/BinaryProtocolLayer.js +90 -103
- package/dist/mxp/BinaryProtocolLayer.js.map +1 -1
- package/dist/mxp/ContextCompressionEngine.d.ts +57 -10
- package/dist/mxp/ContextCompressionEngine.d.ts.map +1 -1
- package/dist/mxp/ContextCompressionEngine.js +170 -128
- package/dist/mxp/ContextCompressionEngine.js.map +1 -1
- package/dist/mxp/MxpTokenOptimizer.d.ts +7 -3
- package/dist/mxp/MxpTokenOptimizer.d.ts.map +1 -1
- package/dist/mxp/MxpTokenOptimizer.js +8 -5
- package/dist/mxp/MxpTokenOptimizer.js.map +1 -1
- package/dist/prompts/MxfAgentSystemPrompt.d.ts +1 -1
- package/dist/prompts/MxfAgentSystemPrompt.d.ts.map +1 -1
- package/dist/prompts/MxfAgentSystemPrompt.js +47 -57
- package/dist/prompts/MxfAgentSystemPrompt.js.map +1 -1
- package/dist/protocols/mcp/ToolError.d.ts +123 -0
- package/dist/protocols/mcp/ToolError.d.ts.map +1 -0
- package/dist/protocols/mcp/ToolError.js +108 -0
- package/dist/protocols/mcp/ToolError.js.map +1 -0
- package/dist/protocols/mcp/defineTool.d.ts +133 -0
- package/dist/protocols/mcp/defineTool.d.ts.map +1 -0
- package/dist/protocols/mcp/defineTool.js +236 -0
- package/dist/protocols/mcp/defineTool.js.map +1 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.d.ts +38 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.d.ts.map +1 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.js +227 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.js.map +1 -0
- package/dist/protocols/mcp/security/McpSecurityGuard.d.ts +9 -1
- package/dist/protocols/mcp/security/McpSecurityGuard.d.ts.map +1 -1
- package/dist/protocols/mcp/security/McpSecurityGuard.js +10 -2
- package/dist/protocols/mcp/security/McpSecurityGuard.js.map +1 -1
- package/dist/protocols/mcp/security/McpToolPolicy.d.ts +81 -0
- package/dist/protocols/mcp/security/McpToolPolicy.d.ts.map +1 -0
- package/dist/protocols/mcp/security/McpToolPolicy.js +202 -0
- package/dist/protocols/mcp/security/McpToolPolicy.js.map +1 -0
- package/dist/protocols/mcp/services/ExternalMcpServerManager.d.ts +51 -18
- package/dist/protocols/mcp/services/ExternalMcpServerManager.d.ts.map +1 -1
- package/dist/protocols/mcp/services/ExternalMcpServerManager.js +256 -283
- package/dist/protocols/mcp/services/ExternalMcpServerManager.js.map +1 -1
- package/dist/protocols/mcp/services/ExternalServerConfigs.d.ts +39 -6
- package/dist/protocols/mcp/services/ExternalServerConfigs.d.ts.map +1 -1
- package/dist/protocols/mcp/services/ExternalServerConfigs.js +76 -18
- package/dist/protocols/mcp/services/ExternalServerConfigs.js.map +1 -1
- package/dist/protocols/mcp/tools/AnalyticsTools.js +1 -1
- package/dist/protocols/mcp/tools/AnalyticsTools.js.map +1 -1
- package/dist/protocols/mcp/tools/CodeAnalysisTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/CodeAnalysisTools.js +15 -0
- package/dist/protocols/mcp/tools/CodeAnalysisTools.js.map +1 -1
- package/dist/protocols/mcp/tools/EffectivenessTools.js +3 -3
- package/dist/protocols/mcp/tools/EffectivenessTools.js.map +1 -1
- package/dist/protocols/mcp/tools/GitTools.d.ts +9 -369
- package/dist/protocols/mcp/tools/GitTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/GitTools.js +222 -517
- package/dist/protocols/mcp/tools/GitTools.js.map +1 -1
- package/dist/protocols/mcp/tools/InfrastructureTools.d.ts +56 -24
- package/dist/protocols/mcp/tools/InfrastructureTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/InfrastructureTools.js +117 -26
- package/dist/protocols/mcp/tools/InfrastructureTools.js.map +1 -1
- package/dist/protocols/mcp/tools/MemorySearchTools.d.ts +0 -3
- package/dist/protocols/mcp/tools/MemorySearchTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/MemorySearchTools.js +58 -1
- package/dist/protocols/mcp/tools/MemorySearchTools.js.map +1 -1
- package/dist/protocols/mcp/tools/OrparTools.d.ts +8 -0
- package/dist/protocols/mcp/tools/OrparTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/OrparTools.js +66 -0
- package/dist/protocols/mcp/tools/OrparTools.js.map +1 -1
- package/dist/protocols/mcp/tools/SafetyTools.d.ts +7 -525
- package/dist/protocols/mcp/tools/SafetyTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/SafetyTools.js +349 -514
- package/dist/protocols/mcp/tools/SafetyTools.js.map +1 -1
- package/dist/protocols/mcp/tools/TaskPlanningTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/TaskPlanningTools.js +56 -26
- package/dist/protocols/mcp/tools/TaskPlanningTools.js.map +1 -1
- package/dist/protocols/mcp/tools/TestTools.d.ts +15 -376
- package/dist/protocols/mcp/tools/TestTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/TestTools.js +241 -363
- package/dist/protocols/mcp/tools/TestTools.js.map +1 -1
- package/dist/protocols/mcp/tools/TypeScriptTools.d.ts +6 -276
- package/dist/protocols/mcp/tools/TypeScriptTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/TypeScriptTools.js +223 -337
- package/dist/protocols/mcp/tools/TypeScriptTools.js.map +1 -1
- package/dist/protocols/mcp/tools/WebTools.d.ts +15 -728
- package/dist/protocols/mcp/tools/WebTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/WebTools.js +151 -257
- package/dist/protocols/mcp/tools/WebTools.js.map +1 -1
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.d.ts +5 -3
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.js +31 -6
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.js.map +1 -1
- package/dist/schemas/EventPayloadSchema.d.ts +21 -0
- package/dist/schemas/EventPayloadSchema.d.ts.map +1 -1
- package/dist/schemas/EventPayloadSchema.js +14 -0
- package/dist/schemas/EventPayloadSchema.js.map +1 -1
- package/dist/services/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/services/BackgroundTaskManager.js +12 -4
- package/dist/services/BackgroundTaskManager.js.map +1 -1
- package/dist/services/BrowserManager.d.ts +1 -1
- package/dist/services/BrowserManager.d.ts.map +1 -1
- package/dist/services/BrowserManager.js +26 -3
- package/dist/services/BrowserManager.js.map +1 -1
- package/dist/services/MemoryCompressor.d.ts +11 -2
- package/dist/services/MemoryCompressor.d.ts.map +1 -1
- package/dist/services/MemoryCompressor.js +8 -26
- package/dist/services/MemoryCompressor.js.map +1 -1
- package/dist/services/MemoryService.d.ts +16 -3
- package/dist/services/MemoryService.d.ts.map +1 -1
- package/dist/services/MemoryService.js +79 -55
- package/dist/services/MemoryService.js.map +1 -1
- package/dist/services/MxfMLService.d.ts +5 -0
- package/dist/services/MxfMLService.d.ts.map +1 -1
- package/dist/services/MxfMLService.js +32 -6
- package/dist/services/MxfMLService.js.map +1 -1
- package/dist/services/PredictiveAnalyticsService.d.ts +45 -9
- package/dist/services/PredictiveAnalyticsService.d.ts.map +1 -1
- package/dist/services/PredictiveAnalyticsService.js +82 -44
- package/dist/services/PredictiveAnalyticsService.js.map +1 -1
- package/dist/services/QValueManager.d.ts +7 -0
- package/dist/services/QValueManager.d.ts.map +1 -1
- package/dist/services/QValueManager.js +9 -0
- package/dist/services/QValueManager.js.map +1 -1
- package/dist/services/RewardSignalProcessor.d.ts +38 -1
- package/dist/services/RewardSignalProcessor.d.ts.map +1 -1
- package/dist/services/RewardSignalProcessor.js +109 -7
- package/dist/services/RewardSignalProcessor.js.map +1 -1
- package/dist/services/SharedStateService.d.ts +16 -2
- package/dist/services/SharedStateService.d.ts.map +1 -1
- package/dist/services/SharedStateService.js +44 -3
- package/dist/services/SharedStateService.js.map +1 -1
- package/dist/services/TaskEffectivenessService.d.ts +34 -18
- package/dist/services/TaskEffectivenessService.d.ts.map +1 -1
- package/dist/services/TaskEffectivenessService.js +91 -155
- package/dist/services/TaskEffectivenessService.js.map +1 -1
- package/dist/services/WebSearchService.d.ts.map +1 -1
- package/dist/services/WebSearchService.js.map +1 -1
- package/dist/services/dag/TaskDagService.d.ts.map +1 -1
- package/dist/services/dag/TaskDagService.js +2 -0
- package/dist/services/dag/TaskDagService.js.map +1 -1
- package/dist/services/orpar-memory/OrparMemoryCoordinator.d.ts +7 -0
- package/dist/services/orpar-memory/OrparMemoryCoordinator.d.ts.map +1 -1
- package/dist/services/orpar-memory/OrparMemoryCoordinator.js +71 -10
- package/dist/services/orpar-memory/OrparMemoryCoordinator.js.map +1 -1
- package/dist/types/MemoryUtilityTypes.d.ts +2 -0
- package/dist/types/MemoryUtilityTypes.d.ts.map +1 -1
- package/dist/types/MemoryUtilityTypes.js.map +1 -1
- package/dist/utils/Logger.d.ts +43 -3
- package/dist/utils/Logger.d.ts.map +1 -1
- package/dist/utils/Logger.js +78 -36
- package/dist/utils/Logger.js.map +1 -1
- package/dist/utils/MxpEncryption.d.ts +19 -6
- package/dist/utils/MxpEncryption.d.ts.map +1 -1
- package/dist/utils/MxpEncryption.js +48 -16
- package/dist/utils/MxpEncryption.js.map +1 -1
- package/package.json +19 -4
- package/src/adapters/FrameworkAdapter.ts +1 -1
- package/src/config/ConfigManager.ts +52 -19
- package/src/config/memory-strata.config.ts +136 -0
- package/src/constants/ToolNames.ts +4 -18
- package/src/database/DatabaseAdapterFactory.ts +23 -6
- package/src/database/adapters/mongodb/MongoBaseRepository.ts +16 -2
- package/src/events/ClientEventBus.ts +68 -403
- package/src/events/EventBus.ts +7 -8
- package/src/events/EventBusBase.ts +479 -116
- package/src/events/EventNames.ts +6 -0
- package/src/events/PublicEvents.ts +7 -4
- package/src/events/ServerEventBus.ts +78 -473
- package/src/events/event-definitions/LlmBudgetEvents.ts +74 -0
- package/src/interfaces/IMemoryPersistence.ts +51 -0
- package/src/middleware/MxpMiddleware.ts +51 -24
- package/src/models/channelKey.ts +18 -4
- package/src/models/memoryUtility.ts +64 -0
- package/src/models/user.ts +36 -4
- package/src/mxp/BinaryProtocolLayer.ts +108 -124
- package/src/mxp/ContextCompressionEngine.ts +219 -161
- package/src/mxp/MxpTokenOptimizer.ts +10 -10
- package/src/prompts/MxfAgentSystemPrompt.ts +45 -55
- package/src/protocols/mcp/ToolError.ts +201 -0
- package/src/protocols/mcp/defineTool.ts +379 -0
- package/src/protocols/mcp/security/HttpTargetGuard.ts +267 -0
- package/src/protocols/mcp/security/McpSecurityGuard.ts +10 -3
- package/src/protocols/mcp/security/McpToolPolicy.ts +221 -0
- package/src/protocols/mcp/services/ExternalMcpServerManager.ts +340 -322
- package/src/protocols/mcp/services/ExternalServerConfigs.ts +82 -19
- package/src/protocols/mcp/tools/AnalyticsTools.ts +1 -1
- package/src/protocols/mcp/tools/CodeAnalysisTools.ts +15 -0
- package/src/protocols/mcp/tools/EffectivenessTools.ts +3 -3
- package/src/protocols/mcp/tools/GitTools.ts +290 -569
- package/src/protocols/mcp/tools/InfrastructureTools.ts +189 -43
- package/src/protocols/mcp/tools/MemorySearchTools.ts +77 -1
- package/src/protocols/mcp/tools/OrparTools.ts +87 -0
- package/src/protocols/mcp/tools/SafetyTools.ts +501 -579
- package/src/protocols/mcp/tools/TaskPlanningTools.ts +81 -39
- package/src/protocols/mcp/tools/TestTools.ts +339 -419
- package/src/protocols/mcp/tools/TypeScriptTools.ts +316 -376
- package/src/protocols/mcp/tools/WebTools.ts +209 -307
- package/src/protocols/mcp/tools/shell/ShellExecuteHandler.ts +53 -14
- package/src/schemas/EventPayloadSchema.ts +37 -0
- package/src/services/BackgroundTaskManager.ts +12 -4
- package/src/services/BrowserManager.ts +41 -1
- package/src/services/MemoryCompressor.ts +20 -32
- package/src/services/MemoryService.ts +96 -58
- package/src/services/MxfMLService.ts +41 -7
- package/src/services/PredictiveAnalyticsService.ts +101 -51
- package/src/services/QValueManager.ts +10 -0
- package/src/services/RewardSignalProcessor.ts +138 -9
- package/src/services/SharedStateService.ts +56 -8
- package/src/services/TaskEffectivenessService.ts +114 -187
- package/src/services/WebSearchService.ts +2 -1
- package/src/services/dag/TaskDagService.ts +2 -0
- package/src/services/orpar-memory/OrparMemoryCoordinator.ts +86 -10
- package/src/types/MemoryUtilityTypes.ts +2 -0
- package/src/utils/Logger.ts +116 -40
- package/src/utils/MxpEncryption.ts +69 -34
- package/dist/mxp/MxpEventForwardingEnhancer.d.ts +0 -134
- package/dist/mxp/MxpEventForwardingEnhancer.d.ts.map +0 -1
- package/dist/mxp/MxpEventForwardingEnhancer.js +0 -361
- package/dist/mxp/MxpEventForwardingEnhancer.js.map +0 -1
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.d.ts +0 -56
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.d.ts.map +0 -1
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.js +0 -57
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.js.map +0 -1
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.d.ts +0 -270
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.d.ts.map +0 -1
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.js +0 -256
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.js.map +0 -1
- package/dist/protocols/mcp/tools/LspTools.d.ts +0 -495
- package/dist/protocols/mcp/tools/LspTools.d.ts.map +0 -1
- package/dist/protocols/mcp/tools/LspTools.js +0 -469
- package/dist/protocols/mcp/tools/LspTools.js.map +0 -1
- package/src/mxp/MxpEventForwardingEnhancer.ts +0 -471
- package/src/protocols/mcp/services/ToolSchemaEnhancements.ts +0 -92
- package/src/protocols/mcp/tools/ControlLoopLifecycle.ts +0 -302
- package/src/protocols/mcp/tools/LspTools.ts +0 -476
|
@@ -18,22 +18,154 @@
|
|
|
18
18
|
* @documentation https://mxf-dev.github.io/mxf/
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* TestTools.ts
|
|
23
|
+
*
|
|
24
|
+
* Run a project's test suite with Jest, Mocha or Vitest, or let test_runner pick
|
|
25
|
+
* the one the project actually uses.
|
|
26
|
+
*
|
|
27
|
+
* On exit codes: a test runner exits non-zero when tests fail. That is the tool
|
|
28
|
+
* working — it ran the suite and the suite failed. So a failing suite comes back
|
|
29
|
+
* with `passed: false` and `isError: false`, and `isError` is reserved for the
|
|
30
|
+
* runner not being usable at all.
|
|
31
|
+
*
|
|
32
|
+
* Watch mode is deliberately not offered. These tools are request/response: a
|
|
33
|
+
* watching runner never exits, so the call would hang until it timed out.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import * as fs from 'fs/promises';
|
|
37
|
+
import * as path from 'path';
|
|
38
|
+
|
|
39
|
+
import { TOOL_CATEGORIES } from '../../../constants/ToolNames.js';
|
|
40
|
+
import { defineTool, ToolRunContext } from '../defineTool.js';
|
|
41
|
+
import { ToolError } from '../ToolError.js';
|
|
22
42
|
import { executeShellCommand } from './InfrastructureTools.js';
|
|
23
|
-
import { McpToolHandlerContext, McpToolHandlerResult, McpToolResultContent } from '../McpServerTypes.js';
|
|
24
43
|
|
|
25
|
-
|
|
44
|
+
/** Exit code the shell reports when a command could not be found or spawned. */
|
|
45
|
+
const COMMAND_NOT_FOUND = 127;
|
|
46
|
+
|
|
47
|
+
const workingDirectoryProperty = {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'Working directory path (defaults to the server working directory)'
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Outcome of a test run, in the shape every runner here reports. */
|
|
53
|
+
export interface TestRunResult {
|
|
54
|
+
runner: 'jest' | 'mocha' | 'vitest';
|
|
55
|
+
passed: boolean;
|
|
56
|
+
testsRun: number;
|
|
57
|
+
testsPassed: number;
|
|
58
|
+
testsFailed: number;
|
|
59
|
+
executionTimeMs: number;
|
|
60
|
+
output: string;
|
|
61
|
+
}
|
|
26
62
|
|
|
27
63
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* and
|
|
64
|
+
* Run a test runner via npx and normalize its output.
|
|
65
|
+
*
|
|
66
|
+
* Throws only when the runner could not be executed. A failing suite is a
|
|
67
|
+
* result, and comes back with passed: false.
|
|
32
68
|
*/
|
|
69
|
+
async function runTestRunner(
|
|
70
|
+
runner: 'jest' | 'mocha' | 'vitest',
|
|
71
|
+
runnerArgs: string[],
|
|
72
|
+
workingDirectory: string | undefined,
|
|
73
|
+
context: ToolRunContext
|
|
74
|
+
): Promise<TestRunResult> {
|
|
75
|
+
const startedAt = Date.now();
|
|
76
|
+
|
|
77
|
+
const result = await executeShellCommand('npx', [runner, ...runnerArgs], {
|
|
78
|
+
context: {
|
|
79
|
+
agentId: context.agentId,
|
|
80
|
+
channelId: context.channelId,
|
|
81
|
+
requestId: context.requestId
|
|
82
|
+
},
|
|
83
|
+
workingDirectory: workingDirectory || process.cwd(),
|
|
84
|
+
captureOutput: true
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (result.exitCode === COMMAND_NOT_FOUND) {
|
|
88
|
+
throw ToolError.preconditionFailed(
|
|
89
|
+
`Could not run ${runner} — the command was not found. ` +
|
|
90
|
+
`Install it in the project before running this tool.`,
|
|
91
|
+
{ runner }
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Jest and Vitest write their summary to stderr; Mocha writes to stdout.
|
|
96
|
+
const output = [result.stdout, result.stderr].filter(Boolean).join('\n');
|
|
97
|
+
const counts = parseTestCounts(runner, output);
|
|
33
98
|
|
|
34
|
-
|
|
99
|
+
return {
|
|
100
|
+
runner,
|
|
101
|
+
passed: result.exitCode === 0,
|
|
102
|
+
...counts,
|
|
103
|
+
executionTimeMs: Date.now() - startedAt,
|
|
104
|
+
output
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Pull pass/fail counts out of a runner's summary.
|
|
110
|
+
*
|
|
111
|
+
* Each runner prints a different summary, so each gets its own pattern rather
|
|
112
|
+
* than one regex that half-matches all three.
|
|
113
|
+
*/
|
|
114
|
+
function parseTestCounts(
|
|
115
|
+
runner: 'jest' | 'mocha' | 'vitest',
|
|
116
|
+
output: string
|
|
117
|
+
): { testsRun: number; testsPassed: number; testsFailed: number } {
|
|
118
|
+
if (runner === 'jest') {
|
|
119
|
+
// "Tests: 1 failed, 2 skipped, 17 passed, 20 total"
|
|
120
|
+
const summary = output.match(/^Tests:\s+(.+)$/m)?.[1] ?? '';
|
|
121
|
+
const read = (label: string): number => {
|
|
122
|
+
const match = summary.match(new RegExp(`(\\d+)\\s+${label}`));
|
|
123
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
testsRun: read('total'),
|
|
127
|
+
testsPassed: read('passed'),
|
|
128
|
+
testsFailed: read('failed')
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (runner === 'vitest') {
|
|
133
|
+
// "Tests 1 failed | 17 passed (18)"
|
|
134
|
+
const summary = output.match(/^\s*Tests\s+(.+)$/m)?.[1] ?? '';
|
|
135
|
+
const passed = parseInt(summary.match(/(\d+)\s+passed/)?.[1] ?? '0', 10);
|
|
136
|
+
const failed = parseInt(summary.match(/(\d+)\s+failed/)?.[1] ?? '0', 10);
|
|
137
|
+
const total = parseInt(summary.match(/\((\d+)\)/)?.[1] ?? '0', 10);
|
|
138
|
+
return {
|
|
139
|
+
testsRun: total || passed + failed,
|
|
140
|
+
testsPassed: passed,
|
|
141
|
+
testsFailed: failed
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Mocha: " 17 passing (1s)" / " 1 failing"
|
|
146
|
+
const passed = parseInt(output.match(/(\d+)\s+passing/)?.[1] ?? '0', 10);
|
|
147
|
+
const failed = parseInt(output.match(/(\d+)\s+failing/)?.[1] ?? '0', 10);
|
|
148
|
+
return {
|
|
149
|
+
testsRun: passed + failed,
|
|
150
|
+
testsPassed: passed,
|
|
151
|
+
testsFailed: failed
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const jestTestTool = defineTool<
|
|
156
|
+
{
|
|
157
|
+
testFiles?: string[];
|
|
158
|
+
testNamePattern?: string;
|
|
159
|
+
coverage?: boolean;
|
|
160
|
+
verbose?: boolean;
|
|
161
|
+
maxWorkers?: number;
|
|
162
|
+
workingDirectory?: string;
|
|
163
|
+
},
|
|
164
|
+
TestRunResult
|
|
165
|
+
>({
|
|
35
166
|
name: 'test_jest',
|
|
36
|
-
|
|
167
|
+
category: TOOL_CATEGORIES.INFRASTRUCTURE,
|
|
168
|
+
description: 'Run Jest tests. passed is false when any test fails.',
|
|
37
169
|
inputSchema: {
|
|
38
170
|
type: 'object',
|
|
39
171
|
properties: {
|
|
@@ -44,159 +176,63 @@ export const jestTestTool = {
|
|
|
44
176
|
},
|
|
45
177
|
testNamePattern: {
|
|
46
178
|
type: 'string',
|
|
47
|
-
description: 'Run tests
|
|
179
|
+
description: 'Run only tests whose name matches this pattern'
|
|
48
180
|
},
|
|
49
181
|
coverage: {
|
|
50
182
|
type: 'boolean',
|
|
51
183
|
default: false,
|
|
52
|
-
description: 'Generate
|
|
53
|
-
},
|
|
54
|
-
watchMode: {
|
|
55
|
-
type: 'boolean',
|
|
56
|
-
default: false,
|
|
57
|
-
description: 'Run tests in watch mode'
|
|
184
|
+
description: 'Generate a coverage report'
|
|
58
185
|
},
|
|
59
186
|
verbose: {
|
|
60
187
|
type: 'boolean',
|
|
61
188
|
default: false,
|
|
62
|
-
description: '
|
|
189
|
+
description: 'Report each test individually'
|
|
63
190
|
},
|
|
64
191
|
maxWorkers: {
|
|
65
192
|
type: 'number',
|
|
193
|
+
minimum: 1,
|
|
66
194
|
description: 'Maximum number of worker processes'
|
|
67
195
|
},
|
|
68
|
-
workingDirectory:
|
|
69
|
-
type: 'string',
|
|
70
|
-
description: 'Working directory path (defaults to current directory)'
|
|
71
|
-
}
|
|
196
|
+
workingDirectory: workingDirectoryProperty
|
|
72
197
|
}
|
|
73
198
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
coverage
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (args.verbose) {
|
|
92
|
-
jestArgs.push('--verbose');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (args.maxWorkers) {
|
|
96
|
-
jestArgs.push('--maxWorkers', args.maxWorkers.toString());
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (args.testNamePattern) {
|
|
100
|
-
jestArgs.push('--testNamePattern', args.testNamePattern);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (args.watchMode) {
|
|
104
|
-
jestArgs.push('--watch');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (args.testFiles && args.testFiles.length > 0) {
|
|
108
|
-
jestArgs.push(...args.testFiles);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const result = await executeShellCommand('npx', ['jest', ...jestArgs], {
|
|
112
|
-
workingDirectory: args.workingDirectory || process.cwd(),
|
|
113
|
-
captureOutput: true
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Parse Jest output for test results
|
|
117
|
-
let testsRun = 0;
|
|
118
|
-
let testsPassed = 0;
|
|
119
|
-
let testsFailed = 0;
|
|
120
|
-
let testSuites = 0;
|
|
121
|
-
let testSuitesPassed = 0;
|
|
122
|
-
let testSuitesFailed = 0;
|
|
123
|
-
|
|
124
|
-
if (result.stdout) {
|
|
125
|
-
// Parse test results
|
|
126
|
-
const runMatch = result.stdout.match(/Tests:\s+(\d+)\s+total/);
|
|
127
|
-
const passMatch = result.stdout.match(/Tests:\s+\d+\s+failed,\s+(\d+)\s+passed/);
|
|
128
|
-
const failMatch = result.stdout.match(/Tests:\s+(\d+)\s+failed/);
|
|
129
|
-
|
|
130
|
-
// Parse test suites
|
|
131
|
-
const suitesMatch = result.stdout.match(/Test Suites:\s+(\d+)\s+total/);
|
|
132
|
-
const suitesPassMatch = result.stdout.match(/Test Suites:\s+\d+\s+failed,\s+(\d+)\s+passed/);
|
|
133
|
-
const suitesFailMatch = result.stdout.match(/Test Suites:\s+(\d+)\s+failed/);
|
|
134
|
-
|
|
135
|
-
if (runMatch) testsRun = parseInt(runMatch[1]);
|
|
136
|
-
if (passMatch) testsPassed = parseInt(passMatch[1]);
|
|
137
|
-
if (failMatch) testsFailed = parseInt(failMatch[1]);
|
|
138
|
-
|
|
139
|
-
if (suitesMatch) testSuites = parseInt(suitesMatch[1]);
|
|
140
|
-
if (suitesPassMatch) testSuitesPassed = parseInt(suitesPassMatch[1]);
|
|
141
|
-
if (suitesFailMatch) testSuitesFailed = parseInt(suitesFailMatch[1]);
|
|
142
|
-
|
|
143
|
-
// Alternative parsing for different Jest output formats
|
|
144
|
-
if (testsRun === 0) {
|
|
145
|
-
const totalMatch = result.stdout.match(/(\d+)\s+tests?\s+passed/);
|
|
146
|
-
if (totalMatch) {
|
|
147
|
-
testsRun = parseInt(totalMatch[1]);
|
|
148
|
-
testsPassed = testsRun;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const content: McpToolResultContent = {
|
|
154
|
-
type: 'application/json',
|
|
155
|
-
data: {
|
|
156
|
-
success: result.exitCode === 0,
|
|
157
|
-
runner: 'jest',
|
|
158
|
-
output: result.stdout || '',
|
|
159
|
-
errorOutput: result.stderr || '',
|
|
160
|
-
testsRun,
|
|
161
|
-
testsPassed,
|
|
162
|
-
testsFailed,
|
|
163
|
-
testSuites,
|
|
164
|
-
testSuitesPassed,
|
|
165
|
-
testSuitesFailed,
|
|
166
|
-
executionTime: result.executionTime,
|
|
167
|
-
coverage: args.coverage ? 'Coverage report generated' : null,
|
|
168
|
-
error: result.exitCode !== 0 ? result.stderr : null
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
return { content };
|
|
172
|
-
} catch (error) {
|
|
173
|
-
logger.error('Jest test failed', { error });
|
|
174
|
-
const content: McpToolResultContent = {
|
|
175
|
-
type: 'application/json',
|
|
176
|
-
data: {
|
|
177
|
-
success: false,
|
|
178
|
-
runner: 'jest',
|
|
179
|
-
output: '',
|
|
180
|
-
errorOutput: '',
|
|
181
|
-
testsRun: 0,
|
|
182
|
-
testsPassed: 0,
|
|
183
|
-
testsFailed: 0,
|
|
184
|
-
testSuites: 0,
|
|
185
|
-
testSuitesPassed: 0,
|
|
186
|
-
testSuitesFailed: 0,
|
|
187
|
-
executionTime: 0,
|
|
188
|
-
coverage: null,
|
|
189
|
-
error: error instanceof Error ? error.message : String(error)
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
return { content };
|
|
199
|
+
run: async (input, context) => {
|
|
200
|
+
const args = ['--no-watchman', '--passWithNoTests'];
|
|
201
|
+
|
|
202
|
+
if (input.coverage) {
|
|
203
|
+
args.push('--coverage');
|
|
204
|
+
}
|
|
205
|
+
if (input.verbose) {
|
|
206
|
+
args.push('--verbose');
|
|
207
|
+
}
|
|
208
|
+
if (input.maxWorkers) {
|
|
209
|
+
args.push('--maxWorkers', String(input.maxWorkers));
|
|
210
|
+
}
|
|
211
|
+
if (input.testNamePattern) {
|
|
212
|
+
args.push('--testNamePattern', input.testNamePattern);
|
|
213
|
+
}
|
|
214
|
+
if (input.testFiles && input.testFiles.length > 0) {
|
|
215
|
+
args.push(...input.testFiles);
|
|
193
216
|
}
|
|
217
|
+
|
|
218
|
+
return runTestRunner('jest', args, input.workingDirectory, context);
|
|
194
219
|
}
|
|
195
|
-
};
|
|
220
|
+
});
|
|
196
221
|
|
|
197
|
-
export const mochaTestTool =
|
|
222
|
+
export const mochaTestTool = defineTool<
|
|
223
|
+
{
|
|
224
|
+
testFiles?: string[];
|
|
225
|
+
grep?: string;
|
|
226
|
+
reporter?: string;
|
|
227
|
+
timeout?: number;
|
|
228
|
+
recursive?: boolean;
|
|
229
|
+
workingDirectory?: string;
|
|
230
|
+
},
|
|
231
|
+
TestRunResult
|
|
232
|
+
>({
|
|
198
233
|
name: 'test_mocha',
|
|
199
|
-
|
|
234
|
+
category: TOOL_CATEGORIES.INFRASTRUCTURE,
|
|
235
|
+
description: 'Run Mocha tests. passed is false when any test fails.',
|
|
200
236
|
inputSchema: {
|
|
201
237
|
type: 'object',
|
|
202
238
|
properties: {
|
|
@@ -207,119 +243,62 @@ export const mochaTestTool = {
|
|
|
207
243
|
},
|
|
208
244
|
grep: {
|
|
209
245
|
type: 'string',
|
|
210
|
-
description: 'Run tests matching this pattern'
|
|
246
|
+
description: 'Run only tests matching this pattern'
|
|
211
247
|
},
|
|
212
248
|
reporter: {
|
|
213
249
|
type: 'string',
|
|
214
|
-
enum: ['spec', '
|
|
250
|
+
enum: ['spec', 'dot', 'nyan', 'tap', 'json', 'min'],
|
|
215
251
|
default: 'spec',
|
|
216
|
-
description: '
|
|
252
|
+
description: 'Reporter format'
|
|
217
253
|
},
|
|
218
254
|
timeout: {
|
|
219
255
|
type: 'number',
|
|
220
|
-
|
|
221
|
-
description: '
|
|
256
|
+
minimum: 1,
|
|
257
|
+
description: 'Per-test timeout in milliseconds'
|
|
222
258
|
},
|
|
223
259
|
recursive: {
|
|
224
260
|
type: 'boolean',
|
|
225
261
|
default: false,
|
|
226
262
|
description: 'Look for tests in subdirectories'
|
|
227
263
|
},
|
|
228
|
-
workingDirectory:
|
|
229
|
-
type: 'string',
|
|
230
|
-
description: 'Working directory path (defaults to current directory)'
|
|
231
|
-
}
|
|
264
|
+
workingDirectory: workingDirectoryProperty
|
|
232
265
|
}
|
|
233
266
|
},
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
reporter
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (args.timeout) {
|
|
247
|
-
mochaArgs.push('--timeout', args.timeout.toString());
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (args.grep) {
|
|
251
|
-
mochaArgs.push('--grep', args.grep);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (args.recursive) {
|
|
255
|
-
mochaArgs.push('--recursive');
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (args.testFiles && args.testFiles.length > 0) {
|
|
259
|
-
mochaArgs.push(...args.testFiles);
|
|
260
|
-
} else {
|
|
261
|
-
mochaArgs.push('test/**/*.js');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const result = await executeShellCommand('npx', ['mocha', ...mochaArgs], {
|
|
265
|
-
workingDirectory: args.workingDirectory || process.cwd(),
|
|
266
|
-
captureOutput: true
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// Parse Mocha output for test results
|
|
270
|
-
let testsRun = 0;
|
|
271
|
-
let testsPassed = 0;
|
|
272
|
-
let testsFailed = 0;
|
|
273
|
-
|
|
274
|
-
if (result.stdout) {
|
|
275
|
-
const passMatch = result.stdout.match(/(\d+)\s+passing/);
|
|
276
|
-
const failMatch = result.stdout.match(/(\d+)\s+failing/);
|
|
277
|
-
|
|
278
|
-
if (passMatch) testsPassed = parseInt(passMatch[1]);
|
|
279
|
-
if (failMatch) testsFailed = parseInt(failMatch[1]);
|
|
280
|
-
|
|
281
|
-
testsRun = testsPassed + testsFailed;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const content: McpToolResultContent = {
|
|
285
|
-
type: 'application/json',
|
|
286
|
-
data: {
|
|
287
|
-
success: result.exitCode === 0,
|
|
288
|
-
runner: 'mocha',
|
|
289
|
-
output: result.stdout || '',
|
|
290
|
-
errorOutput: result.stderr || '',
|
|
291
|
-
testsRun,
|
|
292
|
-
testsPassed,
|
|
293
|
-
testsFailed,
|
|
294
|
-
executionTime: result.executionTime,
|
|
295
|
-
error: result.exitCode !== 0 ? result.stderr : null
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
return { content };
|
|
299
|
-
} catch (error) {
|
|
300
|
-
logger.error('Mocha test failed', { error });
|
|
301
|
-
const content: McpToolResultContent = {
|
|
302
|
-
type: 'application/json',
|
|
303
|
-
data: {
|
|
304
|
-
success: false,
|
|
305
|
-
runner: 'mocha',
|
|
306
|
-
output: '',
|
|
307
|
-
errorOutput: '',
|
|
308
|
-
testsRun: 0,
|
|
309
|
-
testsPassed: 0,
|
|
310
|
-
testsFailed: 0,
|
|
311
|
-
executionTime: 0,
|
|
312
|
-
error: error instanceof Error ? error.message : String(error)
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
return { content };
|
|
267
|
+
run: async (input, context) => {
|
|
268
|
+
const args: string[] = [];
|
|
269
|
+
|
|
270
|
+
if (input.reporter) {
|
|
271
|
+
args.push('--reporter', input.reporter);
|
|
272
|
+
}
|
|
273
|
+
if (input.grep) {
|
|
274
|
+
args.push('--grep', input.grep);
|
|
275
|
+
}
|
|
276
|
+
if (input.timeout) {
|
|
277
|
+
args.push('--timeout', String(input.timeout));
|
|
316
278
|
}
|
|
279
|
+
if (input.recursive) {
|
|
280
|
+
args.push('--recursive');
|
|
281
|
+
}
|
|
282
|
+
if (input.testFiles && input.testFiles.length > 0) {
|
|
283
|
+
args.push(...input.testFiles);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return runTestRunner('mocha', args, input.workingDirectory, context);
|
|
317
287
|
}
|
|
318
|
-
};
|
|
288
|
+
});
|
|
319
289
|
|
|
320
|
-
export const vitestTestTool =
|
|
290
|
+
export const vitestTestTool = defineTool<
|
|
291
|
+
{
|
|
292
|
+
testFiles?: string[];
|
|
293
|
+
coverage?: boolean;
|
|
294
|
+
reporter?: string;
|
|
295
|
+
workingDirectory?: string;
|
|
296
|
+
},
|
|
297
|
+
TestRunResult
|
|
298
|
+
>({
|
|
321
299
|
name: 'test_vitest',
|
|
322
|
-
|
|
300
|
+
category: TOOL_CATEGORIES.INFRASTRUCTURE,
|
|
301
|
+
description: 'Run Vitest tests once (not in watch mode). passed is false when any test fails.',
|
|
323
302
|
inputSchema: {
|
|
324
303
|
type: 'object',
|
|
325
304
|
properties: {
|
|
@@ -331,114 +310,99 @@ export const vitestTestTool = {
|
|
|
331
310
|
coverage: {
|
|
332
311
|
type: 'boolean',
|
|
333
312
|
default: false,
|
|
334
|
-
description: 'Generate
|
|
335
|
-
},
|
|
336
|
-
watchMode: {
|
|
337
|
-
type: 'boolean',
|
|
338
|
-
default: false,
|
|
339
|
-
description: 'Run tests in watch mode'
|
|
313
|
+
description: 'Generate a coverage report'
|
|
340
314
|
},
|
|
341
315
|
reporter: {
|
|
342
316
|
type: 'string',
|
|
343
|
-
enum: ['default', 'verbose', 'dot', 'json', '
|
|
317
|
+
enum: ['default', 'verbose', 'dot', 'json', 'tap'],
|
|
344
318
|
default: 'default',
|
|
345
|
-
description: '
|
|
319
|
+
description: 'Reporter format'
|
|
346
320
|
},
|
|
347
|
-
workingDirectory:
|
|
348
|
-
type: 'string',
|
|
349
|
-
description: 'Working directory path (defaults to current directory)'
|
|
350
|
-
}
|
|
321
|
+
workingDirectory: workingDirectoryProperty
|
|
351
322
|
}
|
|
352
323
|
},
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
try {
|
|
362
|
-
const vitestArgs = ['--run']; // Force single run (no watch by default)
|
|
363
|
-
|
|
364
|
-
if (args.coverage) {
|
|
365
|
-
vitestArgs.push('--coverage');
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (args.watchMode) {
|
|
369
|
-
vitestArgs.pop(); // Remove --run
|
|
370
|
-
vitestArgs.push('--watch');
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (args.reporter) {
|
|
374
|
-
vitestArgs.push('--reporter', args.reporter);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (args.testFiles && args.testFiles.length > 0) {
|
|
378
|
-
vitestArgs.push(...args.testFiles);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const result = await executeShellCommand('npx', ['vitest', ...vitestArgs], {
|
|
382
|
-
workingDirectory: args.workingDirectory || process.cwd(),
|
|
383
|
-
captureOutput: true
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
// Parse Vitest output for test results
|
|
387
|
-
let testsRun = 0;
|
|
388
|
-
let testsPassed = 0;
|
|
389
|
-
let testsFailed = 0;
|
|
390
|
-
|
|
391
|
-
if (result.stdout) {
|
|
392
|
-
const testsMatch = result.stdout.match(/Test Files\s+(\d+)\s+passed/);
|
|
393
|
-
const failMatch = result.stdout.match(/Test Files\s+(\d+)\s+failed/);
|
|
394
|
-
|
|
395
|
-
if (testsMatch) testsPassed = parseInt(testsMatch[1]);
|
|
396
|
-
if (failMatch) testsFailed = parseInt(failMatch[1]);
|
|
397
|
-
|
|
398
|
-
testsRun = testsPassed + testsFailed;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const content: McpToolResultContent = {
|
|
402
|
-
type: 'application/json',
|
|
403
|
-
data: {
|
|
404
|
-
success: result.exitCode === 0,
|
|
405
|
-
runner: 'vitest',
|
|
406
|
-
output: result.stdout || '',
|
|
407
|
-
errorOutput: result.stderr || '',
|
|
408
|
-
testsRun,
|
|
409
|
-
testsPassed,
|
|
410
|
-
testsFailed,
|
|
411
|
-
executionTime: result.executionTime,
|
|
412
|
-
coverage: args.coverage ? 'Coverage report generated' : null,
|
|
413
|
-
error: result.exitCode !== 0 ? result.stderr : null
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
return { content };
|
|
417
|
-
} catch (error) {
|
|
418
|
-
logger.error('Vitest test failed', { error });
|
|
419
|
-
const content: McpToolResultContent = {
|
|
420
|
-
type: 'application/json',
|
|
421
|
-
data: {
|
|
422
|
-
success: false,
|
|
423
|
-
runner: 'vitest',
|
|
424
|
-
output: '',
|
|
425
|
-
errorOutput: '',
|
|
426
|
-
testsRun: 0,
|
|
427
|
-
testsPassed: 0,
|
|
428
|
-
testsFailed: 0,
|
|
429
|
-
executionTime: 0,
|
|
430
|
-
coverage: null,
|
|
431
|
-
error: error instanceof Error ? error.message : String(error)
|
|
432
|
-
}
|
|
433
|
-
};
|
|
434
|
-
return { content };
|
|
324
|
+
run: async (input, context) => {
|
|
325
|
+
// `run` is Vitest's non-watching mode. Without it Vitest watches by
|
|
326
|
+
// default and the process would never exit.
|
|
327
|
+
const args = ['run'];
|
|
328
|
+
|
|
329
|
+
if (input.coverage) {
|
|
330
|
+
args.push('--coverage');
|
|
435
331
|
}
|
|
332
|
+
if (input.reporter) {
|
|
333
|
+
args.push('--reporter', input.reporter);
|
|
334
|
+
}
|
|
335
|
+
if (input.testFiles && input.testFiles.length > 0) {
|
|
336
|
+
args.push(...input.testFiles);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return runTestRunner('vitest', args, input.workingDirectory, context);
|
|
436
340
|
}
|
|
437
|
-
};
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Read the project's package.json and work out which runner it uses.
|
|
345
|
+
*
|
|
346
|
+
* Throws when there is no package.json or no known runner in it — guessing
|
|
347
|
+
* "jest" for a project that does not use jest produces a confusing failure two
|
|
348
|
+
* steps later.
|
|
349
|
+
*/
|
|
350
|
+
async function detectFramework(workingDirectory: string): Promise<'jest' | 'mocha' | 'vitest'> {
|
|
351
|
+
const packageJsonPath = path.join(workingDirectory, 'package.json');
|
|
438
352
|
|
|
439
|
-
|
|
353
|
+
let raw: string;
|
|
354
|
+
try {
|
|
355
|
+
raw = await fs.readFile(packageJsonPath, 'utf-8');
|
|
356
|
+
} catch {
|
|
357
|
+
throw ToolError.notFound(
|
|
358
|
+
`No package.json in ${workingDirectory}, so the test framework cannot be detected. ` +
|
|
359
|
+
`Pass framework explicitly, or run this from the project root.`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
let packageJson: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> };
|
|
364
|
+
try {
|
|
365
|
+
packageJson = JSON.parse(raw);
|
|
366
|
+
} catch (parseError) {
|
|
367
|
+
throw ToolError.executionFailed(
|
|
368
|
+
`package.json in ${workingDirectory} is not valid JSON: ` +
|
|
369
|
+
`${parseError instanceof Error ? parseError.message : String(parseError)}`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
374
|
+
|
|
375
|
+
if (deps.vitest) {
|
|
376
|
+
return 'vitest';
|
|
377
|
+
}
|
|
378
|
+
if (deps.jest) {
|
|
379
|
+
return 'jest';
|
|
380
|
+
}
|
|
381
|
+
if (deps.mocha) {
|
|
382
|
+
return 'mocha';
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
throw ToolError.notFound(
|
|
386
|
+
`No supported test framework found in ${packageJsonPath}. ` +
|
|
387
|
+
`Expected one of jest, mocha or vitest in dependencies or devDependencies. ` +
|
|
388
|
+
`Pass framework explicitly to override.`
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export const testRunnerTool = defineTool<
|
|
393
|
+
{
|
|
394
|
+
framework?: 'auto' | 'jest' | 'mocha' | 'vitest';
|
|
395
|
+
testFiles?: string[];
|
|
396
|
+
coverage?: boolean;
|
|
397
|
+
workingDirectory?: string;
|
|
398
|
+
},
|
|
399
|
+
TestRunResult & { detected: boolean }
|
|
400
|
+
>({
|
|
440
401
|
name: 'test_runner',
|
|
441
|
-
|
|
402
|
+
category: TOOL_CATEGORIES.INFRASTRUCTURE,
|
|
403
|
+
description:
|
|
404
|
+
'Run the project\'s tests, detecting whether it uses Jest, Mocha or Vitest from ' +
|
|
405
|
+
'package.json. passed is false when any test fails.',
|
|
442
406
|
inputSchema: {
|
|
443
407
|
type: 'object',
|
|
444
408
|
properties: {
|
|
@@ -446,7 +410,7 @@ export const testRunnerTool = {
|
|
|
446
410
|
type: 'string',
|
|
447
411
|
enum: ['auto', 'jest', 'mocha', 'vitest'],
|
|
448
412
|
default: 'auto',
|
|
449
|
-
description: 'Test framework
|
|
413
|
+
description: 'Test framework. "auto" reads it from package.json.'
|
|
450
414
|
},
|
|
451
415
|
testFiles: {
|
|
452
416
|
type: 'array',
|
|
@@ -456,98 +420,54 @@ export const testRunnerTool = {
|
|
|
456
420
|
coverage: {
|
|
457
421
|
type: 'boolean',
|
|
458
422
|
default: false,
|
|
459
|
-
description: 'Generate
|
|
460
|
-
},
|
|
461
|
-
watchMode: {
|
|
462
|
-
type: 'boolean',
|
|
463
|
-
default: false,
|
|
464
|
-
description: 'Run tests in watch mode'
|
|
423
|
+
description: 'Generate a coverage report'
|
|
465
424
|
},
|
|
466
|
-
workingDirectory:
|
|
467
|
-
type: 'string',
|
|
468
|
-
description: 'Working directory path (defaults to current directory)'
|
|
469
|
-
}
|
|
425
|
+
workingDirectory: workingDirectoryProperty
|
|
470
426
|
}
|
|
471
427
|
},
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
captureOutput: true
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
if (packageJsonResult.exitCode === 0 && packageJsonResult.stdout) {
|
|
492
|
-
try {
|
|
493
|
-
const packageJson = JSON.parse(packageJsonResult.stdout);
|
|
494
|
-
|
|
495
|
-
// Check dependencies and devDependencies
|
|
496
|
-
const allDeps = {
|
|
497
|
-
...packageJson.dependencies,
|
|
498
|
-
...packageJson.devDependencies
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
if (allDeps.vitest) {
|
|
502
|
-
framework = 'vitest';
|
|
503
|
-
} else if (allDeps.jest) {
|
|
504
|
-
framework = 'jest';
|
|
505
|
-
} else if (allDeps.mocha) {
|
|
506
|
-
framework = 'mocha';
|
|
507
|
-
} else {
|
|
508
|
-
framework = 'jest'; // Default fallback
|
|
509
|
-
}
|
|
510
|
-
} catch (parseError) {
|
|
511
|
-
framework = 'jest'; // Default fallback
|
|
512
|
-
}
|
|
513
|
-
} else {
|
|
514
|
-
framework = 'jest'; // Default fallback
|
|
428
|
+
run: async (input, context) => {
|
|
429
|
+
const workingDirectory = input.workingDirectory || process.cwd();
|
|
430
|
+
const requested = input.framework ?? 'auto';
|
|
431
|
+
|
|
432
|
+
const detected = requested === 'auto';
|
|
433
|
+
const framework = detected
|
|
434
|
+
? await detectFramework(workingDirectory)
|
|
435
|
+
: requested;
|
|
436
|
+
|
|
437
|
+
const args: string[] = [];
|
|
438
|
+
|
|
439
|
+
switch (framework) {
|
|
440
|
+
case 'jest':
|
|
441
|
+
args.push('--no-watchman', '--passWithNoTests');
|
|
442
|
+
if (input.coverage) {
|
|
443
|
+
args.push('--coverage');
|
|
515
444
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
return await jestTestTool.handler(args, context);
|
|
522
|
-
case 'mocha':
|
|
523
|
-
return await mochaTestTool.handler(args, context);
|
|
524
|
-
case 'vitest':
|
|
525
|
-
return await vitestTestTool.handler(args, context);
|
|
526
|
-
default:
|
|
527
|
-
throw new Error(`Unknown test framework: ${framework}`);
|
|
528
|
-
}
|
|
529
|
-
} catch (error) {
|
|
530
|
-
logger.error('Universal test runner failed', { error });
|
|
531
|
-
const content: McpToolResultContent = {
|
|
532
|
-
type: 'application/json',
|
|
533
|
-
data: {
|
|
534
|
-
success: false,
|
|
535
|
-
runner: 'unknown',
|
|
536
|
-
output: '',
|
|
537
|
-
errorOutput: '',
|
|
538
|
-
testsRun: 0,
|
|
539
|
-
testsPassed: 0,
|
|
540
|
-
testsFailed: 0,
|
|
541
|
-
executionTime: 0,
|
|
542
|
-
error: error instanceof Error ? error.message : String(error)
|
|
445
|
+
break;
|
|
446
|
+
case 'vitest':
|
|
447
|
+
args.push('run');
|
|
448
|
+
if (input.coverage) {
|
|
449
|
+
args.push('--coverage');
|
|
543
450
|
}
|
|
544
|
-
|
|
545
|
-
|
|
451
|
+
break;
|
|
452
|
+
case 'mocha':
|
|
453
|
+
if (input.coverage) {
|
|
454
|
+
throw ToolError.notImplemented(
|
|
455
|
+
'Mocha does not produce coverage on its own — run it under nyc or c8 instead.'
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (input.testFiles && input.testFiles.length > 0) {
|
|
462
|
+
args.push(...input.testFiles);
|
|
546
463
|
}
|
|
464
|
+
|
|
465
|
+
const result = await runTestRunner(framework, args, workingDirectory, context);
|
|
466
|
+
|
|
467
|
+
return { ...result, detected };
|
|
547
468
|
}
|
|
548
|
-
};
|
|
469
|
+
});
|
|
549
470
|
|
|
550
|
-
// Export all test tools
|
|
551
471
|
export const testTools = [
|
|
552
472
|
jestTestTool,
|
|
553
473
|
mochaTestTool,
|