@qulib/mcp 0.5.2 → 0.6.0
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/dist/analyze-app-mcp-payload.d.ts +12 -0
- package/dist/analyze-app-mcp-payload.d.ts.map +1 -0
- package/dist/analyze-app-mcp-payload.js +12 -0
- package/dist/index.js +10 -3
- package/package.json +3 -3
- package/dist/compact-analyze-payload.d.ts +0 -164
- package/dist/compact-analyze-payload.d.ts.map +0 -1
- package/dist/compact-analyze-payload.js +0 -115
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AnalyzeResult } from '@qulib/core';
|
|
2
|
+
export interface AnalyzeAppMcpPayloadOptions {
|
|
3
|
+
includeFullReport?: boolean;
|
|
4
|
+
/** When true, returns only `toAgentSummary(result)` JSON (QLIB-001). Ignores `includeFullReport`. */
|
|
5
|
+
agentSummary?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Single place for analyze_app response shaping: default summary-first,
|
|
9
|
+
* optional full report, or compact agent gate summary.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildAnalyzeAppMcpPayload(result: AnalyzeResult, input: AnalyzeAppMcpPayloadOptions): unknown;
|
|
12
|
+
//# sourceMappingURL=analyze-app-mcp-payload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-app-mcp-payload.d.ts","sourceRoot":"","sources":["../src/analyze-app-mcp-payload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,MAAM,WAAW,2BAA2B;IAC1C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,qGAAqG;IACrG,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAKT"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { toAgentSummary } from '@qulib/core';
|
|
2
|
+
import { summarizeAnalyzeResult } from './summarize-analyze-result.js';
|
|
3
|
+
/**
|
|
4
|
+
* Single place for analyze_app response shaping: default summary-first,
|
|
5
|
+
* optional full report, or compact agent gate summary.
|
|
6
|
+
*/
|
|
7
|
+
export function buildAnalyzeAppMcpPayload(result, input) {
|
|
8
|
+
if (input.agentSummary === true) {
|
|
9
|
+
return toAgentSummary(result);
|
|
10
|
+
}
|
|
11
|
+
return summarizeAnalyzeResult(result, input.includeFullReport === true);
|
|
12
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ const requirePkg = createRequire(import.meta.url);
|
|
|
16
16
|
const pkg = requirePkg('../package.json');
|
|
17
17
|
import { analyzeApp, detectAuth, exploreAuth, scanRepo, computeAutomationMaturity, } from '@qulib/core';
|
|
18
18
|
import { z } from 'zod';
|
|
19
|
-
import {
|
|
19
|
+
import { buildAnalyzeAppMcpPayload } from './analyze-app-mcp-payload.js';
|
|
20
20
|
import { log } from './logger.js';
|
|
21
21
|
function toolError(code, message, detail) {
|
|
22
22
|
return {
|
|
@@ -68,6 +68,10 @@ const AnalyzeInputSchema = z.object({
|
|
|
68
68
|
timeoutMs: z.number().int().positive().optional(),
|
|
69
69
|
auth: z.discriminatedUnion('type', [FormLoginMcpAuthSchema, StorageStateMcpAuthSchema]).optional(),
|
|
70
70
|
includeFullReport: z.boolean().optional(),
|
|
71
|
+
agentSummary: z
|
|
72
|
+
.boolean()
|
|
73
|
+
.optional()
|
|
74
|
+
.describe('When true, return only the versioned agent-summary JSON ({ schemaVersion: 1, gate, coverageStatus, topRisks, recommendedNextChecks, honestyNotes, costSummary, deterministicFollowUps }). Use this for CI gates and orchestrators that need a single small payload to decide pass/warn/fail. Overrides includeFullReport.'),
|
|
71
75
|
llmTokenBudget: z.number().int().positive().optional(),
|
|
72
76
|
llmMaxOutputTokensPerCall: z.number().int().positive().optional(),
|
|
73
77
|
testGenerationLimit: z.number().int().positive().max(50).optional(),
|
|
@@ -153,7 +157,7 @@ mcpServer.registerTool('detect_auth', {
|
|
|
153
157
|
}
|
|
154
158
|
});
|
|
155
159
|
mcpServer.registerTool('analyze_app', {
|
|
156
|
-
description: 'Analyze a deployed web app for quality gaps. Default response is summary-first (top gaps, cost summary, next checks). Set includeFullReport for the full gapAnalysis. Optional llmMaxOutputTokensPerCall / llmTokenBudget (legacy), testGenerationLimit, enableLlmScenarios align with @qulib/core HarnessConfig.',
|
|
160
|
+
description: 'Analyze a deployed web app for quality gaps. Default response is summary-first (top gaps, cost summary, next checks). Set includeFullReport for the full gapAnalysis. Set agentSummary for the compact gate-decision payload (pass/warn/fail with honesty notes) — use this when calling from a CI gate or orchestrator. Optional llmMaxOutputTokensPerCall / llmTokenBudget (legacy), testGenerationLimit, enableLlmScenarios align with @qulib/core HarnessConfig.',
|
|
157
161
|
inputSchema: AnalyzeInputSchema,
|
|
158
162
|
}, async (input) => {
|
|
159
163
|
try {
|
|
@@ -202,7 +206,10 @@ mcpServer.registerTool('analyze_app', {
|
|
|
202
206
|
progressLog: mcpProgressLog,
|
|
203
207
|
telemetry: telemetrySink,
|
|
204
208
|
});
|
|
205
|
-
const payload =
|
|
209
|
+
const payload = buildAnalyzeAppMcpPayload(result, {
|
|
210
|
+
includeFullReport: input.includeFullReport,
|
|
211
|
+
agentSummary: input.agentSummary,
|
|
212
|
+
});
|
|
206
213
|
return {
|
|
207
214
|
content: [
|
|
208
215
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qulib/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "MCP server for Qulib — AI-callable QA gap analysis",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Tapesh Nagarwal",
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "npm --prefix ../.. run build -w @qulib/core && tsc && chmod +x dist/index.js",
|
|
31
31
|
"dev": "tsx src/index.ts",
|
|
32
|
-
"test": "node --import tsx/esm --test src/__tests__/summarize-analyze-result.test.ts"
|
|
32
|
+
"test": "node --import tsx/esm --test src/__tests__/summarize-analyze-result.test.ts src/__tests__/analyze-app-mcp-payload.test.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
36
|
-
"@qulib/core": "0.
|
|
36
|
+
"@qulib/core": "0.6.0",
|
|
37
37
|
"zod": "^3.23.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import type { AnalyzeResult } from '@qulib/core';
|
|
2
|
-
export declare function buildCompactAnalyzePayload(result: AnalyzeResult, includeFullReport: boolean): AnalyzeResult | {
|
|
3
|
-
includeFullReport: boolean;
|
|
4
|
-
note: string;
|
|
5
|
-
detectedAuth?: {
|
|
6
|
-
type: "unknown" | "form-login" | "oauth" | "magic-link" | "none";
|
|
7
|
-
loginUrl: string | null;
|
|
8
|
-
provider: string | null;
|
|
9
|
-
hasAuth: boolean;
|
|
10
|
-
observedSelectors: {
|
|
11
|
-
usernameSelector: string | null;
|
|
12
|
-
passwordSelector: string | null;
|
|
13
|
-
submitSelector: string | null;
|
|
14
|
-
} | null;
|
|
15
|
-
oauthButtons: {
|
|
16
|
-
text: string;
|
|
17
|
-
provider: string;
|
|
18
|
-
}[];
|
|
19
|
-
recommendation: string;
|
|
20
|
-
authOptions?: {
|
|
21
|
-
type: "unknown" | "form-login" | "oauth" | "oauth-unknown" | "form-multi" | "magic-link";
|
|
22
|
-
label: string;
|
|
23
|
-
id: string;
|
|
24
|
-
provider: string | null;
|
|
25
|
-
source: "built-in" | "user-local" | "heuristic";
|
|
26
|
-
automatable: boolean;
|
|
27
|
-
confidence: "high" | "medium" | "low";
|
|
28
|
-
requirements: {
|
|
29
|
-
method: "storage-state";
|
|
30
|
-
instruction: string;
|
|
31
|
-
} | {
|
|
32
|
-
method: "credentials";
|
|
33
|
-
fields: {
|
|
34
|
-
type: "password" | "text" | "email" | "select" | "checkbox";
|
|
35
|
-
name: string;
|
|
36
|
-
label: string;
|
|
37
|
-
observedOptions: string[];
|
|
38
|
-
}[];
|
|
39
|
-
} | {
|
|
40
|
-
method: "unknown";
|
|
41
|
-
instruction: string;
|
|
42
|
-
};
|
|
43
|
-
}[] | undefined;
|
|
44
|
-
} | undefined;
|
|
45
|
-
repoInventorySummary: {
|
|
46
|
-
framework?: {
|
|
47
|
-
primary: "unknown" | "nextjs-app-router" | "nextjs-pages-router" | "express" | "remix" | "nuxt" | "sveltekit" | "astro" | "vite";
|
|
48
|
-
confidence: "high" | "medium" | "low";
|
|
49
|
-
testFrameworks: ("playwright" | "cypress-e2e" | "cypress-component" | "jest" | "vitest" | "other")[];
|
|
50
|
-
evidenceCount: number;
|
|
51
|
-
} | undefined;
|
|
52
|
-
repoPath: string;
|
|
53
|
-
scannedAt: string;
|
|
54
|
-
routeCount: number;
|
|
55
|
-
testFileCount: number;
|
|
56
|
-
missingTestIdCount: number;
|
|
57
|
-
interactiveTsxFilesScanned: number | null;
|
|
58
|
-
cypressDetected: boolean;
|
|
59
|
-
} | null;
|
|
60
|
-
decisionLogPreview: {
|
|
61
|
-
timestamp: string;
|
|
62
|
-
reason: string;
|
|
63
|
-
phase: "observe" | "think" | "act" | "harness";
|
|
64
|
-
decision: string;
|
|
65
|
-
metadata?: Record<string, unknown> | undefined;
|
|
66
|
-
}[];
|
|
67
|
-
automationMaturitySummary?: {
|
|
68
|
-
overallScore: number;
|
|
69
|
-
level: number;
|
|
70
|
-
label: string;
|
|
71
|
-
topRecommendations: string[];
|
|
72
|
-
} | undefined;
|
|
73
|
-
summary: {
|
|
74
|
-
status: import("@qulib/core").AnalyzeStatus;
|
|
75
|
-
coverageScore: number | null;
|
|
76
|
-
releaseConfidence: number | null;
|
|
77
|
-
mode: "url-only" | "url-repo" | "auth-required";
|
|
78
|
-
coveragePagesScanned: number;
|
|
79
|
-
coverageBudgetExceeded: boolean;
|
|
80
|
-
coverageWarning: "auth-required" | "budget-exceeded" | "low-coverage" | "navigation-failures" | null;
|
|
81
|
-
gapCount: number;
|
|
82
|
-
scenarioCount: number;
|
|
83
|
-
generatedTestCount: number;
|
|
84
|
-
publicSurface: {
|
|
85
|
-
pageCount: number;
|
|
86
|
-
gapCount: number;
|
|
87
|
-
accessibilityViolationCount: number;
|
|
88
|
-
brokenLinkCount: number;
|
|
89
|
-
} | null;
|
|
90
|
-
};
|
|
91
|
-
topGaps: {
|
|
92
|
-
path: string;
|
|
93
|
-
category: "untested-route" | "a11y" | "console-error" | "broken-link" | "auth-surface" | "coverage";
|
|
94
|
-
severity: "critical" | "high" | "medium" | "low";
|
|
95
|
-
reason: string;
|
|
96
|
-
}[];
|
|
97
|
-
costIntelligenceSummary: {
|
|
98
|
-
maxOutputTokensPerLlmCall: number;
|
|
99
|
-
usageDataQuality: "none" | "actual" | "estimated" | "mixed";
|
|
100
|
-
totalInputTokens: number;
|
|
101
|
-
totalOutputTokens: number;
|
|
102
|
-
budgetWarningCount: number;
|
|
103
|
-
maturityLevel: number;
|
|
104
|
-
maturityLabel: string;
|
|
105
|
-
} | null;
|
|
106
|
-
costIntelligence: {
|
|
107
|
-
maxOutputTokensPerLlmCall: number;
|
|
108
|
-
budgetRole: "max-output-tokens-per-llm-call";
|
|
109
|
-
records: {
|
|
110
|
-
provider: string;
|
|
111
|
-
model: string;
|
|
112
|
-
inputTokens: number;
|
|
113
|
-
outputTokens: number;
|
|
114
|
-
operationType: "scenario-generation";
|
|
115
|
-
timestamp: string;
|
|
116
|
-
dataQuality: "none" | "actual" | "estimated" | "mixed";
|
|
117
|
-
estimatedCostUsd?: number | undefined;
|
|
118
|
-
promptHash?: string | undefined;
|
|
119
|
-
resultHash?: string | undefined;
|
|
120
|
-
notes?: string | undefined;
|
|
121
|
-
}[];
|
|
122
|
-
budgetWarnings: string[];
|
|
123
|
-
usageSummary: {
|
|
124
|
-
dataQuality: "none" | "actual" | "estimated" | "mixed";
|
|
125
|
-
totalInputTokens: number;
|
|
126
|
-
totalOutputTokens: number;
|
|
127
|
-
};
|
|
128
|
-
repeatedOperations: {
|
|
129
|
-
recommendation: string;
|
|
130
|
-
promptHash: string;
|
|
131
|
-
count: number;
|
|
132
|
-
}[];
|
|
133
|
-
deterministicMaturity: {
|
|
134
|
-
label: string;
|
|
135
|
-
level: number;
|
|
136
|
-
rationale: string;
|
|
137
|
-
ceilingNote?: string | undefined;
|
|
138
|
-
};
|
|
139
|
-
conversionRecommendations: string[];
|
|
140
|
-
} | null;
|
|
141
|
-
nextDeterministicChecks: string[];
|
|
142
|
-
gapAnalysisPreview: {
|
|
143
|
-
analyzedAt: string;
|
|
144
|
-
gapsSample: {
|
|
145
|
-
path: string;
|
|
146
|
-
id: string;
|
|
147
|
-
severity: "critical" | "high" | "medium" | "low";
|
|
148
|
-
reason: string;
|
|
149
|
-
category: "untested-route" | "a11y" | "console-error" | "broken-link" | "auth-surface" | "coverage";
|
|
150
|
-
recommendation?: string | undefined;
|
|
151
|
-
description?: string | undefined;
|
|
152
|
-
}[];
|
|
153
|
-
scenariosOmitted: number;
|
|
154
|
-
generatedTestsOmitted: number;
|
|
155
|
-
};
|
|
156
|
-
routeInventorySummary: {
|
|
157
|
-
scannedAt: string;
|
|
158
|
-
baseUrl: string;
|
|
159
|
-
routeCount: number;
|
|
160
|
-
pagesSkipped: number;
|
|
161
|
-
budgetExceeded: boolean;
|
|
162
|
-
};
|
|
163
|
-
};
|
|
164
|
-
//# sourceMappingURL=compact-analyze-payload.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"compact-analyze-payload.d.ts","sourceRoot":"","sources":["../src/compact-analyze-payload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAqBjD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,aAAa,EAAE,iBAAiB,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAuG+9d,CAAC;sBAA4C,CAAC;sBAA4C,CAAC;iBAAuC,CAAC;;;;;;;;;;;;;;;;;uBAAghB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;EAD9sf"}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
2
|
-
function topGapsBySeverity(gaps, limit) {
|
|
3
|
-
return [...gaps].sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]).slice(0, limit);
|
|
4
|
-
}
|
|
5
|
-
function nextDeterministicChecks(gaps, conversion) {
|
|
6
|
-
const out = [];
|
|
7
|
-
const byCat = new Map();
|
|
8
|
-
for (const g of gaps) {
|
|
9
|
-
byCat.set(g.category, (byCat.get(g.category) ?? 0) + 1);
|
|
10
|
-
}
|
|
11
|
-
for (const [cat, n] of [...byCat.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3)) {
|
|
12
|
-
out.push(`Add or tighten deterministic coverage for **${cat}** (${n} gap(s) in this scan).`);
|
|
13
|
-
}
|
|
14
|
-
out.push(...conversion.slice(0, 2));
|
|
15
|
-
return out.slice(0, 5);
|
|
16
|
-
}
|
|
17
|
-
export function buildCompactAnalyzePayload(result, includeFullReport) {
|
|
18
|
-
if (includeFullReport) {
|
|
19
|
-
return result;
|
|
20
|
-
}
|
|
21
|
-
const g = result.gapAnalysis;
|
|
22
|
-
const ci = g.costIntelligence;
|
|
23
|
-
const top = topGapsBySeverity(result.gaps, 5);
|
|
24
|
-
const costSummary = ci
|
|
25
|
-
? {
|
|
26
|
-
maxOutputTokensPerLlmCall: ci.maxOutputTokensPerLlmCall,
|
|
27
|
-
usageDataQuality: ci.usageSummary.dataQuality,
|
|
28
|
-
totalInputTokens: ci.usageSummary.totalInputTokens,
|
|
29
|
-
totalOutputTokens: ci.usageSummary.totalOutputTokens,
|
|
30
|
-
budgetWarningCount: ci.budgetWarnings.length,
|
|
31
|
-
maturityLevel: ci.deterministicMaturity.level,
|
|
32
|
-
maturityLabel: ci.deterministicMaturity.label,
|
|
33
|
-
}
|
|
34
|
-
: null;
|
|
35
|
-
const ps = result.publicSurface;
|
|
36
|
-
const repo = result.repoInventory;
|
|
37
|
-
const repoInventorySummary = repo
|
|
38
|
-
? {
|
|
39
|
-
repoPath: repo.repoPath,
|
|
40
|
-
scannedAt: repo.scannedAt,
|
|
41
|
-
routeCount: repo.routes.length,
|
|
42
|
-
testFileCount: repo.testFiles.length,
|
|
43
|
-
missingTestIdCount: repo.missingTestIds.length,
|
|
44
|
-
interactiveTsxFilesScanned: repo.interactiveTsxFilesScanned ?? null,
|
|
45
|
-
cypressDetected: repo.cypressStructure.detected,
|
|
46
|
-
...(repo.framework && {
|
|
47
|
-
framework: {
|
|
48
|
-
primary: repo.framework.primary,
|
|
49
|
-
confidence: repo.framework.confidence,
|
|
50
|
-
testFrameworks: repo.framework.testFrameworks,
|
|
51
|
-
evidenceCount: repo.framework.evidence.length,
|
|
52
|
-
},
|
|
53
|
-
}),
|
|
54
|
-
}
|
|
55
|
-
: null;
|
|
56
|
-
return {
|
|
57
|
-
summary: {
|
|
58
|
-
status: result.status,
|
|
59
|
-
coverageScore: result.coverageScore,
|
|
60
|
-
releaseConfidence: g.releaseConfidence,
|
|
61
|
-
mode: g.mode,
|
|
62
|
-
coveragePagesScanned: g.coveragePagesScanned,
|
|
63
|
-
coverageBudgetExceeded: g.coverageBudgetExceeded,
|
|
64
|
-
coverageWarning: g.coverageWarning ?? null,
|
|
65
|
-
gapCount: g.gaps.length,
|
|
66
|
-
scenarioCount: g.scenarios.length,
|
|
67
|
-
generatedTestCount: g.generatedTests.length,
|
|
68
|
-
publicSurface: ps === null
|
|
69
|
-
? null
|
|
70
|
-
: {
|
|
71
|
-
pageCount: ps.pages.length,
|
|
72
|
-
gapCount: ps.gaps.length,
|
|
73
|
-
accessibilityViolationCount: ps.accessibilityViolations.length,
|
|
74
|
-
brokenLinkCount: ps.brokenLinks.length,
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
topGaps: top.map((x) => ({
|
|
78
|
-
path: x.path,
|
|
79
|
-
category: x.category,
|
|
80
|
-
severity: x.severity,
|
|
81
|
-
reason: x.reason,
|
|
82
|
-
})),
|
|
83
|
-
costIntelligenceSummary: costSummary,
|
|
84
|
-
costIntelligence: ci ?? null,
|
|
85
|
-
nextDeterministicChecks: ci
|
|
86
|
-
? nextDeterministicChecks(result.gaps, ci.conversionRecommendations)
|
|
87
|
-
: nextDeterministicChecks(result.gaps, []),
|
|
88
|
-
gapAnalysisPreview: {
|
|
89
|
-
analyzedAt: g.analyzedAt,
|
|
90
|
-
gapsSample: g.gaps.slice(0, 8),
|
|
91
|
-
scenariosOmitted: g.scenarios.length,
|
|
92
|
-
generatedTestsOmitted: g.generatedTests.length,
|
|
93
|
-
},
|
|
94
|
-
routeInventorySummary: {
|
|
95
|
-
scannedAt: result.routeInventory.scannedAt,
|
|
96
|
-
baseUrl: result.routeInventory.baseUrl,
|
|
97
|
-
routeCount: result.routeInventory.routes.length,
|
|
98
|
-
pagesSkipped: result.routeInventory.pagesSkipped,
|
|
99
|
-
budgetExceeded: result.routeInventory.budgetExceeded,
|
|
100
|
-
},
|
|
101
|
-
...(repo?.automationMaturity && {
|
|
102
|
-
automationMaturitySummary: {
|
|
103
|
-
overallScore: repo.automationMaturity.overallScore,
|
|
104
|
-
level: repo.automationMaturity.level,
|
|
105
|
-
label: repo.automationMaturity.label,
|
|
106
|
-
topRecommendations: repo.automationMaturity.topRecommendations,
|
|
107
|
-
},
|
|
108
|
-
}),
|
|
109
|
-
repoInventorySummary,
|
|
110
|
-
decisionLogPreview: result.decisionLog.slice(-8),
|
|
111
|
-
...(result.detectedAuth !== undefined && { detectedAuth: result.detectedAuth }),
|
|
112
|
-
includeFullReport: false,
|
|
113
|
-
note: 'Summary-first payload. Pass includeFullReport: true for the full gapAnalysis (all scenarios, generated tests) and the full repoInventory (test files, missing test IDs).',
|
|
114
|
-
};
|
|
115
|
-
}
|