@skyramp/mcp 0.2.6 → 0.2.8-rc.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/build/commands/commandLibrary.d.ts +12 -0
- package/build/commands/recommendTestsAndExecuteCommand.d.ts +2 -0
- package/build/commands/testThisEndpointCommand.d.ts +2 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +6 -4
- package/build/playwright/PlaywrightTraceService.d.ts +32 -0
- package/build/playwright/index.d.ts +3 -0
- package/build/playwright/registerPlaywrightTools.d.ts +8 -0
- package/build/playwright/registerPlaywrightTools.js +12 -11
- package/build/playwright/traceRecordingPrompt.d.ts +13 -0
- package/build/playwright/traceRecordingPrompt.js +3 -11
- package/build/prompts/code-reuse.d.ts +1 -0
- package/build/prompts/enhance-assertions/contractProviderAssertionsPrompt.d.ts +2 -0
- package/build/prompts/enhance-assertions/integrationAssertionsPrompt.d.ts +2 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +15 -0
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.d.ts +2 -0
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -5
- package/build/prompts/fix-error-prompt.d.ts +1 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.d.ts +1 -0
- package/build/prompts/modularization/integration-test-modularization.d.ts +1 -0
- package/build/prompts/modularization/ui-test-modularization.d.ts +1 -0
- package/build/prompts/personas.d.ts +21 -0
- package/build/prompts/pom-aware-code-reuse.d.ts +1 -0
- package/build/prompts/startTraceCollectionPrompts.d.ts +2 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.d.ts +2 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +114 -0
- package/build/prompts/sut-setup/modes/dockerComposePrompt.d.ts +2 -0
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +292 -0
- package/build/prompts/sut-setup/shared.d.ts +39 -0
- package/build/prompts/sut-setup/shared.js +132 -0
- package/build/prompts/test-maintenance/actionsInstructions.d.ts +35 -0
- package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +4 -0
- package/build/prompts/test-maintenance/driftAnalysisSections.d.ts +13 -0
- package/build/prompts/test-maintenance/driftAnalysisSections.js +9 -3
- package/build/prompts/test-recommendation/analysisOutputPrompt.d.ts +84 -0
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +1 -1
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +1 -0
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +1 -0
- package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +17 -0
- package/build/prompts/test-recommendation/diffExecutionPlan.js +83 -18
- package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +7 -0
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +1 -0
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +1 -0
- package/build/prompts/test-recommendation/promptPlan.d.ts +203 -0
- package/build/prompts/test-recommendation/promptPlan.test.d.ts +1 -0
- package/build/prompts/test-recommendation/recommendationSections.d.ts +35 -0
- package/build/prompts/test-recommendation/recommendationShared.d.ts +31 -0
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +7 -0
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +55 -0
- package/build/prompts/test-recommendation/scopeAssessment.js +2 -2
- package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +1 -0
- package/build/prompts/test-recommendation/scopeAssessment.test.js +1 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +6 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +4 -4
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +1 -0
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +97 -99
- package/build/prompts/testbot/testbot-prompts.d.ts +26 -0
- package/build/prompts/testbot/testbot-prompts.js +301 -46
- package/build/prompts/testbot/testbot-prompts.test.d.ts +1 -0
- package/build/prompts/testbot/testbot-prompts.test.js +109 -117
- package/build/resources/analysisResources.d.ts +17 -0
- package/build/resources/progressResource.d.ts +2 -0
- package/build/resources/sutSetupResource.d.ts +2 -0
- package/build/resources/sutSetupResource.js +45 -0
- package/build/resources/testbotResource.d.ts +2 -0
- package/build/resources/testbotResource.js +2 -2
- package/build/services/AnalyticsService.d.ts +13 -0
- package/build/services/AnalyticsService.test.d.ts +1 -0
- package/build/services/AnalyticsService.test.js +86 -0
- package/build/services/ModularizationService.d.ts +11 -0
- package/build/services/ScenarioGenerationService.d.ts +37 -0
- package/build/services/ScenarioGenerationService.integration.test.d.ts +1 -0
- package/build/services/ScenarioGenerationService.integration.test.js +4 -0
- package/build/services/ScenarioGenerationService.test.d.ts +1 -0
- package/build/services/TestDiscoveryService.d.ts +128 -0
- package/build/services/TestDiscoveryService.js +22 -12
- package/build/services/TestDiscoveryService.test.d.ts +1 -0
- package/build/services/TestDiscoveryService.test.js +4 -3
- package/build/services/TestExecutionService.d.ts +55 -0
- package/build/services/TestExecutionService.js +158 -26
- package/build/services/TestExecutionService.test.d.ts +1 -0
- package/build/services/TestExecutionService.test.js +339 -25
- package/build/services/TestGenerationService.d.ts +56 -0
- package/build/services/TestGenerationService.test.d.ts +1 -0
- package/build/services/TestGenerationService.test.js +10 -8
- package/build/services/containerEnv.d.ts +14 -0
- package/build/tool-phase-coverage.test.d.ts +1 -0
- package/build/tool-phase-coverage.test.js +2 -0
- package/build/tool-phases.d.ts +38 -0
- package/build/tools/auth/loginTool.d.ts +2 -0
- package/build/tools/auth/logoutTool.d.ts +2 -0
- package/build/tools/code-refactor/codeReuseTool.d.ts +2 -0
- package/build/tools/code-refactor/enhanceAssertionsTool.d.ts +2 -0
- package/build/tools/code-refactor/modularizationTool.d.ts +2 -0
- package/build/tools/executeSkyrampTestTool.d.ts +2 -0
- package/build/tools/executeSkyrampTestTool.js +26 -7
- package/build/tools/fixErrorTool.d.ts +2 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.d.ts +45 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +1 -1
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +1 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +11 -9
- package/build/tools/generate-tests/generateContractRestTool.d.ts +31 -0
- package/build/tools/generate-tests/generateE2ERestTool.d.ts +13 -0
- package/build/tools/generate-tests/generateFuzzRestTool.d.ts +10 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +16 -0
- package/build/tools/generate-tests/generateLoadRestTool.d.ts +18 -0
- package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +1 -0
- package/build/tools/generate-tests/generateMockRestTool.d.ts +64 -0
- package/build/tools/generate-tests/generateSmokeRestTool.d.ts +8 -0
- package/build/tools/generate-tests/generateUIRestTool.d.ts +14 -0
- package/build/tools/generate-tests/generateUIRestTool.js +1 -1
- package/build/tools/generate-tests/loadTestSchema.d.ts +39 -0
- package/build/tools/one-click/oneClickTool.d.ts +11 -0
- package/build/tools/submitReportTool.d.ts +394 -0
- package/build/tools/submitReportTool.js +250 -41
- package/build/tools/submitReportTool.test.d.ts +1 -0
- package/build/tools/submitReportTool.test.js +511 -55
- package/build/tools/test-management/actionsTool.d.ts +10 -0
- package/build/tools/test-management/actionsTool.js +21 -7
- package/build/tools/test-management/analyzeChangesTool.d.ts +27 -0
- package/build/tools/test-management/analyzeChangesTool.js +150 -48
- package/build/tools/test-management/analyzeChangesTool.test.d.ts +1 -0
- package/build/tools/test-management/analyzeChangesTool.test.js +95 -78
- package/build/tools/test-management/analyzeTestHealthTool.d.ts +2 -0
- package/build/tools/test-management/analyzeTestHealthTool.js +16 -9
- package/build/tools/test-management/index.d.ts +3 -0
- package/build/tools/test-management/index.js +0 -1
- package/build/tools/trace/resolveSaveStoragePath.d.ts +7 -0
- package/build/tools/trace/resolveSaveStoragePath.test.d.ts +1 -0
- package/build/tools/trace/resolveSessionPaths.d.ts +40 -0
- package/build/tools/trace/resolveSessionPaths.test.d.ts +1 -0
- package/build/tools/trace/sessionState.d.ts +2 -0
- package/build/tools/trace/sessionState.test.d.ts +1 -0
- package/build/tools/trace/startTraceCollectionTool.d.ts +2 -0
- package/build/tools/trace/stopTraceCollectionTool.d.ts +2 -0
- package/build/tools/workspace/initScanWorkspaceTool.d.ts +4 -0
- package/build/tools/workspace/initializeWorkspaceTool.d.ts +2 -0
- package/build/tools/workspace/initializeWorkspaceTool.js +39 -22
- package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +1 -0
- package/build/tools/workspace/initializeWorkspaceTool.test.js +134 -0
- package/build/tools/workspace/serviceUpsert.d.ts +12 -0
- package/build/tools/workspace/serviceUpsert.js +23 -0
- package/build/tools/workspace/serviceUpsert.test.d.ts +1 -0
- package/build/tools/workspace/serviceUpsert.test.js +50 -0
- package/build/types/OneClickCommands.d.ts +68 -0
- package/build/types/RepositoryAnalysis.d.ts +3004 -0
- package/build/types/TestAnalysis.d.ts +97 -0
- package/build/types/TestExecution.d.ts +63 -0
- package/build/types/TestExecution.js +11 -1
- package/build/types/TestRecommendation.d.ts +12 -0
- package/build/types/TestTypes.d.ts +234 -0
- package/build/types/TestbotReport.d.ts +68 -0
- package/build/types/TestbotReport.js +1 -0
- package/build/types/index.d.ts +4 -0
- package/build/types/index.js +3 -0
- package/build/utils/AnalysisStateManager.d.ts +242 -0
- package/build/utils/AnalysisStateManager.js +144 -3
- package/build/utils/AnalysisStateManager.test.d.ts +1 -0
- package/build/utils/AnalysisStateManager.test.js +98 -0
- package/build/utils/analyze-openapi.d.ts +1 -0
- package/build/utils/branchDiff.d.ts +18 -0
- package/build/utils/dartRouteExtractor.d.ts +45 -0
- package/build/utils/dartRouteExtractor.test.d.ts +1 -0
- package/build/utils/docker.d.ts +19 -0
- package/build/utils/docker.test.d.ts +1 -0
- package/build/utils/docker.test.js +10 -9
- package/build/utils/featureFlags.d.ts +37 -0
- package/build/utils/featureFlags.test.d.ts +1 -0
- package/build/utils/gitStaging.d.ts +24 -0
- package/build/utils/gitStaging.test.d.ts +1 -0
- package/build/utils/gitStaging.test.js +13 -7
- package/build/utils/httpDefaults.d.ts +10 -0
- package/build/utils/httpDefaults.test.d.ts +1 -0
- package/build/utils/initAgent.d.ts +36 -0
- package/build/utils/language-helper.d.ts +6 -0
- package/build/utils/logger.d.ts +11 -0
- package/build/utils/normalizeParams.d.ts +14 -0
- package/build/utils/normalizeSkyrampImports.d.ts +13 -0
- package/build/utils/pr-comment-parser.d.ts +31 -0
- package/build/utils/pr-comment-parser.test.d.ts +1 -0
- package/build/utils/pr-comment-parser.test.js +6 -5
- package/build/utils/projectMetadata.d.ts +15 -0
- package/build/utils/projectMetadata.test.d.ts +1 -0
- package/build/utils/proxy-terminal.d.ts +2 -0
- package/build/utils/repoScanner.d.ts +27 -0
- package/build/utils/repoScanner.test.d.ts +1 -0
- package/build/utils/routeParsers.d.ts +53 -0
- package/build/utils/routeParsers.js +31 -38
- package/build/utils/routeParsers.test.d.ts +1 -0
- package/build/utils/scenarioDrafting.d.ts +71 -0
- package/build/utils/scenarioDrafting.js +23 -1
- package/build/utils/scenarioDrafting.test.d.ts +1 -0
- package/build/utils/skyrampMdContent.d.ts +5 -0
- package/build/utils/sourceRouteExtractor.d.ts +48 -0
- package/build/utils/sourceRouteExtractor.test.d.ts +1 -0
- package/build/utils/telemetry.d.ts +32 -0
- package/build/utils/telemetry.test.d.ts +1 -0
- package/build/utils/trace-parser.d.ts +32 -0
- package/build/utils/trace-parser.test.d.ts +6 -0
- package/build/utils/uiPageEnumerator.d.ts +146 -0
- package/build/utils/uiPageEnumerator.js +58 -14
- package/build/utils/uiPageEnumerator.test.d.ts +1 -0
- package/build/utils/uiPageEnumerator.test.js +223 -43
- package/build/utils/utils.d.ts +45 -0
- package/build/utils/utils.js +10 -0
- package/build/utils/utils.test.d.ts +1 -0
- package/build/utils/versions.d.ts +3 -0
- package/build/utils/versions.js +1 -1
- package/build/utils/workspaceAuth.d.ts +127 -0
- package/build/utils/workspaceAuth.test.d.ts +1 -0
- package/build/utils/workspaceAuth.test.js +25 -25
- package/build/workspace/index.d.ts +1 -0
- package/build/workspace/workspace.d.ts +390 -0
- package/build/workspace/workspace.js +7 -7
- package/build/workspace/workspace.test.d.ts +1 -0
- package/build/workspace/workspace.test.js +6 -6
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +31 -13
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +88 -15
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -3
- package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +23 -126
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderApp.js +79 -19
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/aliasRemapper.js +84 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/replayEngine.js +15 -4
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-CPZKmjFR.js → codeMirrorModule-D0BjbCb7.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-lvTRGFx-.js +193 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-B0JOjboO.js → codeMirrorModule-Cqp3cwEJ.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-1anWeyDf.js → defaultSettingsView-CYf9adZh.js} +79 -79
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.Cc5029a3.js → index.B_7ywgmr.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.Wo5yvvVh.js → uiMode.u4_8VnCV.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderApp.ts +129 -43
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/aliasRemapper.ts +119 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/replayEngine.ts +33 -10
- package/node_modules/playwright/package.json +1 -1
- package/package.json +16 -4
- package/build/tools/test-management/uiAnalyzeChangesTool.js +0 -155
- package/build/tools/test-management/uiAnalyzeChangesTool.test.js +0 -147
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.bin/node-which +0 -52
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/is-docker +0 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/mime +0 -46
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/yaml +0 -11
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-CsgPR2wy.js +0 -193
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.CP3Y8Zsb.js +0 -2
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
jest
|
|
1
|
+
import { jest } from "@jest/globals";
|
|
2
|
+
jest.unstable_mockModule("../../workspace/workspace.js", () => ({
|
|
2
3
|
WorkspaceConfigManager: jest.fn(),
|
|
3
4
|
}));
|
|
4
|
-
jest.
|
|
5
|
+
jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
|
|
5
6
|
AnalyticsService: { pushMCPToolEvent: jest.fn() },
|
|
6
7
|
}));
|
|
7
|
-
jest.
|
|
8
|
+
jest.unstable_mockModule("../../playwright/traceRecordingPrompt.js", () => ({
|
|
8
9
|
getTraceRecordingPromptText: () => "",
|
|
9
10
|
}));
|
|
10
|
-
|
|
11
|
+
const { getTestbotPrompt, buildWorkspaceRecoveryPrefix, parseRelatedRepositories } = await import("./testbot-prompts.js");
|
|
11
12
|
// Minimal args to invoke getTestbotPrompt — only services matter for these tests
|
|
12
13
|
const baseArgs = {
|
|
13
14
|
prTitle: "Test PR",
|
|
@@ -24,26 +25,6 @@ function callWithServices(services) {
|
|
|
24
25
|
undefined, // userPrompt
|
|
25
26
|
services);
|
|
26
27
|
}
|
|
27
|
-
function callWithStateOutputFile(stateOutputFile) {
|
|
28
|
-
return getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath, undefined, // baseBranch
|
|
29
|
-
undefined, // maxRecommendations
|
|
30
|
-
undefined, // maxGenerate
|
|
31
|
-
undefined, // maxCritical
|
|
32
|
-
undefined, // prNumber
|
|
33
|
-
undefined, // userPrompt
|
|
34
|
-
undefined, // services
|
|
35
|
-
stateOutputFile);
|
|
36
|
-
}
|
|
37
|
-
function callFollowUpWithStateOutputFile(stateOutputFile) {
|
|
38
|
-
return getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath, undefined, // baseBranch
|
|
39
|
-
undefined, // maxRecommendations
|
|
40
|
-
undefined, // maxGenerate
|
|
41
|
-
undefined, // maxCritical
|
|
42
|
-
undefined, // prNumber
|
|
43
|
-
"add more tests", // userPrompt — triggers follow-up path
|
|
44
|
-
undefined, // services
|
|
45
|
-
stateOutputFile);
|
|
46
|
-
}
|
|
47
28
|
describe("buildServiceContext (via getTestbotPrompt)", () => {
|
|
48
29
|
it("renders full service with all fields", () => {
|
|
49
30
|
const prompt = callWithServices([
|
|
@@ -121,27 +102,6 @@ describe("buildServiceContext (via getTestbotPrompt)", () => {
|
|
|
121
102
|
expect(prompt).toContain("</REPOSITORY PATH>\nUse the Skyramp MCP server tools");
|
|
122
103
|
});
|
|
123
104
|
});
|
|
124
|
-
describe("stateOutputFile in getTestbotPrompt", () => {
|
|
125
|
-
it("includes stateOutputFile in skyramp_analyze_changes call for first-run prompt", () => {
|
|
126
|
-
const stateFile = "/tmp/skyramp/analyze-changes-state.json";
|
|
127
|
-
const prompt = callWithStateOutputFile(stateFile);
|
|
128
|
-
// The prompt must pass stateOutputFile to skyramp_analyze_changes
|
|
129
|
-
expect(prompt).toContain(`\`stateOutputFile\`: "${stateFile}"`);
|
|
130
|
-
});
|
|
131
|
-
it("includes stateOutputFile in skyramp_analyze_changes call for follow-up prompt", () => {
|
|
132
|
-
const stateFile = "/tmp/skyramp/analyze-changes-state.json";
|
|
133
|
-
const prompt = callFollowUpWithStateOutputFile(stateFile);
|
|
134
|
-
expect(prompt).toContain(`\`stateOutputFile\`: "${stateFile}"`);
|
|
135
|
-
});
|
|
136
|
-
it("omits stateOutputFile from skyramp_analyze_changes call when not provided", () => {
|
|
137
|
-
const prompt = callWithStateOutputFile(undefined);
|
|
138
|
-
expect(prompt).not.toContain("stateOutputFile");
|
|
139
|
-
});
|
|
140
|
-
it("omits stateOutputFile from follow-up prompt when not provided", () => {
|
|
141
|
-
const prompt = callFollowUpWithStateOutputFile(undefined);
|
|
142
|
-
expect(prompt).not.toContain("stateOutputFile");
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
105
|
describe("uiCredentials in getTestbotPrompt", () => {
|
|
146
106
|
function callWithUiCredentials(uiCredentials) {
|
|
147
107
|
return getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath, undefined, // baseBranch
|
|
@@ -151,7 +111,6 @@ describe("uiCredentials in getTestbotPrompt", () => {
|
|
|
151
111
|
undefined, // prNumber
|
|
152
112
|
undefined, // userPrompt
|
|
153
113
|
undefined, // services
|
|
154
|
-
undefined, // stateOutputFile
|
|
155
114
|
uiCredentials);
|
|
156
115
|
}
|
|
157
116
|
// The instructions text references `<ui-credentials>` in prose (backticked);
|
|
@@ -216,13 +175,11 @@ describe("drift analysis — runtime tool call (step 3)", () => {
|
|
|
216
175
|
const prompt = basePrompt();
|
|
217
176
|
expect(prompt).toContain("skyramp_actions");
|
|
218
177
|
});
|
|
219
|
-
it("
|
|
178
|
+
it("skyramp_analyze_test_health is referenced in the prompt", () => {
|
|
179
|
+
// After the unified maintenance refactor, analyze_test_health is referenced
|
|
180
|
+
// in the testMaintenance submission guidance rather than as an explicit step.
|
|
220
181
|
const prompt = basePrompt();
|
|
221
|
-
|
|
222
|
-
const healthPos = prompt.indexOf("skyramp_analyze_test_health");
|
|
223
|
-
const task2Pos = prompt.indexOf("## Task 2");
|
|
224
|
-
expect(healthPos).toBeGreaterThan(task1Pos);
|
|
225
|
-
expect(healthPos).toBeLessThan(task2Pos);
|
|
182
|
+
expect(prompt).toContain("skyramp_analyze_test_health");
|
|
226
183
|
});
|
|
227
184
|
it("does not contain the build-time embedded drift_analysis_rules content (Action Decision Tree)", () => {
|
|
228
185
|
// The rules are now fetched at runtime via skyramp_analyze_test_health —
|
|
@@ -237,57 +194,7 @@ describe("drift analysis — runtime tool call (step 3)", () => {
|
|
|
237
194
|
expect(prompt).not.toContain("You are acting as a Skyramp Integration Architect");
|
|
238
195
|
});
|
|
239
196
|
});
|
|
240
|
-
describe("UI grounding via Task 2 capture-act-capture", () => {
|
|
241
|
-
it("surfaces uiContext as guidance, not a contract", () => {
|
|
242
|
-
const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
|
|
243
|
-
// uiContext fields are explained inline so the agent knows what to do with
|
|
244
|
-
// them. Step 1 provides candidate URLs but gives fallback instructions
|
|
245
|
-
// ("navigate from the workspace baseUrl and explore") for 404s/redirects,
|
|
246
|
-
// treating candidates as guidance not a rigid contract.
|
|
247
|
-
expect(prompt).toContain("uiContext");
|
|
248
|
-
expect(prompt).toContain("candidateUiPages");
|
|
249
|
-
expect(prompt).toContain("changedFrontendFiles");
|
|
250
|
-
expect(prompt).toMatch(/navigate from the workspace baseUrl and explore/i);
|
|
251
|
-
});
|
|
252
|
-
it("step 5 enforces Blueprint Citation Invariant in natural prose", () => {
|
|
253
|
-
const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
|
|
254
|
-
// Step 5 is the citation-invariant guardrail, not a "fill in tuples"
|
|
255
|
-
// post-processing step (slice 4 cleanup: recs are grounded upstream).
|
|
256
|
-
expect(prompt).toContain("Blueprint Citation Invariant");
|
|
257
|
-
// Reasoning must be natural prose, NOT internal-identifier syntax.
|
|
258
|
-
expect(prompt).toMatch(/natural prose/i);
|
|
259
|
-
expect(prompt).toMatch(/internal-identifier syntax/i);
|
|
260
|
-
});
|
|
261
|
-
it("Task 2 no longer instructs the agent to fill in tuples post-hoc", () => {
|
|
262
|
-
const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
|
|
263
|
-
// After slice 4 cleanup: Task 2 captures are for trace recording's own
|
|
264
|
-
// assertions, not for retroactively rewriting recommendation reasoning.
|
|
265
|
-
// The phrase "fill in tuples" must NOT appear anywhere in the prompt.
|
|
266
|
-
expect(prompt).not.toMatch(/fill in tuples/i);
|
|
267
|
-
expect(prompt).not.toMatch(/return to step 5 and fill/i);
|
|
268
|
-
});
|
|
269
|
-
it("Task 2 step 5 mentions possibleAssertions as available, NOT as required (slice 5.5 softening)", () => {
|
|
270
|
-
const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
|
|
271
|
-
// Slice 5: AFTER-action browser_blueprint response includes
|
|
272
|
-
// possibleAssertions[] — mechanically translated candidates.
|
|
273
|
-
expect(prompt).toContain("possibleAssertions");
|
|
274
|
-
// Slice 5.5: prompt explicitly tells the agent NOT to feel obligated.
|
|
275
|
-
// Two P09 runs with the prior "emit at least one" directive showed the
|
|
276
|
-
// agent over-using shallow visibility assertions at the expense of
|
|
277
|
-
// integration-test depth. The softened version says: read them, use
|
|
278
|
-
// when they happen to match what you'd write anyway, ignore otherwise.
|
|
279
|
-
expect(prompt).toMatch(/do not feel obligated/i);
|
|
280
|
-
expect(prompt).toMatch(/biased toward visibility/i);
|
|
281
|
-
// The candidate format is still documented.
|
|
282
|
-
expect(prompt).toMatch(/\bcode\b.*\brationale\b.*\btier\b/i);
|
|
283
|
-
// The pre-existing "at least one browser_assert per page navigated"
|
|
284
|
-
// rule should be preserved (it's about meaningful business-outcome
|
|
285
|
-
// assertions, not about possibleAssertions).
|
|
286
|
-
expect(prompt).toMatch(/at least one .browser_assert. per page navigated/i);
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
197
|
describe("buildWorkspaceRecoveryPrefix", () => {
|
|
290
|
-
const { buildWorkspaceRecoveryPrefix } = require("./testbot-prompts.js");
|
|
291
198
|
it("includes repositoryPath in both init_scan and init_workspace instructions", () => {
|
|
292
199
|
const prefix = buildWorkspaceRecoveryPrefix("/home/user/repo");
|
|
293
200
|
expect(prefix).toContain('skyramp_init_scan with workspacePath "/home/user/repo"');
|
|
@@ -312,7 +219,6 @@ describe("testsRepoDir in getTestbotPrompt", () => {
|
|
|
312
219
|
undefined, // prNumber
|
|
313
220
|
undefined, // userPrompt
|
|
314
221
|
undefined, // services
|
|
315
|
-
undefined, // stateOutputFile
|
|
316
222
|
undefined, // uiCredentials
|
|
317
223
|
testsRepoDir);
|
|
318
224
|
}
|
|
@@ -324,7 +230,6 @@ describe("testsRepoDir in getTestbotPrompt", () => {
|
|
|
324
230
|
undefined, // prNumber
|
|
325
231
|
"add more tests", // userPrompt — triggers follow-up path
|
|
326
232
|
undefined, // services
|
|
327
|
-
undefined, // stateOutputFile
|
|
328
233
|
undefined, // uiCredentials
|
|
329
234
|
testsRepoDir);
|
|
330
235
|
}
|
|
@@ -348,20 +253,13 @@ describe("testsRepoDir in getTestbotPrompt", () => {
|
|
|
348
253
|
});
|
|
349
254
|
});
|
|
350
255
|
describe("testbot prompt blueprint-grounded recommendations (slice 4)", () => {
|
|
351
|
-
it("
|
|
352
|
-
const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
|
|
353
|
-
const uiacIdx = prompt.indexOf("skyramp_ui_analyze_changes");
|
|
354
|
-
const acIdx = prompt.indexOf("skyramp_analyze_changes");
|
|
355
|
-
expect(uiacIdx).toBeGreaterThan(-1);
|
|
356
|
-
expect(acIdx).toBeGreaterThan(uiacIdx);
|
|
357
|
-
});
|
|
358
|
-
it("Task 1 step 1 instructs the agent to capture blueprints (without threading them through a param)", () => {
|
|
256
|
+
it("uses a single skyramp_analyze_changes call (no separate pre-flight tool, no blueprint threading)", () => {
|
|
359
257
|
const prompt = getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath);
|
|
360
|
-
//
|
|
361
|
-
|
|
362
|
-
expect(prompt).
|
|
363
|
-
|
|
364
|
-
//
|
|
258
|
+
// The standalone pre-flight tool is gone — its work folded into analyze_changes.
|
|
259
|
+
expect(prompt).not.toContain("skyramp_ui_analyze_changes");
|
|
260
|
+
expect(prompt).toContain("skyramp_analyze_changes");
|
|
261
|
+
// Captures stay in tool-result history — the old capturedBlueprints
|
|
262
|
+
// threading directive must be gone.
|
|
365
263
|
expect(prompt).not.toMatch(/capturedBlueprints/);
|
|
366
264
|
});
|
|
367
265
|
// Flutter support — both the generalised UI trigger wording and the
|
|
@@ -386,3 +284,97 @@ describe("testbot prompt blueprint-grounded recommendations (slice 4)", () => {
|
|
|
386
284
|
expect(exceptionBlock).toContain(".vue");
|
|
387
285
|
});
|
|
388
286
|
});
|
|
287
|
+
describe("relatedRepositories in getTestbotPrompt (multi-repo mode)", () => {
|
|
288
|
+
const PRIMARY_REPO = "letsramp/primary-app";
|
|
289
|
+
const related = [
|
|
290
|
+
{ repo: "letsramp/backend", repositoryPath: "/tmp/skyramp/related-repo-0-backend", baseBranch: "main" },
|
|
291
|
+
{ repo: "letsramp/shared", repositoryPath: "/tmp/skyramp/related-repo-1-shared", baseBranch: "master" },
|
|
292
|
+
];
|
|
293
|
+
function callWithRelated(repos, opts) {
|
|
294
|
+
return getTestbotPrompt(baseArgs.prTitle, baseArgs.prDescription, baseArgs.summaryOutputFile, baseArgs.repositoryPath, undefined, // baseBranch
|
|
295
|
+
undefined, // maxRecommendations
|
|
296
|
+
undefined, // maxGenerate
|
|
297
|
+
undefined, // maxCritical
|
|
298
|
+
undefined, // prNumber
|
|
299
|
+
undefined, // userPrompt
|
|
300
|
+
undefined, // services
|
|
301
|
+
undefined, // uiCredentials
|
|
302
|
+
opts?.testsRepoDir, // testsRepoDir
|
|
303
|
+
repos, opts?.primaryRepo ?? PRIMARY_REPO);
|
|
304
|
+
}
|
|
305
|
+
it("emits a <related_repositories> XML block with repository + path + base_branch per repo", () => {
|
|
306
|
+
const prompt = callWithRelated(related);
|
|
307
|
+
expect(prompt).toContain("<related_repositories>");
|
|
308
|
+
expect(prompt).toContain("</related_repositories>");
|
|
309
|
+
expect(prompt).toContain('<repository repository="letsramp/backend" path="/tmp/skyramp/related-repo-0-backend" base_branch="main" />');
|
|
310
|
+
expect(prompt).toContain('<repository repository="letsramp/shared" path="/tmp/skyramp/related-repo-1-shared" base_branch="master" />');
|
|
311
|
+
// Pure XML — no free-form "repositoryPath:" / "baseBranch:" colon syntax.
|
|
312
|
+
expect(prompt).not.toContain("repositoryPath:");
|
|
313
|
+
expect(prompt).not.toMatch(/baseBranch:/);
|
|
314
|
+
});
|
|
315
|
+
it("emits the primary repo's owner/repo in a <REPOSITORY> block", () => {
|
|
316
|
+
const prompt = callWithRelated(related);
|
|
317
|
+
expect(prompt).toContain(`<REPOSITORY>${PRIMARY_REPO}</REPOSITORY>`);
|
|
318
|
+
});
|
|
319
|
+
it("always emits a base_branch attribute, even when no explicit baseBranch is provided", () => {
|
|
320
|
+
const prompt = callWithRelated([
|
|
321
|
+
{ repo: "letsramp/backend", repositoryPath: "/tmp/skyramp/related-repo-0-backend" },
|
|
322
|
+
]);
|
|
323
|
+
expect(prompt).toContain("<related_repositories>");
|
|
324
|
+
// Absent value → explicit auto-detect marker in the attribute.
|
|
325
|
+
expect(prompt).toContain('<repository repository="letsramp/backend" path="/tmp/skyramp/related-repo-0-backend" base_branch="auto-detect" />');
|
|
326
|
+
});
|
|
327
|
+
it("keeps the unified workspace in the PRIMARY repo even when testsRepoDir is set", () => {
|
|
328
|
+
const prompt = callWithRelated(related, { testsRepoDir: "/tmp/skyramp/test-repo" });
|
|
329
|
+
// workspace.yml is registered in the primary repo regardless of the test repo.
|
|
330
|
+
expect(prompt).toContain(`\`workspacePath\`: "${baseArgs.repositoryPath}"`);
|
|
331
|
+
expect(prompt).not.toContain('`workspacePath`: "/tmp/skyramp/test-repo"');
|
|
332
|
+
// And it makes the primary-vs-test-repo distinction explicit to the agent.
|
|
333
|
+
expect(prompt).toMatch(/NOT in the configured test repo/);
|
|
334
|
+
});
|
|
335
|
+
it("leaves the single-repo prompt unchanged when no related repos are provided", () => {
|
|
336
|
+
const prompt = callWithRelated(undefined);
|
|
337
|
+
expect(prompt).not.toContain("<related_repositories>");
|
|
338
|
+
expect(prompt).not.toContain("MULTI-REPO CONTEXT");
|
|
339
|
+
expect(prompt).not.toContain("MULTI-REPO attribution");
|
|
340
|
+
// The cross-repo round-robin / candidate-pooling language must not leak into
|
|
341
|
+
// single-repo runs (the as-is GENERATE mandate stays authoritative there).
|
|
342
|
+
expect(prompt).not.toMatch(/round-robin/i);
|
|
343
|
+
expect(prompt).not.toContain("Multi-repo exception");
|
|
344
|
+
expect(prompt).not.toMatch(/candidates, not a fixed selection/i);
|
|
345
|
+
});
|
|
346
|
+
it("treats an empty array as single-repo", () => {
|
|
347
|
+
const prompt = callWithRelated([]);
|
|
348
|
+
expect(prompt).not.toContain("<related_repositories>");
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
describe("parseRelatedRepositories", () => {
|
|
352
|
+
it("parses a valid JSON array", () => {
|
|
353
|
+
const parsed = parseRelatedRepositories(JSON.stringify([
|
|
354
|
+
{ repo: "o/a", repositoryPath: "/a", baseBranch: "main" },
|
|
355
|
+
{ repo: "o/b", repositoryPath: "/b" },
|
|
356
|
+
]));
|
|
357
|
+
expect(parsed).toEqual([
|
|
358
|
+
{ repo: "o/a", repositoryPath: "/a", baseBranch: "main" },
|
|
359
|
+
{ repo: "o/b", repositoryPath: "/b", baseBranch: undefined },
|
|
360
|
+
]);
|
|
361
|
+
});
|
|
362
|
+
it("returns undefined for missing input", () => {
|
|
363
|
+
expect(parseRelatedRepositories(undefined)).toBeUndefined();
|
|
364
|
+
expect(parseRelatedRepositories("")).toBeUndefined();
|
|
365
|
+
});
|
|
366
|
+
it("returns undefined for malformed JSON", () => {
|
|
367
|
+
expect(parseRelatedRepositories("{not json")).toBeUndefined();
|
|
368
|
+
});
|
|
369
|
+
it("returns undefined for an empty array", () => {
|
|
370
|
+
expect(parseRelatedRepositories("[]")).toBeUndefined();
|
|
371
|
+
});
|
|
372
|
+
it("drops entries missing a repositoryPath or repo", () => {
|
|
373
|
+
const parsed = parseRelatedRepositories(JSON.stringify([
|
|
374
|
+
{ repo: "o/x", baseBranch: "main" }, // no repositoryPath
|
|
375
|
+
{ repositoryPath: "/no-repo" }, // no repo
|
|
376
|
+
{ repo: "o/ok", repositoryPath: "/ok" },
|
|
377
|
+
]));
|
|
378
|
+
expect(parsed).toEqual([{ repo: "o/ok", repositoryPath: "/ok", baseBranch: undefined }]);
|
|
379
|
+
});
|
|
380
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare const ANALYSIS_URI_PREFIX = "skyramp://analysis";
|
|
3
|
+
/**
|
|
4
|
+
* Register MCP Resources for analysis data access.
|
|
5
|
+
*
|
|
6
|
+
* Resources provide read-only, token-efficient access to analysis data
|
|
7
|
+
* via sessionId. The state file is an internal backing store.
|
|
8
|
+
*
|
|
9
|
+
* URI scheme:
|
|
10
|
+
* skyramp://analysis/{sessionId}/summary
|
|
11
|
+
* skyramp://analysis/{sessionId}/endpoints
|
|
12
|
+
* skyramp://analysis/{sessionId}/endpoints/{+path}
|
|
13
|
+
* skyramp://analysis/{sessionId}/endpoints/{+path}/{method}
|
|
14
|
+
* skyramp://analysis/{sessionId}/scenarios
|
|
15
|
+
* skyramp://analysis/{sessionId}/diff
|
|
16
|
+
*/
|
|
17
|
+
export declare function registerAnalysisResources(server: McpServer): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { logger } from "../utils/logger.js";
|
|
3
|
+
import { resourceText } from "../utils/utils.js";
|
|
4
|
+
import { AnalyticsService } from "../services/AnalyticsService.js";
|
|
5
|
+
import { getAdaptWorkflowPrompt } from "../prompts/sut-setup/modes/adaptWorkflowPrompt.js";
|
|
6
|
+
import { getDockerComposePrompt } from "../prompts/sut-setup/modes/dockerComposePrompt.js";
|
|
7
|
+
import { SutSetupMode } from "../prompts/sut-setup/shared.js";
|
|
8
|
+
export function registerSutSetupResource(server) {
|
|
9
|
+
logger.info("Registering SUT setup resource");
|
|
10
|
+
// RFC 6570 {+rest} (reserved expansion) captures the entire query string
|
|
11
|
+
// including the leading "?". This avoids the SDK's per-param regex which
|
|
12
|
+
// fails on empty query-param values.
|
|
13
|
+
const template = new ResourceTemplate("skyramp://prompts/skyramp_testbot_sut{+rest}", { list: undefined });
|
|
14
|
+
server.registerResource("skyramp_testbot_sut", template, {
|
|
15
|
+
title: "Skyramp Testbot SUT Setup",
|
|
16
|
+
description: "Returns mode-specific instructions for SUT (System Under Test) setup. " +
|
|
17
|
+
"Modes: none, adapt_workflow, docker_compose.",
|
|
18
|
+
mimeType: "text/plain",
|
|
19
|
+
}, async (uri) => {
|
|
20
|
+
const param = (name, fallback) => uri.searchParams.get(name) ?? fallback;
|
|
21
|
+
const mode = param("sutSetupMode", SutSetupMode.None);
|
|
22
|
+
const args = {
|
|
23
|
+
repositoryPath: param("repositoryPath", "."),
|
|
24
|
+
sutSetupMode: mode,
|
|
25
|
+
sutSourceWorkflowFile: param("sutSourceWorkflowFile", ""),
|
|
26
|
+
sutSourceDockerComposeFile: param("sutSourceDockerComposeFile", ""),
|
|
27
|
+
};
|
|
28
|
+
let prompt;
|
|
29
|
+
switch (mode) {
|
|
30
|
+
case SutSetupMode.AdaptWorkflow:
|
|
31
|
+
prompt = getAdaptWorkflowPrompt(args);
|
|
32
|
+
break;
|
|
33
|
+
case SutSetupMode.DockerCompose:
|
|
34
|
+
prompt = getDockerComposePrompt(args);
|
|
35
|
+
break;
|
|
36
|
+
case SutSetupMode.None:
|
|
37
|
+
prompt = "sutSetupMode is 'none'. No SUT setup required.";
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
prompt = `Unknown SUT setup mode: '${mode}'. Valid modes: none, adapt_workflow, docker_compose.`;
|
|
41
|
+
}
|
|
42
|
+
AnalyticsService.pushMCPToolEvent("skyramp_testbot_sut_resource", undefined, { mode }).catch(() => { });
|
|
43
|
+
return resourceText(uri.toString(), prompt);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -2,7 +2,7 @@ import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { logger } from "../utils/logger.js";
|
|
3
3
|
import { AnalyticsService } from "../services/AnalyticsService.js";
|
|
4
4
|
import { MAX_TESTS_TO_GENERATE, MAX_RECOMMENDATIONS, MAX_CRITICAL_TESTS, } from "../prompts/test-recommendation/recommendationSections.js";
|
|
5
|
-
import { getTestbotPrompt, readWorkspaceServices } from "../prompts/testbot/testbot-prompts.js";
|
|
5
|
+
import { getTestbotPrompt, readWorkspaceServices, parseRelatedRepositories, } from "../prompts/testbot/testbot-prompts.js";
|
|
6
6
|
export function registerTestbotResource(server) {
|
|
7
7
|
logger.info("Registering testbot resource");
|
|
8
8
|
// RFC 6570 {+rest} (reserved expansion) captures the entire query string
|
|
@@ -25,7 +25,7 @@ export function registerTestbotResource(server) {
|
|
|
25
25
|
const maxCrit = parseInt(uri.searchParams.get("maxCritical") || "", 10);
|
|
26
26
|
const repositoryPath = param("repositoryPath", ".");
|
|
27
27
|
const services = await readWorkspaceServices(repositoryPath);
|
|
28
|
-
const prompt = getTestbotPrompt(param("prTitle", ""), param("prDescription", ""), param("summaryOutputFile", ""), repositoryPath, uri.searchParams.get("baseBranch") || undefined, isNaN(maxRec) ? MAX_RECOMMENDATIONS : maxRec, isNaN(maxGen) ? MAX_TESTS_TO_GENERATE : maxGen, isNaN(maxCrit) ? MAX_CRITICAL_TESTS : maxCrit, isNaN(prNum) ? undefined : prNum, uri.searchParams.get("userPrompt") || undefined, services.length ? services : undefined, uri.searchParams.get("
|
|
28
|
+
const prompt = getTestbotPrompt(param("prTitle", ""), param("prDescription", ""), param("summaryOutputFile", ""), repositoryPath, uri.searchParams.get("baseBranch") || undefined, isNaN(maxRec) ? MAX_RECOMMENDATIONS : maxRec, isNaN(maxGen) ? MAX_TESTS_TO_GENERATE : maxGen, isNaN(maxCrit) ? MAX_CRITICAL_TESTS : maxCrit, isNaN(prNum) ? undefined : prNum, uri.searchParams.get("userPrompt") || undefined, services.length ? services : undefined, uri.searchParams.get("uiCredentials") || undefined, uri.searchParams.get("testsRepoDir") || undefined, parseRelatedRepositories(uri.searchParams.get("relatedRepositories") || undefined), uri.searchParams.get("primaryRepo") || undefined);
|
|
29
29
|
AnalyticsService.pushMCPToolEvent("skyramp_testbot_prompt", undefined, {}).catch(() => { });
|
|
30
30
|
// Return the original URI — clients may use it to re-fetch the resource,
|
|
31
31
|
// and the caller already has these params. Credentials never appear in
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export declare class AnalyticsService {
|
|
3
|
+
static pushTestGenerationToolEvent(toolName: string, result: CallToolResult, params: Record<string, any>): Promise<void>;
|
|
4
|
+
static pushMCPToolEvent(toolName: string, result: CallToolResult | undefined, params: Record<string, string>): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Track server crash events
|
|
7
|
+
*/
|
|
8
|
+
static pushServerCrashEvent(crashType: string, errorMessage: string, errorStack?: string): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Track tool timeout events
|
|
11
|
+
*/
|
|
12
|
+
static pushToolTimeoutEvent(toolName: string, timeoutMs: number, params: Record<string, string>): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// @ts-nocheck - Jest ESM type inference issues
|
|
2
|
+
import { jest } from "@jest/globals";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
// AnalyticsService resolves the MCP version with
|
|
7
|
+
// `fileURLToPath(import.meta.url)` at module scope. Under ts-jest's old
|
|
8
|
+
// CommonJS transform that top-level import.meta failed to compile with TS1343,
|
|
9
|
+
// so this module — and therefore this test — could not exist. The native-ESM
|
|
10
|
+
// Jest config (SKYR-3799) is what makes it loadable.
|
|
11
|
+
// pushToolEvent lives in @skyramp/skyramp, which pulls the native dylib. Mock
|
|
12
|
+
// it so we can assert on the event payload without loading the binary. Under
|
|
13
|
+
// native ESM jest.mock is not hoisted — use unstable_mockModule + dynamic
|
|
14
|
+
// import of the subject below.
|
|
15
|
+
const pushToolEvent = jest.fn();
|
|
16
|
+
jest.unstable_mockModule("@skyramp/skyramp", () => ({ pushToolEvent }));
|
|
17
|
+
jest.unstable_mockModule("../utils/telemetry.js", () => ({
|
|
18
|
+
getEntryPoint: jest.fn(() => "mcp"),
|
|
19
|
+
getCIPlatform: jest.fn(() => undefined),
|
|
20
|
+
getRepositoryInfo: jest.fn(async () => ({})),
|
|
21
|
+
}));
|
|
22
|
+
jest.unstable_mockModule("../utils/logger.js", () => ({
|
|
23
|
+
logger: { info: jest.fn(), error: jest.fn(), debug: jest.fn(), warn: jest.fn() },
|
|
24
|
+
}));
|
|
25
|
+
const { AnalyticsService } = await import("./AnalyticsService.js");
|
|
26
|
+
const telemetry = await import("../utils/telemetry.js");
|
|
27
|
+
// The version the production code should resolve: package.json two levels up
|
|
28
|
+
// from this module's own location — the same lookup getMCPPackageVersion does
|
|
29
|
+
// via import.meta.url. Computing it the same way here proves the test harness
|
|
30
|
+
// itself supports import.meta now.
|
|
31
|
+
const expectedVersion = JSON.parse(fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), "../../package.json"), "utf8")).version;
|
|
32
|
+
describe("AnalyticsService — import.meta-based version resolution", () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
jest.clearAllMocks();
|
|
35
|
+
telemetry.getEntryPoint.mockReturnValue("mcp");
|
|
36
|
+
telemetry.getCIPlatform.mockReturnValue(undefined);
|
|
37
|
+
telemetry.getRepositoryInfo.mockResolvedValue({});
|
|
38
|
+
});
|
|
39
|
+
it("stamps the package.json version (resolved via import.meta.url) into the event params", async () => {
|
|
40
|
+
const params = {};
|
|
41
|
+
await AnalyticsService.pushMCPToolEvent("skyramp_smoke_test_generation", undefined, params);
|
|
42
|
+
expect(pushToolEvent).toHaveBeenCalledTimes(1);
|
|
43
|
+
const [, toolName, , sentParams] = pushToolEvent.mock.calls[0];
|
|
44
|
+
expect(toolName).toBe("skyramp_smoke_test_generation");
|
|
45
|
+
expect(sentParams.mcpServerVersion).toBe(expectedVersion);
|
|
46
|
+
// Sanity: a real semver-ish string, not an empty/undefined fallback.
|
|
47
|
+
expect(sentParams.mcpServerVersion).toMatch(/^\d+\.\d+\.\d+/);
|
|
48
|
+
});
|
|
49
|
+
it("concatenates error text from an isError result into the event error message", async () => {
|
|
50
|
+
const result = {
|
|
51
|
+
isError: true,
|
|
52
|
+
content: [
|
|
53
|
+
{ type: "text", text: "first failure" },
|
|
54
|
+
{ type: "text", text: "second failure" },
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
await AnalyticsService.pushMCPToolEvent("skyramp_execute_test", result, {});
|
|
58
|
+
const [, , errorMessage] = pushToolEvent.mock.calls[0];
|
|
59
|
+
expect(errorMessage).toBe("first failure, second failure");
|
|
60
|
+
});
|
|
61
|
+
it("sends an empty error message for a successful result", async () => {
|
|
62
|
+
const result = { isError: false, content: [{ type: "text", text: "ok" }] };
|
|
63
|
+
await AnalyticsService.pushMCPToolEvent("skyramp_execute_test", result, {});
|
|
64
|
+
const [, , errorMessage] = pushToolEvent.mock.calls[0];
|
|
65
|
+
expect(errorMessage).toBe("");
|
|
66
|
+
});
|
|
67
|
+
it("includes ciPlatform in the params when running in CI", async () => {
|
|
68
|
+
telemetry.getCIPlatform.mockReturnValue("github_actions");
|
|
69
|
+
const params = {};
|
|
70
|
+
await AnalyticsService.pushMCPToolEvent("skyramp_init_workspace", undefined, params);
|
|
71
|
+
const [, , , sentParams] = pushToolEvent.mock.calls[0];
|
|
72
|
+
expect(sentParams.ciPlatform).toBe("github_actions");
|
|
73
|
+
});
|
|
74
|
+
it("merges repository info into the params", async () => {
|
|
75
|
+
telemetry.getRepositoryInfo.mockResolvedValue({ repoName: "letsramp/mcp", repoOwner: "letsramp" });
|
|
76
|
+
const params = { repositoryPath: "/repo" };
|
|
77
|
+
await AnalyticsService.pushMCPToolEvent("skyramp_analyze_changes", undefined, params);
|
|
78
|
+
expect(telemetry.getRepositoryInfo).toHaveBeenCalledWith("/repo");
|
|
79
|
+
const [, , , sentParams] = pushToolEvent.mock.calls[0];
|
|
80
|
+
expect(sentParams.repoName).toBe("letsramp/mcp");
|
|
81
|
+
});
|
|
82
|
+
it("swallows errors from pushToolEvent (telemetry must never break a tool call)", async () => {
|
|
83
|
+
pushToolEvent.mockRejectedValueOnce(new Error("network down"));
|
|
84
|
+
await expect(AnalyticsService.pushMCPToolEvent("skyramp_smoke_test_generation", undefined, {})).resolves.toBeUndefined();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export interface ModularizationParams {
|
|
3
|
+
testFile: string;
|
|
4
|
+
testType?: string;
|
|
5
|
+
language?: string;
|
|
6
|
+
isTraceBased?: boolean;
|
|
7
|
+
prompt?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class ModularizationService {
|
|
10
|
+
processModularizationRequest(params: ModularizationParams): Promise<CallToolResult>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export interface TraceRequest {
|
|
3
|
+
Source: string;
|
|
4
|
+
Destination: string;
|
|
5
|
+
RequestBody: string;
|
|
6
|
+
ResponseBody: string;
|
|
7
|
+
RequestHeaders: Record<string, string[]>;
|
|
8
|
+
ResponseHeaders: Record<string, string[]>;
|
|
9
|
+
Method: string;
|
|
10
|
+
Path: string;
|
|
11
|
+
QueryParams: Record<string, string[]>;
|
|
12
|
+
StatusCode: number;
|
|
13
|
+
Port: number;
|
|
14
|
+
Timestamp: string;
|
|
15
|
+
Scheme: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ScenarioParams {
|
|
18
|
+
scenarioName: string;
|
|
19
|
+
destination: string;
|
|
20
|
+
apiSchema?: string;
|
|
21
|
+
baseURL?: string;
|
|
22
|
+
method: string;
|
|
23
|
+
path: string;
|
|
24
|
+
requestBody?: string;
|
|
25
|
+
queryParams?: string;
|
|
26
|
+
responseBody?: string;
|
|
27
|
+
statusCode?: number;
|
|
28
|
+
outputDir: string;
|
|
29
|
+
authHeader?: string;
|
|
30
|
+
authScheme?: string;
|
|
31
|
+
authToken?: string;
|
|
32
|
+
responseHeaders?: Record<string, string[]>;
|
|
33
|
+
}
|
|
34
|
+
export declare class ScenarioGenerationService {
|
|
35
|
+
parseScenario(params: ScenarioParams): Promise<CallToolResult>;
|
|
36
|
+
generateTraceRequestFromInput(params: ScenarioParams): TraceRequest | null;
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Requires: @skyramp/skyramp native dylib (skips if not available)
|
|
6
6
|
*/
|
|
7
|
+
import { jest } from "@jest/globals";
|
|
7
8
|
import fs from "fs";
|
|
8
9
|
import path from "path";
|
|
9
10
|
import os from "os";
|
|
11
|
+
import { createRequire } from "module";
|
|
10
12
|
import { ScenarioGenerationService } from "./ScenarioGenerationService.js";
|
|
13
|
+
// Native dylib is a CommonJS addon — load it via createRequire under ESM.
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
11
15
|
let SkyrampClient;
|
|
12
16
|
try {
|
|
13
17
|
SkyrampClient = require("@skyramp/skyramp").SkyrampClient;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|