@skyramp/mcp 0.3.8 → 0.4.0-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 +1 -1
- package/build/commands/commandLibrary.js +3 -3
- package/build/commands/recommendTestsAndExecuteCommand.d.ts +1 -1
- package/build/commands/recommendTestsAndExecuteCommand.js +35 -20
- package/build/commands/testThisEndpointCommand.js +35 -19
- package/build/index.js +9 -3
- package/build/playwright/blueprintDigest.d.ts +15 -0
- package/build/playwright/blueprintDigest.js +152 -0
- package/build/playwright/blueprintDigestStore.d.ts +31 -0
- package/build/playwright/blueprintDigestStore.js +117 -0
- package/build/playwright/registerPlaywrightTools.js +60 -12
- package/build/playwright/traceRecordingPrompt.js +8 -7
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +9 -8
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -2
- package/build/prompts/promptAssets.d.ts +20 -0
- package/build/prompts/promptAssets.js +55 -0
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +19 -5
- package/build/prompts/test-maintenance/actionsInstructions.d.ts +4 -0
- package/build/prompts/test-maintenance/actionsInstructions.js +14 -2
- package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +0 -10
- package/build/prompts/test-maintenance/drift-analysis-prompt.js +2 -11
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +8 -4
- package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +5 -22
- package/build/prompts/test-recommendation/diffExecutionPlan.js +37 -465
- package/build/prompts/test-recommendation/recommendationSections.d.ts +7 -17
- package/build/prompts/test-recommendation/recommendationSections.js +67 -309
- package/build/prompts/test-recommendation/recommendationShared.d.ts +19 -47
- package/build/prompts/test-recommendation/recommendationShared.js +49 -155
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +0 -5
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +10 -153
- package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +2 -29
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +32 -457
- package/build/prompts/testbot/planDeclarations.d.ts +6 -0
- package/build/prompts/testbot/planDeclarations.js +9 -0
- package/build/prompts/testbot/testbot-prompts.d.ts +8 -0
- package/build/prompts/testbot/testbot-prompts.js +256 -381
- package/build/recommendation/answers.d.ts +35 -0
- package/build/recommendation/answers.js +96 -0
- package/build/recommendation/registerPlan.d.ts +49 -0
- package/build/recommendation/registerPlan.js +117 -0
- package/build/recommendation/runVerifiers.d.ts +10 -0
- package/build/recommendation/runVerifiers.js +49 -0
- package/build/recommendation/subjectStep.d.ts +42 -0
- package/build/recommendation/subjectStep.js +86 -0
- package/build/recommendation/types.d.ts +163 -0
- package/build/recommendation/types.js +20 -0
- package/build/recommendation/verifierContracts.d.ts +382 -0
- package/build/recommendation/verifierContracts.js +263 -0
- package/build/recommendation/verifiers/changedFile.d.ts +2 -0
- package/build/recommendation/verifiers/changedFile.js +82 -0
- package/build/recommendation/verifiers/citedPath.d.ts +12 -0
- package/build/recommendation/verifiers/citedPath.js +35 -0
- package/build/recommendation/verifiers/coverage.d.ts +7 -0
- package/build/recommendation/verifiers/coverage.js +617 -0
- package/build/recommendation/verifiers/deliveredMatchesPlan.d.ts +11 -0
- package/build/recommendation/verifiers/deliveredMatchesPlan.js +33 -0
- package/build/recommendation/verifiers/endpointGrounded.d.ts +17 -0
- package/build/recommendation/verifiers/endpointGrounded.js +128 -0
- package/build/recommendation/verifiers/existingCoverage.d.ts +6 -0
- package/build/recommendation/verifiers/existingCoverage.js +51 -0
- package/build/recommendation/verifiers/expectedOutcome.d.ts +31 -0
- package/build/recommendation/verifiers/expectedOutcome.js +105 -0
- package/build/recommendation/verifiers/removedElementGuarded.d.ts +2 -0
- package/build/recommendation/verifiers/removedElementGuarded.js +57 -0
- package/build/recommendation/verifiers/reportedCategory.d.ts +26 -0
- package/build/recommendation/verifiers/reportedCategory.js +84 -0
- package/build/recommendation/verifiers/screenRoute.d.ts +10 -0
- package/build/recommendation/verifiers/screenRoute.js +118 -0
- package/build/recommendation/verifiers/statedDifference.d.ts +6 -0
- package/build/recommendation/verifiers/statedDifference.js +140 -0
- package/build/recommendation/verifiers/uiElementGrounded.d.ts +7 -0
- package/build/recommendation/verifiers/uiElementGrounded.js +318 -0
- package/build/resources/analysisResources.js +1 -114
- package/build/resources/testbotResource.js +23 -13
- package/build/services/ModularizationService.js +2 -1
- package/build/services/TestDiscoveryService.d.ts +3 -72
- package/build/services/TestDiscoveryService.js +10 -303
- package/build/services/containerEnv.d.ts +1 -1
- package/build/services/containerEnv.js +12 -0
- package/build/skills/fixTestImportErrorsSkill.d.ts +13 -0
- package/build/skills/fixTestImportErrorsSkill.js +20 -0
- package/build/toolNames.d.ts +1 -0
- package/build/toolNames.js +1 -0
- package/build/tools/code-refactor/enhanceAssertionsTool.js +3 -3
- package/build/tools/code-refactor/modularizationTool.js +2 -1
- package/build/tools/executeSkyrampTestTool.d.ts +80 -0
- package/build/tools/executeSkyrampTestTool.js +246 -19
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +6 -0
- package/build/tools/generate-tests/generateContractRestTool.js +3 -3
- package/build/tools/generate-tests/planGuard.d.ts +2 -2
- package/build/tools/generate-tests/planGuard.js +78 -18
- package/build/tools/one-click/oneClickTool.d.ts +0 -1
- package/build/tools/one-click/oneClickTool.js +0 -5
- package/build/tools/submitReportTool.d.ts +48 -42
- package/build/tools/submitReportTool.js +576 -193
- package/build/tools/test-management/actionsTool.js +72 -4
- package/build/tools/test-management/analyzeChangesTool.d.ts +144 -48
- package/build/tools/test-management/analyzeChangesTool.js +212 -1219
- package/build/tools/test-management/analyzeTestHealthTool.js +13 -24
- package/build/tools/test-management/index.d.ts +1 -0
- package/build/tools/test-management/index.js +1 -0
- package/build/tools/test-management/registerTestPlanTool.d.ts +795 -172
- package/build/tools/test-management/registerTestPlanTool.js +609 -542
- package/build/tools/test-management/resolveScreenTool.d.ts +75 -0
- package/build/tools/test-management/resolveScreenTool.js +289 -0
- package/build/types/BlueprintDigest.d.ts +34 -0
- package/build/types/BlueprintDigest.js +1 -0
- package/build/types/RepositoryAnalysis.d.ts +20 -1559
- package/build/types/RepositoryAnalysis.js +2 -58
- package/build/types/StepMethod.d.ts +40 -0
- package/build/types/StepMethod.js +77 -0
- package/build/types/TestAnalysis.d.ts +12 -0
- package/build/types/TestExecution.d.ts +4 -0
- package/build/types/TestRecommendation.d.ts +24 -24
- package/build/types/TestRecommendation.js +91 -89
- package/build/types/TestbotPromptOptions.d.ts +0 -4
- package/build/types/TestbotReport.d.ts +64 -2
- package/build/utils/AnalysisStateManager.d.ts +79 -113
- package/build/utils/AnalysisStateManager.js +147 -57
- package/build/utils/assertion-verify/api-shared-lints.js +1 -1
- package/build/utils/assertion-verify/metrics.js +85 -36
- package/build/utils/assertion-verify/ui-lints.d.ts +0 -5
- package/build/utils/assertion-verify/ui-lints.js +32 -0
- package/build/utils/branchDiff.d.ts +63 -31
- package/build/utils/branchDiff.js +242 -94
- package/build/utils/containedPath.d.ts +18 -0
- package/build/utils/containedPath.js +73 -0
- package/build/utils/dartRouteExtractor.d.ts +18 -34
- package/build/utils/dartRouteExtractor.js +101 -173
- package/build/utils/featureFlags.d.ts +12 -0
- package/build/utils/featureFlags.js +14 -0
- package/build/utils/frontendSelectors.d.ts +48 -27
- package/build/utils/frontendSelectors.js +241 -80
- package/build/utils/pathMatching.d.ts +2 -4
- package/build/utils/pathMatching.js +2 -4
- package/build/utils/planMatchKeys.d.ts +38 -47
- package/build/utils/planMatchKeys.js +143 -81
- package/build/utils/rebaselineSnapshots.d.ts +24 -0
- package/build/utils/rebaselineSnapshots.js +65 -0
- package/build/utils/removedUiElements.d.ts +22 -0
- package/build/utils/removedUiElements.js +106 -0
- package/build/utils/reportVerification.d.ts +2 -6
- package/build/utils/reportVerification.js +61 -2
- package/build/utils/screenRoutes.d.ts +66 -0
- package/build/utils/screenRoutes.js +727 -0
- package/build/utils/sourceRouteExtractor.js +320 -112
- package/build/utils/testFileClassification.d.ts +11 -2
- package/build/utils/testFileClassification.js +44 -2
- package/build/utils/testFixtures.d.ts +5 -0
- package/build/utils/testFixtures.js +13 -0
- package/build/utils/utils.d.ts +0 -1
- package/build/utils/utils.js +0 -11
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +12 -12
- package/node_modules/playwright/lib/mcp/skyramp/assertHiddenTool.js +56 -0
- package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +2 -1
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +10 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +4 -1
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +160 -1
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderSignalProcessor.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder.js +5 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.-Id052Lr.js → index.B7KbSQcC.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderSignalProcessor.ts +7 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder.ts +6 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +4 -3
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/plugin.json +6 -0
- package/plugin/prompts/declaring-a-plan.md +20 -0
- package/plugin/prompts/generate-tests/context-fetching.md +4 -0
- package/plugin/prompts/generate-tests/execution-plan.md +63 -0
- package/plugin/prompts/generate-tests/generation.md +108 -0
- package/plugin/prompts/generate-tests/path-parameters.md +1 -0
- package/plugin/prompts/generate-tests/reasoning-protocol.md +17 -0
- package/plugin/prompts/generate-tests/tool-workflow-variants.md +61 -0
- package/plugin/prompts/generate-tests/tool-workflows.md +65 -0
- package/plugin/prompts/plan-tests.md +42 -0
- package/plugin/prompts/testbot-task1.md +82 -0
- package/plugin/skills/fix-test-import-errors/SKILL.md +98 -0
- package/build/prompts/test-recommendation/analysisOutputPrompt.d.ts +0 -84
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +0 -369
- package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +0 -7
- package/build/prompts/test-recommendation/fullRepoCatalog.js +0 -283
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +0 -81
- package/build/prompts/test-recommendation/scopeAssessment.js +0 -359
- package/build/recommendation/budgeters/diversityBalancedBudgeter.d.ts +0 -7
- package/build/recommendation/budgeters/diversityBalancedBudgeter.js +0 -105
- package/build/recommendation/budgeters/fixedNBudgeter.d.ts +0 -7
- package/build/recommendation/budgeters/fixedNBudgeter.js +0 -11
- package/build/recommendation/budgeters/shared.d.ts +0 -32
- package/build/recommendation/budgeters/shared.js +0 -246
- package/build/recommendation/discriminators.d.ts +0 -37
- package/build/recommendation/discriminators.js +0 -379
- package/build/recommendation/diversity.d.ts +0 -47
- package/build/recommendation/diversity.js +0 -101
- package/build/recommendation/planRanker.d.ts +0 -65
- package/build/recommendation/planRanker.js +0 -83
- package/build/recommendation/testFixtures.d.ts +0 -25
- package/build/recommendation/testFixtures.js +0 -45
- package/build/types/FrontendIntegration.d.ts +0 -28
- package/build/types/FrontendIntegration.js +0 -22
- package/build/types/Recommendation.d.ts +0 -146
- package/build/types/Recommendation.js +0 -74
- package/build/utils/changedRoutes.d.ts +0 -29
- package/build/utils/changedRoutes.js +0 -87
- package/build/utils/frontendIntegration.d.ts +0 -9
- package/build/utils/frontendIntegration.js +0 -243
- package/build/utils/importerHop.d.ts +0 -135
- package/build/utils/importerHop.js +0 -489
- package/build/utils/pathAffinityClassification.d.ts +0 -49
- package/build/utils/pathAffinityClassification.js +0 -180
- package/build/utils/pythonMountPrefixes.d.ts +0 -25
- package/build/utils/pythonMountPrefixes.js +0 -347
- package/build/utils/repoScanner.d.ts +0 -34
- package/build/utils/repoScanner.js +0 -300
- package/build/utils/routeParsers.d.ts +0 -95
- package/build/utils/routeParsers.js +0 -951
- package/build/utils/scenarioDrafting.d.ts +0 -92
- package/build/utils/scenarioDrafting.js +0 -951
- package/build/utils/subjectEndpoints.d.ts +0 -19
- package/build/utils/subjectEndpoints.js +0 -98
- package/build/utils/uiPageEnumerator.d.ts +0 -172
- package/build/utils/uiPageEnumerator.js +0 -474
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { ScenarioStep } from "../types/RepositoryAnalysis.js";
|
|
2
|
-
/** A method+path pair extracted from an actually-changed (`+`/`-`) diff line. */
|
|
3
|
-
export interface ChangedRoute {
|
|
4
|
-
method: string;
|
|
5
|
-
path: string;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Extract method+path from every changed (`+`/`-`, non-header) line of a raw
|
|
9
|
-
* unified diff. Reuses `parseRouteLine`, which already strips the leading
|
|
10
|
-
* `+`/`-` marker and matches the same route-decorator patterns the endpoint
|
|
11
|
-
* scanner does.
|
|
12
|
-
*/
|
|
13
|
-
export declare function collectChangedRouteLines(diffText: string): ChangedRoute[];
|
|
14
|
-
/**
|
|
15
|
-
* The first step of `steps` that targets a method+path on the changed hunk, or
|
|
16
|
-
* undefined. Returns the STEP, not a boolean, because two callers need
|
|
17
|
-
* different things from the same comparison: the ranker asks whether any step
|
|
18
|
-
* matches, and the subject resolver needs which ones do.
|
|
19
|
-
*
|
|
20
|
-
* Diff-extracted paths are local to the file's own router declaration (e.g.
|
|
21
|
-
* "/suggestions"); scenario step paths are fully mounted (e.g.
|
|
22
|
-
* "/api/recipes/suggestions"). A local path matches when the step path ends
|
|
23
|
-
* with it at a segment boundary (`pathSuffixMatches`), so the cross-file mount
|
|
24
|
-
* prefix difference (see recoverRemovedEndpointsFromBase, SKYR-4026) does not
|
|
25
|
-
* prevent the match — while a plain `endsWith` would also wrongly match an
|
|
26
|
-
* unrelated path that merely shares a trailing substring (e.g. "/preorders"
|
|
27
|
-
* against "orders").
|
|
28
|
-
*/
|
|
29
|
-
export declare function findStepOnChangedRoute(steps: ScenarioStep[] | undefined, changedRoutes: ChangedRoute[]): ScenarioStep | undefined;
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { parseRouteLine, normalizeDiffPath } from "./routeParsers.js";
|
|
2
|
-
import { isBareIdPath, pathSuffixMatches } from "./pathMatching.js";
|
|
3
|
-
/**
|
|
4
|
-
* Extract method+path from every changed (`+`/`-`, non-header) line of a raw
|
|
5
|
-
* unified diff. Reuses `parseRouteLine`, which already strips the leading
|
|
6
|
-
* `+`/`-` marker and matches the same route-decorator patterns the endpoint
|
|
7
|
-
* scanner does.
|
|
8
|
-
*/
|
|
9
|
-
export function collectChangedRouteLines(diffText) {
|
|
10
|
-
const routes = [];
|
|
11
|
-
let currentFile = "";
|
|
12
|
-
let removedFile = "";
|
|
13
|
-
for (const line of diffText.split("\n")) {
|
|
14
|
-
// Track the current file from the unified-diff header so parseRouteLine
|
|
15
|
-
// gets the real path — its UI-component guard (UI_COMPONENT_EXT) depends
|
|
16
|
-
// on it, or a route-shaped line inside a .tsx/.jsx file (e.g. a client
|
|
17
|
-
// router registration) gets misparsed as a changed backend route.
|
|
18
|
-
// A deleted file has "+++ /dev/null", so the old side is the only name it
|
|
19
|
-
// has. Without it parseRouteLine gets an empty path and every
|
|
20
|
-
// extension-gated framework (Go, Rails, PHP) rejects the line, losing the
|
|
21
|
-
// routes the deletion removed.
|
|
22
|
-
if (line.startsWith("--- ")) {
|
|
23
|
-
const spec = line.slice(4).trim().split("\t")[0];
|
|
24
|
-
removedFile = spec === "/dev/null" ? "" : normalizeDiffPath(spec);
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
if (line.startsWith("+++ ")) {
|
|
28
|
-
const spec = line.slice(4).trim().split("\t")[0];
|
|
29
|
-
currentFile = spec === "/dev/null" ? removedFile : normalizeDiffPath(spec);
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (line.startsWith("diff --git") || line.startsWith("index "))
|
|
33
|
-
continue;
|
|
34
|
-
if (!(line.startsWith("+") || line.startsWith("-")))
|
|
35
|
-
continue;
|
|
36
|
-
const parsed = parseRouteLine(line, currentFile);
|
|
37
|
-
if (parsed)
|
|
38
|
-
routes.push({ method: parsed.method, path: parsed.path });
|
|
39
|
-
}
|
|
40
|
-
return routes;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* The first step of `steps` that targets a method+path on the changed hunk, or
|
|
44
|
-
* undefined. Returns the STEP, not a boolean, because two callers need
|
|
45
|
-
* different things from the same comparison: the ranker asks whether any step
|
|
46
|
-
* matches, and the subject resolver needs which ones do.
|
|
47
|
-
*
|
|
48
|
-
* Diff-extracted paths are local to the file's own router declaration (e.g.
|
|
49
|
-
* "/suggestions"); scenario step paths are fully mounted (e.g.
|
|
50
|
-
* "/api/recipes/suggestions"). A local path matches when the step path ends
|
|
51
|
-
* with it at a segment boundary (`pathSuffixMatches`), so the cross-file mount
|
|
52
|
-
* prefix difference (see recoverRemovedEndpointsFromBase, SKYR-4026) does not
|
|
53
|
-
* prevent the match — while a plain `endsWith` would also wrongly match an
|
|
54
|
-
* unrelated path that merely shares a trailing substring (e.g. "/preorders"
|
|
55
|
-
* against "orders").
|
|
56
|
-
*/
|
|
57
|
-
export function findStepOnChangedRoute(steps, changedRoutes) {
|
|
58
|
-
if (changedRoutes.length === 0)
|
|
59
|
-
return undefined;
|
|
60
|
-
for (const step of steps ?? []) {
|
|
61
|
-
const stepMethod = (step.method ?? "").toUpperCase();
|
|
62
|
-
const stepPath = (step.path ?? "").replace(/\/+$/, "");
|
|
63
|
-
for (const route of changedRoutes) {
|
|
64
|
-
if (route.method.toUpperCase() !== stepMethod)
|
|
65
|
-
continue;
|
|
66
|
-
const routePath = route.path.replace(/\/+$/, "");
|
|
67
|
-
// A diff line declaring nothing but a bare id — `router.delete("/:id")`
|
|
68
|
-
// from a mounted Express router, `@Delete(":id")` from NestJS — names no
|
|
69
|
-
// resource, so it suffix-matches every `DELETE …/{id}` step. Both callers
|
|
70
|
-
// ask "does this changed route touch this step", and the answer there is
|
|
71
|
-
// no: for the ranker it lifted an unrelated candidate over a business-rule
|
|
72
|
-
// one, and for subject resolution it would make every step a subject.
|
|
73
|
-
// A NAMED parameter (`/:order_id`) still matches only its own resource,
|
|
74
|
-
// because the helper unifies a parameter's spelling and not its name.
|
|
75
|
-
if (isBareIdPath(routePath))
|
|
76
|
-
continue;
|
|
77
|
-
if (routePath === "") {
|
|
78
|
-
if (stepPath === "" || stepPath === "/")
|
|
79
|
-
return step;
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (pathSuffixMatches(stepPath, routePath))
|
|
83
|
-
return step;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return undefined;
|
|
87
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { FrontendFileIntegration } from "../types/FrontendIntegration.js";
|
|
2
|
-
export type { FrontendFileIntegration } from "../types/FrontendIntegration.js";
|
|
3
|
-
/**
|
|
4
|
-
* Checks each changed frontend file for a production importer — the
|
|
5
|
-
* deterministic version of the grep the testbot prompt used to ask the
|
|
6
|
-
* agent to run mid-task. See module doc for the signature-based approach
|
|
7
|
-
* and the fail-open rationale.
|
|
8
|
-
*/
|
|
9
|
-
export declare function checkFrontendFileIntegration(repositoryPath: string, changedFrontendFiles: string[]): FrontendFileIntegration[];
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server-side "frontend component integration check" — SKYR-3855.
|
|
3
|
-
*
|
|
4
|
-
* Deterministically answers the question the testbot prompt previously asked
|
|
5
|
-
* the agent to answer via a mid-run grep: "is this changed frontend file
|
|
6
|
-
* actually rendered anywhere in the running app, or is it dead/orphaned
|
|
7
|
-
* code?" Computed here so the skip decision for UI test generation consumes
|
|
8
|
-
* a server fact instead of an LLM judgment call made under budget pressure.
|
|
9
|
-
*
|
|
10
|
-
* Detection greps for the module's PATH-REFERENCE SIGNATURE — the basename
|
|
11
|
-
* preceded by a slash and followed by a quote (`/Foo'`, `/Foo"`, `/Foo\``) —
|
|
12
|
-
* rather than the bare component name. Imports always reference the module
|
|
13
|
-
* path in quotes (`from './Foo'`, `require("components/Foo")`,
|
|
14
|
-
* `import('./Foo')`), so the signature carries import-context precision
|
|
15
|
-
* lexically: no per-line verification, no file reads in Node, and `git grep
|
|
16
|
-
* -l -F` returns only matching paths (bounded output regardless of file
|
|
17
|
-
* sizes). Named imports that go through a barrel resolve one hop: the
|
|
18
|
-
* barrel's `export { Foo } from './Foo'` contains the signature, and the
|
|
19
|
-
* barrel counts as the importer. Known miss: extensionless root-level
|
|
20
|
-
* imports with no slash (`from 'Foo'`) — root files are route/entrypoint
|
|
21
|
-
* short-circuited in practice.
|
|
22
|
-
*
|
|
23
|
-
* Fail-open by design: any scan failure reports `integrated: true` so a bug
|
|
24
|
-
* in this heuristic can never wrongly suppress UI test generation.
|
|
25
|
-
*/
|
|
26
|
-
import { execFileSync } from "child_process";
|
|
27
|
-
import * as fs from "fs";
|
|
28
|
-
import * as path from "path";
|
|
29
|
-
import { isTestFile, isI18nLocaleFile } from "../prompts/test-recommendation/scopeAssessment.js";
|
|
30
|
-
import { logger } from "./logger.js";
|
|
31
|
-
import { walkDir } from "./fileWalk.js";
|
|
32
|
-
import { buildPathSignatures, importsIndexModule, isIndexModule } from "./pathSignatures.js";
|
|
33
|
-
import { IntegrationReason } from "../types/FrontendIntegration.js";
|
|
34
|
-
const MAX_IMPORTERS = 10;
|
|
35
|
-
const MAX_FILES_WALKED = 3000;
|
|
36
|
-
const MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
37
|
-
// Framework route/page/entrypoint conventions — mirrors the testbot prompt's
|
|
38
|
-
// "Never apply the unintegrated heuristic to framework route/entrypoint files"
|
|
39
|
-
// rule. These are reachable by convention, so the grep is skipped entirely.
|
|
40
|
-
// `app` is deliberately NOT in the dir pattern: it is a common generic
|
|
41
|
-
// monorepo directory (e.g. appsmith's top-level app/), and matching it made
|
|
42
|
-
// the check inert for every file in such repos (eval run 28618761653).
|
|
43
|
-
// Next.js App Router route files are still short-circuited via their
|
|
44
|
-
// basenames (page.*, layout.*); non-route components under app/ resolve
|
|
45
|
-
// through the importer grep like any other file.
|
|
46
|
-
const ROUTE_DIR_PATTERN = /(^|\/)(pages|routes)(\/|$)/i;
|
|
47
|
-
// Generic entrypoint basenames (index/main/app/_app) are only reachable-by-
|
|
48
|
-
// convention at the repo root or directly under `src/` — e.g. Vite's
|
|
49
|
-
// `src/main.tsx`, CRA's `src/index.tsx`, Pages Router's root `_app.tsx`.
|
|
50
|
-
// Deeper in the tree these are ordinary component filenames (folder-per-
|
|
51
|
-
// component layouts commonly name the component file `index.tsx`), so
|
|
52
|
-
// matching them anywhere would short-circuit real, checkable components —
|
|
53
|
-
// the same class of bug as the `app/` dir match (see module doc).
|
|
54
|
-
const ROOT_ENTRYPOINT_BASENAME_PATTERN = /^(src\/)?(index|main|app|_app)\.[^/]+$/i;
|
|
55
|
-
// `page`/`layout` are App Router-specific and only meaningful under an
|
|
56
|
-
// `app/` segment; outside of one, a file named `page.tsx` or `layout.tsx`
|
|
57
|
-
// has no special framework meaning and should go through the normal check.
|
|
58
|
-
const APP_ROUTER_BASENAME_PATTERN = /(^|\/)app\/(.*\/)?(page|layout)\.[^/]+$/i;
|
|
59
|
-
// Frontend-importer file types only. Deliberately NOT shared with
|
|
60
|
-
// analyzeChangesTool's SOURCE_EXTS (backend-inclusive: py|java|go|…) or
|
|
61
|
-
// repoScanner's ROUTE_FILE_PATTERN — those detect backend route handlers;
|
|
62
|
-
// this detects files that can render a frontend component. The domains must
|
|
63
|
-
// stay independently tunable (see the `app/` route-dir lesson above).
|
|
64
|
-
const FRONTEND_SOURCE_EXT_PATTERN = /\.(ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/i;
|
|
65
|
-
const STORY_FILE_PATTERN = /\.stories\.[^/]+$/i;
|
|
66
|
-
const EXCLUDED_DIR_PATTERN = /(^|\/)(node_modules|dist|build|\.git)(\/|$)/;
|
|
67
|
-
function isRouteOrEntrypoint(file) {
|
|
68
|
-
return (ROUTE_DIR_PATTERN.test(file) ||
|
|
69
|
-
ROOT_ENTRYPOINT_BASENAME_PATTERN.test(file) ||
|
|
70
|
-
APP_ROUTER_BASENAME_PATTERN.test(file));
|
|
71
|
-
}
|
|
72
|
-
function isExcludedImporterPath(relPath) {
|
|
73
|
-
return (EXCLUDED_DIR_PATTERN.test(relPath) ||
|
|
74
|
-
isTestFile(relPath) ||
|
|
75
|
-
STORY_FILE_PATTERN.test(relPath) ||
|
|
76
|
-
!FRONTEND_SOURCE_EXT_PATTERN.test(relPath));
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Runs `git grep -l -F` for the given fixed-string signatures (OR'd
|
|
80
|
-
* together) scoped to the repository's tracked files. Returns file paths
|
|
81
|
-
* relative to `repositoryPath` — only paths, never matched lines, so output
|
|
82
|
-
* stays bounded no matter how large the matching files are. An exit code of
|
|
83
|
-
* 1 means "no matches" — a normal outcome, not an error. Throws on real
|
|
84
|
-
* failures (not a git repo, git not installed) so the caller can fall back
|
|
85
|
-
* to a manual walk.
|
|
86
|
-
*/
|
|
87
|
-
function gitGrepFiles(repositoryPath, signatures) {
|
|
88
|
-
const args = ["grep", "-l", "-F", "-I"];
|
|
89
|
-
for (const signature of signatures)
|
|
90
|
-
args.push("-e", signature);
|
|
91
|
-
try {
|
|
92
|
-
const stdout = execFileSync("git", args, {
|
|
93
|
-
cwd: repositoryPath,
|
|
94
|
-
encoding: "utf8",
|
|
95
|
-
maxBuffer: 20 * 1024 * 1024,
|
|
96
|
-
});
|
|
97
|
-
return stdout.split("\n").filter(Boolean);
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
if (err && err.status === 1)
|
|
101
|
-
return []; // no matches — not an error
|
|
102
|
-
throw err;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Bounded manual directory walk used only when `git grep` itself fails
|
|
107
|
-
* (e.g. not a git repository). Caps the number and size of files scanned so
|
|
108
|
-
* a huge repo can't turn a fallback path into a multi-minute scan or a
|
|
109
|
-
* large allocation.
|
|
110
|
-
*
|
|
111
|
-
* If the walk is cut off by `MAX_FILES_WALKED` before finding any importer,
|
|
112
|
-
* the "no importer" result can't be trusted — an importer may exist in the
|
|
113
|
-
* unscanned remainder. Per the module's fail-open contract, that ambiguity
|
|
114
|
-
* throws so the caller reports `scan-error` (`integrated: true`) instead of
|
|
115
|
-
* a false "no-importers".
|
|
116
|
-
*/
|
|
117
|
-
function manualWalkGrep(repositoryPath, signatures) {
|
|
118
|
-
const results = [];
|
|
119
|
-
let filesScanned = 0;
|
|
120
|
-
let truncated = false;
|
|
121
|
-
const shouldSkipDir = (_entry, full) => {
|
|
122
|
-
// EXCLUDED_DIR_PATTERN matches on the "/"-normalized relative path.
|
|
123
|
-
const rel = path.relative(repositoryPath, full).replace(/\\/g, "/");
|
|
124
|
-
return EXCLUDED_DIR_PATTERN.test(rel + "/");
|
|
125
|
-
};
|
|
126
|
-
for (const [entry, abs] of walkDir(repositoryPath, { shouldSkipDir })) {
|
|
127
|
-
if (!FRONTEND_SOURCE_EXT_PATTERN.test(entry.name))
|
|
128
|
-
continue;
|
|
129
|
-
if (filesScanned >= MAX_FILES_WALKED) {
|
|
130
|
-
truncated = true;
|
|
131
|
-
break; // stop the entire walk
|
|
132
|
-
}
|
|
133
|
-
filesScanned++;
|
|
134
|
-
// Normalize to forward slashes: on Windows, path.relative() returns
|
|
135
|
-
// "\\"-separated paths, but isTestFile assumes "/".
|
|
136
|
-
const rel = path.relative(repositoryPath, abs).replace(/\\/g, "/");
|
|
137
|
-
try {
|
|
138
|
-
if (fs.statSync(abs).size > MAX_FILE_SIZE_BYTES)
|
|
139
|
-
continue;
|
|
140
|
-
const content = fs.readFileSync(abs, "utf-8");
|
|
141
|
-
if (signatures.some((s) => content.includes(s)))
|
|
142
|
-
results.push(rel);
|
|
143
|
-
}
|
|
144
|
-
catch {
|
|
145
|
-
// Unreadable/vanished file — skip it, keep scanning.
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (truncated && results.length === 0) {
|
|
149
|
-
throw new Error(`manual-walk fallback truncated at ${MAX_FILES_WALKED} files with no importer found`);
|
|
150
|
-
}
|
|
151
|
-
return results;
|
|
152
|
-
}
|
|
153
|
-
function searchProductionImporters(repositoryPath, signatures) {
|
|
154
|
-
try {
|
|
155
|
-
return gitGrepFiles(repositoryPath, signatures);
|
|
156
|
-
}
|
|
157
|
-
catch (err) {
|
|
158
|
-
logger.warning("git grep failed for integration check — falling back to bounded manual walk", {
|
|
159
|
-
repositoryPath,
|
|
160
|
-
error: err instanceof Error ? err.message : String(err),
|
|
161
|
-
});
|
|
162
|
-
return manualWalkGrep(repositoryPath, signatures);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
function checkOneFile(repositoryPath, file) {
|
|
166
|
-
if (isRouteOrEntrypoint(file)) {
|
|
167
|
-
return { file, integrated: true, importers: [], reason: IntegrationReason.RouteOrEntrypoint };
|
|
168
|
-
}
|
|
169
|
-
// i18n/locale content is commonly loaded via a runtime-templated path (e.g.
|
|
170
|
-
// i18next-http-backend's `loadPath: '/locales/{{lng}}/{{ns}}.json'`) rather
|
|
171
|
-
// than a static quoted import — the signature grep below would never find
|
|
172
|
-
// that reference and would misreport a real, live locale file as dead code.
|
|
173
|
-
if (isI18nLocaleFile(file)) {
|
|
174
|
-
return { file, integrated: true, importers: [], reason: IntegrationReason.I18nLocaleContent };
|
|
175
|
-
}
|
|
176
|
-
// A missing changed file (e.g. deleted, or an unexpected diff shape) means
|
|
177
|
-
// the classification can't be trusted — fail open rather than risk wrongly
|
|
178
|
-
// suppressing UI test generation.
|
|
179
|
-
if (!fs.existsSync(path.join(repositoryPath, file))) {
|
|
180
|
-
logger.warning("Integration check: changed file not found on disk — reporting integrated (fail-open)", {
|
|
181
|
-
file,
|
|
182
|
-
});
|
|
183
|
-
return { file, integrated: true, importers: [], reason: IntegrationReason.ScanError };
|
|
184
|
-
}
|
|
185
|
-
try {
|
|
186
|
-
const signatures = buildPathSignatures(file);
|
|
187
|
-
const candidates = searchProductionImporters(repositoryPath, signatures)
|
|
188
|
-
.filter((candidate) => candidate !== file && !isExcludedImporterPath(candidate));
|
|
189
|
-
const importers = isIndexModule(file)
|
|
190
|
-
? selectIndexModuleImporters(repositoryPath, file, candidates)
|
|
191
|
-
: candidates.slice(0, MAX_IMPORTERS);
|
|
192
|
-
return {
|
|
193
|
-
file,
|
|
194
|
-
integrated: importers.length > 0,
|
|
195
|
-
importers,
|
|
196
|
-
reason: importers.length > 0 ? IntegrationReason.Imported : IntegrationReason.NoImporters,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
catch (err) {
|
|
200
|
-
logger.warning("Integration check scan failed — reporting integrated (fail-open)", {
|
|
201
|
-
file,
|
|
202
|
-
error: err instanceof Error ? err.message : String(err),
|
|
203
|
-
});
|
|
204
|
-
return { file, integrated: true, importers: [], reason: IntegrationReason.ScanError };
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* An index module's directory stem (`/_constants'`) is a plain string match, so a
|
|
209
|
-
* file importing its own unrelated `./_constants` also hits. Keep only candidates
|
|
210
|
-
* whose specifier resolves to this module's directory. Reads stop once
|
|
211
|
-
* MAX_IMPORTERS are accepted; files over MAX_FILE_SIZE_BYTES are skipped like in
|
|
212
|
-
* manualWalkGrep; an unreadable candidate is kept (fail-open, like the module).
|
|
213
|
-
*/
|
|
214
|
-
function selectIndexModuleImporters(repositoryPath, file, candidates) {
|
|
215
|
-
const accepted = [];
|
|
216
|
-
for (const candidate of candidates) {
|
|
217
|
-
if (accepted.length >= MAX_IMPORTERS)
|
|
218
|
-
break;
|
|
219
|
-
const abs = path.join(repositoryPath, candidate);
|
|
220
|
-
let content;
|
|
221
|
-
try {
|
|
222
|
-
if (fs.statSync(abs).size > MAX_FILE_SIZE_BYTES)
|
|
223
|
-
continue;
|
|
224
|
-
content = fs.readFileSync(abs, "utf8");
|
|
225
|
-
}
|
|
226
|
-
catch {
|
|
227
|
-
accepted.push(candidate);
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
if (importsIndexModule(candidate, content, file))
|
|
231
|
-
accepted.push(candidate);
|
|
232
|
-
}
|
|
233
|
-
return accepted;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Checks each changed frontend file for a production importer — the
|
|
237
|
-
* deterministic version of the grep the testbot prompt used to ask the
|
|
238
|
-
* agent to run mid-task. See module doc for the signature-based approach
|
|
239
|
-
* and the fail-open rationale.
|
|
240
|
-
*/
|
|
241
|
-
export function checkFrontendFileIntegration(repositoryPath, changedFrontendFiles) {
|
|
242
|
-
return changedFrontendFiles.map((file) => checkOneFile(repositoryPath, file));
|
|
243
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { sliceAddedLinesByFile } from "./branchDiff.js";
|
|
2
|
-
import type { ScannedEndpoint } from "./repoScanner.js";
|
|
3
|
-
/**
|
|
4
|
-
* Derives a singular, lowercase "resource token" from a backend source file
|
|
5
|
-
* path — e.g. "src/dtos/user.dto.ts" -> "user",
|
|
6
|
-
* "modules/orders/order.service.ts" -> "order". Strips the file extension and
|
|
7
|
-
* a trailing framework-layer suffix, then reuses `extractResourceFromPath`'s
|
|
8
|
-
* "last meaningful path segment" rule by treating the remaining slash-joined
|
|
9
|
-
* path as if it were a URL path.
|
|
10
|
-
*/
|
|
11
|
-
export declare function deriveResourceToken(file: string): string;
|
|
12
|
-
/**
|
|
13
|
-
* Derives ALL plausible resource-affinity tokens for a changed file: the
|
|
14
|
-
* filename-derived token from `deriveResourceToken` (when it isn't generic),
|
|
15
|
-
* plus tokens split out of any class/interface/type/enum/schema symbols
|
|
16
|
-
* declared on the file's ADDED diff lines (when `diffHunk` is supplied).
|
|
17
|
-
*
|
|
18
|
-
* This is the sharper anchor requested on PR #588: a generic multi-DTO
|
|
19
|
-
* module (`schemas/actions.py` holding `class DeploymentCreate`, `class
|
|
20
|
-
* FlowCreate`, ...) has no single filename->resource mapping, but the
|
|
21
|
-
* declared symbol name IS the resource — `DeploymentCreate` -> "deployment".
|
|
22
|
-
* Same problem exists for TS (`types/common.ts` holding many interfaces).
|
|
23
|
-
*
|
|
24
|
-
* `diffHunk` is expected to be the file's own added-line slice (e.g. one
|
|
25
|
-
* entry's lines from `sliceAddedLinesByFile`, joined with "\n") — this
|
|
26
|
-
* function does not re-slice a full multi-file diff itself, to avoid
|
|
27
|
-
* duplicating that walking logic in two places.
|
|
28
|
-
*
|
|
29
|
-
* Deduped; capped at `MAX_RESOURCE_TOKENS` so a huge schema file with many
|
|
30
|
-
* declared symbols can't explode the affinity filter into a near-match-all.
|
|
31
|
-
* With no `diffHunk` (or no symbols found), the result is exactly
|
|
32
|
-
* `[deriveResourceToken(file)]` (or `[]` if that's generic/unknown) —
|
|
33
|
-
* identical to today's filename-only behavior.
|
|
34
|
-
*/
|
|
35
|
-
/**
|
|
36
|
-
* Declared symbol names changed in a unified diff — from added-line declarations
|
|
37
|
-
* AND from the hunk-header enclosing type (so a field-only edit to an existing class,
|
|
38
|
-
* e.g. `DeploymentCreate.work_pool_name` → required, still yields `DeploymentCreate`).
|
|
39
|
-
* Returns raw names, used both for resource-token derivation (via `tokensFromSymbol`)
|
|
40
|
-
* and for content-grep matching against test bodies (SKYR-3924).
|
|
41
|
-
*/
|
|
42
|
-
export declare function extractChangedSymbols(diffHunk: string): string[];
|
|
43
|
-
export declare function deriveResourceTokensForFile(file: string, diffHunk?: string): string[];
|
|
44
|
-
/**
|
|
45
|
-
* All singular resource tokens implied by an endpoint path, used for affinity
|
|
46
|
-
* matching against a `deriveResourceToken` result. Shared by the importer-hop
|
|
47
|
-
* and path-affinity fallbacks (SKYR-3857) — the ONLY endpoint-path to
|
|
48
|
-
* resource-token derivation in the codebase, mirroring how
|
|
49
|
-
* `deriveResourceTokensForFile` is the only file-side one:
|
|
50
|
-
* - the primary (first non-skip, non-param, non-version) path segment —
|
|
51
|
-
* catches literal action-style segments like "/users/me", which
|
|
52
|
-
* `extractResourceFromPath`'s "last segment" rule would otherwise resolve
|
|
53
|
-
* to "me" instead of "users". Version-like segments are excluded via
|
|
54
|
-
* `isVersionLikeSegment` — `SKIP_PATH_SEGMENTS` covers "api"/"public" but
|
|
55
|
-
* only v1-v3, so without it any endpoint versioned beyond v3 would derive
|
|
56
|
-
* a nonsense token ("v4") as its resource.
|
|
57
|
-
* - `extractResourceFromPath`'s own result, split on "_" for the sub-resource
|
|
58
|
-
* composite form ("orders_items" -> "order", "item"). That function only
|
|
59
|
-
* recognizes OpenAPI-style "{param}" segments (its dedup-key callers see
|
|
60
|
-
* normalized paths), so ":id"/"[id]"-style params can flow through as its
|
|
61
|
-
* result — filtered here rather than widening a function whose other
|
|
62
|
-
* callers don't need it.
|
|
63
|
-
*/
|
|
64
|
-
export declare function endpointResourceTokens(endpointPath: string): Set<string>;
|
|
65
|
-
export interface ImporterHopResult {
|
|
66
|
-
/** Endpoints mapped from unmatched files, deduped against `alreadyClassifiedEndpoints`. */
|
|
67
|
-
endpoints: ScannedEndpoint[];
|
|
68
|
-
/** Unmatched file -> endpoint paths newly mapped to it (for logging/drafting). */
|
|
69
|
-
byFile: Record<string, string[]>;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Maps unmatched changed files (from `classifyEndpointsByChangedFiles`) to
|
|
73
|
-
* endpoints that import them, when the import target and the endpoint share
|
|
74
|
-
* the same REST resource.
|
|
75
|
-
*
|
|
76
|
-
* `alreadyClassifiedEndpoints` (typically `changedEndpoints` + `newEndpoints`
|
|
77
|
-
* from the same classification pass) is used purely for dedup — an endpoint
|
|
78
|
-
* already classified directly is not duplicated into the returned `endpoints`
|
|
79
|
-
* list, though the mapping is still established (harmless to skip re-adding
|
|
80
|
-
* something already present).
|
|
81
|
-
*
|
|
82
|
-
* `diffContent`, when supplied, is the full unified diff — used to derive
|
|
83
|
-
* per-file added-line hunks for `deriveResourceTokensForFile`'s symbol-based
|
|
84
|
-
* affinity anchors (see module doc, limitation 2). Without it, affinity falls
|
|
85
|
-
* back to the filename-only token, identical to pre-symbol-affinity behavior.
|
|
86
|
-
*/
|
|
87
|
-
export declare function mapUnmatchedFilesToImporterEndpoints(repositoryPath: string, unmatchedFiles: string[], scannedEndpoints: ScannedEndpoint[], alreadyClassifiedEndpoints?: ScannedEndpoint[], diffContent?: string): ImporterHopResult;
|
|
88
|
-
/** Inputs for response-shape scenario drafting, derived from an {@link ImporterHopResult}. */
|
|
89
|
-
export interface HopResponseShapeInputs {
|
|
90
|
-
/** Endpoints to draft response-shape scenarios for. */
|
|
91
|
-
endpoints: ScannedEndpoint[];
|
|
92
|
-
/** Added-field hints per hop-mapped file, for scenario descriptions. */
|
|
93
|
-
addedFieldsByFile: Record<string, string[]>;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Builds the inputs `draftResponseShapeScenarios` needs from an
|
|
97
|
-
* {@link ImporterHopResult}, keyed off `byFile` rather than `endpoints`.
|
|
98
|
-
*
|
|
99
|
-
* `hopResult.endpoints` is deduped against endpoints already present in
|
|
100
|
-
* `changedEndpoints`/`newEndpoints` (see `mapUnmatchedFilesToImporterEndpoints`),
|
|
101
|
-
* so it can be empty — or missing entries — even when `byFile` holds valid
|
|
102
|
-
* path mappings (e.g. a controller and the DTO it imports both changed in the
|
|
103
|
-
* same PR: the controller's endpoint is already classified directly, so the
|
|
104
|
-
* hop drops it from `endpoints`, but `byFile` still records the mapping).
|
|
105
|
-
* Response-shape drafting wants every hop-mapped endpoint regardless of
|
|
106
|
-
* whether it was newly added to the change set, so this re-derives the
|
|
107
|
-
* endpoint list from `scannedEndpoints` filtered to the paths `byFile` maps
|
|
108
|
-
* to, instead of relying on the deduped `endpoints` array.
|
|
109
|
-
*/
|
|
110
|
-
export declare function buildHopResponseShapeInputs(hopResult: ImporterHopResult, scannedEndpoints: ScannedEndpoint[], diffContent: string | undefined): HopResponseShapeInputs;
|
|
111
|
-
/**
|
|
112
|
-
* Extracts likely property/field names from an array of raw added diff lines
|
|
113
|
-
* (each still prefixed with the unified-diff `+` marker). Best-effort regex,
|
|
114
|
-
* not a parser — intended to give a scenario a field name to name-drop, not
|
|
115
|
-
* to be exhaustive.
|
|
116
|
-
*/
|
|
117
|
-
export declare function extractFieldNamesFromAddedLines(lines: string[]): string[];
|
|
118
|
-
export { sliceAddedLinesByFile };
|
|
119
|
-
/**
|
|
120
|
-
* Splits a unified diff into per-file raw sections — every line after a file's
|
|
121
|
-
* `diff --git` header up to the next one — keyed by the `b/` (post-change) path.
|
|
122
|
-
* Unlike `sliceAddedLinesByFile` (added lines only), this preserves the `@@`
|
|
123
|
-
* hunk headers, so `extractChangedSymbols`' enclosing-type extraction (which
|
|
124
|
-
* reads `@@ ... class Foo`) still works when a single file's section is passed
|
|
125
|
-
* to `deriveResourceTokensForFile`. Use this for per-file token derivation that
|
|
126
|
-
* must not mix symbols across files in a multi-file diff (SKYR-3924).
|
|
127
|
-
*/
|
|
128
|
-
export declare function sliceDiffByFile(diffContent: string): Map<string, string>;
|
|
129
|
-
/**
|
|
130
|
-
* Computes added-field names per file for the given files, from a full
|
|
131
|
-
* unified diff. Files with no added-line matches are omitted from the result
|
|
132
|
-
* (an empty field list is a valid, expected outcome — the caller still drafts
|
|
133
|
-
* a scenario, just without field hints).
|
|
134
|
-
*/
|
|
135
|
-
export declare function computeAddedFieldsByFile(diffContent: string, files: string[]): Record<string, string[]>;
|