sentinelayer-cli 0.1.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +998 -996
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +63 -54
- package/src/agents/jules/config/definition.js +209 -209
- package/src/agents/jules/config/system-prompt.js +175 -175
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +377 -377
- package/src/agents/jules/loop.js +367 -367
- package/src/agents/jules/pulse.js +327 -319
- package/src/agents/jules/stream.js +186 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +308 -308
- package/src/agents/jules/tools/auth-audit.js +557 -222
- package/src/agents/jules/tools/dispatch.js +327 -327
- package/src/agents/jules/tools/file-edit.js +180 -180
- package/src/agents/jules/tools/file-read.js +100 -100
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +168 -168
- package/src/agents/jules/tools/grep.js +228 -228
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +161 -161
- package/src/agents/jules/tools/runtime-audit.js +503 -493
- package/src/agents/jules/tools/shell.js +383 -383
- package/src/agents/jules/tools/url-policy.js +100 -0
- package/src/ai/aidenid.js +972 -945
- package/src/ai/client.js +508 -508
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/gate.js +45 -11
- package/src/auth/http.js +270 -113
- package/src/auth/service.js +891 -848
- package/src/auth/session-store.js +359 -345
- package/src/cli.js +252 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1337
- package/src/commands/ai/provision-governance.js +1272 -1246
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1166 -1166
- package/src/commands/auth.js +375 -366
- package/src/commands/chat.js +191 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +10 -10
- package/src/commands/mcp.js +461 -404
- package/src/commands/omargate.js +15 -15
- package/src/commands/persona.js +20 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +866 -788
- package/src/commands/spec.js +716 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +510 -510
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tracker.js +171 -171
- package/src/daemon/artifact-lineage.js +534 -534
- package/src/daemon/assignment-ledger.js +770 -770
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +626 -626
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/jira-lifecycle.js +632 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/watchdog.js +971 -971
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +918 -918
- package/src/legacy-cli.js +2592 -2435
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +118 -106
- package/src/review/ai-review.js +669 -669
- package/src/review/local-review.js +1295 -1284
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -0
- package/src/scaffold/templates.js +150 -0
- package/src/scan/generator.js +418 -351
- package/src/scan/gh-secrets.js +107 -0
- package/src/spec/generator.js +519 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +576 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/sync.js +107 -61
- package/src/ui/markdown.js +220 -220
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
function normalizeString(value) {
|
|
2
|
-
return String(value || "").trim();
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function toPosixPath(value) {
|
|
6
|
-
return String(value || "").replace(/\\/g, "/");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function summarizeSeverity(findings = []) {
|
|
10
|
-
const summary = { P0: 0, P1: 0, P2: 0, P3: 0 };
|
|
11
|
-
for (const finding of findings) {
|
|
12
|
-
const severity = normalizeString(finding.severity).toUpperCase();
|
|
13
|
-
if (Object.prototype.hasOwnProperty.call(summary, severity)) {
|
|
14
|
-
summary[severity] += 1;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
summary.blocking = summary.P0 > 0 || summary.P1 > 0;
|
|
18
|
-
return summary;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function isTestPath(filePath) {
|
|
22
|
-
return /(^|\/)(__tests__|tests?|spec)(\/|$)|\.(test|spec)\.[^.]+$/i.test(filePath);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function buildCoverageInventory(ingest = {}) {
|
|
26
|
-
const indexedFiles = Array.isArray(ingest.indexedFiles?.files) ? ingest.indexedFiles.files : [];
|
|
27
|
-
const normalized = indexedFiles.map((file) => ({
|
|
28
|
-
...file,
|
|
29
|
-
path: toPosixPath(file.path),
|
|
30
|
-
loc: Number(file.loc || 0),
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
const testFiles = normalized.filter((file) => isTestPath(file.path));
|
|
34
|
-
const nonTestCodeFiles = normalized.filter((file) => !isTestPath(file.path) && file.loc > 0);
|
|
35
|
-
const ratio = nonTestCodeFiles.length > 0 ? testFiles.length / nonTestCodeFiles.length : 0;
|
|
36
|
-
const likelyGaps = nonTestCodeFiles
|
|
37
|
-
.filter((file) => file.loc >= 260)
|
|
38
|
-
.filter((file) => {
|
|
39
|
-
const basename = file.path.slice(file.path.lastIndexOf("/") + 1).replace(/\.[^.]+$/, "");
|
|
40
|
-
return !testFiles.some((candidate) => candidate.path.includes(basename));
|
|
41
|
-
})
|
|
42
|
-
.sort((left, right) => right.loc - left.loc)
|
|
43
|
-
.slice(0, 20)
|
|
44
|
-
.map((file) => ({
|
|
45
|
-
path: file.path,
|
|
46
|
-
loc: file.loc,
|
|
47
|
-
language: file.language,
|
|
48
|
-
severity: file.loc >= 550 ? "P1" : "P2",
|
|
49
|
-
}));
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
testFileCount: testFiles.length,
|
|
53
|
-
codeFileCount: nonTestCodeFiles.length,
|
|
54
|
-
ratio,
|
|
55
|
-
testFiles: testFiles.slice(0, 30).map((file) => file.path),
|
|
56
|
-
likelyGaps,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function deriveTestingFindings({ findings = [], coverageInventory } = {}) {
|
|
61
|
-
const derived = [];
|
|
62
|
-
const scopedFindings = Array.isArray(findings) ? findings : [];
|
|
63
|
-
for (const finding of scopedFindings) {
|
|
64
|
-
const haystack = `${normalizeString(finding.message)} ${normalizeString(finding.ruleId)} ${normalizeString(
|
|
65
|
-
finding.file
|
|
66
|
-
)}`.toLowerCase();
|
|
67
|
-
if (/test|coverage|assert|typecheck|lint|static analysis|flaky|fixture/.test(haystack)) {
|
|
68
|
-
derived.push(finding);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
for (const gap of coverageInventory.likelyGaps || []) {
|
|
73
|
-
const exists = derived.some((finding) => toPosixPath(finding.file) === gap.path);
|
|
74
|
-
if (exists) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
derived.push({
|
|
78
|
-
severity: gap.severity,
|
|
79
|
-
file: gap.path,
|
|
80
|
-
line: 1,
|
|
81
|
-
message: `High-risk module has no obvious colocated test coverage (${gap.loc} LOC).`,
|
|
82
|
-
excerpt: `${gap.language || "code"} module ${gap.path}`,
|
|
83
|
-
ruleId: "SL-TST-001",
|
|
84
|
-
suggestedFix: "Add deterministic unit/integration coverage with failure-path assertions.",
|
|
85
|
-
layer: "testing",
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return derived.slice(0, 120);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function estimateTestingScore(coverageInventory = {}, summary = {}) {
|
|
93
|
-
const ratio = Number(coverageInventory.ratio || 0);
|
|
94
|
-
const ratioScore = Math.min(60, Math.round(ratio * 100));
|
|
95
|
-
const penalty = Number(summary.P1 || 0) * 10 + Number(summary.P2 || 0) * 5 + Number(summary.P3 || 0) * 1;
|
|
96
|
-
return Math.max(0, Math.min(100, ratioScore + 40 - penalty));
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function buildRecommendations({ coverageInventory = {}, summary = {} } = {}) {
|
|
100
|
-
const recommendations = [];
|
|
101
|
-
if (Number(summary.P1 || 0) > 0) {
|
|
102
|
-
recommendations.push("Stabilize high-risk modules with P1 test gaps before feature expansion.");
|
|
103
|
-
}
|
|
104
|
-
if (coverageInventory.likelyGaps.length > 0) {
|
|
105
|
-
recommendations.push("Create targeted tests for top LOC modules lacking colocated test companions.");
|
|
106
|
-
}
|
|
107
|
-
if (coverageInventory.ratio < 0.2) {
|
|
108
|
-
recommendations.push("Increase test-to-code ratio with deterministic smoke and integration suites.");
|
|
109
|
-
}
|
|
110
|
-
if (recommendations.length === 0) {
|
|
111
|
-
recommendations.push("Testing posture is stable; continue enforcing deterministic regression checks.");
|
|
112
|
-
}
|
|
113
|
-
return recommendations;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function runTestingSpecialist({ findings = [], ingest = {} } = {}) {
|
|
117
|
-
const coverageInventory = buildCoverageInventory(ingest);
|
|
118
|
-
const testingFindings = deriveTestingFindings({
|
|
119
|
-
findings,
|
|
120
|
-
coverageInventory,
|
|
121
|
-
});
|
|
122
|
-
const summary = summarizeSeverity(testingFindings);
|
|
123
|
-
const testingScore = estimateTestingScore(coverageInventory, summary);
|
|
124
|
-
const confidence = testingFindings.length > 0 ? Math.max(0.74, 1 - testingFindings.length * 0.002) : 0.9;
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
schemaVersion: "1.0.0",
|
|
128
|
-
generatedAt: new Date().toISOString(),
|
|
129
|
-
summary: {
|
|
130
|
-
...summary,
|
|
131
|
-
findingCount: testingFindings.length,
|
|
132
|
-
testingScore,
|
|
133
|
-
},
|
|
134
|
-
confidence,
|
|
135
|
-
coverageInventory,
|
|
136
|
-
recommendations: buildRecommendations({
|
|
137
|
-
coverageInventory,
|
|
138
|
-
summary,
|
|
139
|
-
}),
|
|
140
|
-
findings: testingFindings,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function renderTestingSpecialistMarkdown(report = {}) {
|
|
145
|
-
const likelyGaps = (report.coverageInventory?.likelyGaps || [])
|
|
146
|
-
.map((item) => `- ${item.path} (${item.loc} LOC, severity=${item.severity})`)
|
|
147
|
-
.join("\n");
|
|
148
|
-
const recommendations = (report.recommendations || []).map((item) => `- ${item}`).join("\n");
|
|
149
|
-
|
|
150
|
-
return `# TESTING_AGENT_REPORT
|
|
151
|
-
|
|
152
|
-
Generated: ${report.generatedAt}
|
|
153
|
-
Testing score: ${report.summary?.testingScore ?? 0}/100
|
|
154
|
-
Confidence: ${((report.confidence || 0) * 100).toFixed(0)}%
|
|
155
|
-
|
|
156
|
-
Summary:
|
|
157
|
-
- Findings: P0=${report.summary?.P0 ?? 0} P1=${report.summary?.P1 ?? 0} P2=${report.summary?.P2 ?? 0} P3=${report.summary?.P3 ?? 0}
|
|
158
|
-
- Blocking: ${report.summary?.blocking ? "yes" : "no"}
|
|
159
|
-
- Total findings: ${report.summary?.findingCount ?? 0}
|
|
160
|
-
|
|
161
|
-
Coverage inventory:
|
|
162
|
-
- test files: ${report.coverageInventory?.testFileCount ?? 0}
|
|
163
|
-
- code files: ${report.coverageInventory?.codeFileCount ?? 0}
|
|
164
|
-
- ratio: ${((report.coverageInventory?.ratio || 0) * 100).toFixed(1)}%
|
|
165
|
-
|
|
166
|
-
Likely coverage gaps:
|
|
167
|
-
${likelyGaps || "- none"}
|
|
168
|
-
|
|
169
|
-
Recommendations:
|
|
170
|
-
${recommendations || "- none"}
|
|
171
|
-
`;
|
|
172
|
-
}
|
|
1
|
+
function normalizeString(value) {
|
|
2
|
+
return String(value || "").trim();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function toPosixPath(value) {
|
|
6
|
+
return String(value || "").replace(/\\/g, "/");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function summarizeSeverity(findings = []) {
|
|
10
|
+
const summary = { P0: 0, P1: 0, P2: 0, P3: 0 };
|
|
11
|
+
for (const finding of findings) {
|
|
12
|
+
const severity = normalizeString(finding.severity).toUpperCase();
|
|
13
|
+
if (Object.prototype.hasOwnProperty.call(summary, severity)) {
|
|
14
|
+
summary[severity] += 1;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
summary.blocking = summary.P0 > 0 || summary.P1 > 0;
|
|
18
|
+
return summary;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isTestPath(filePath) {
|
|
22
|
+
return /(^|\/)(__tests__|tests?|spec)(\/|$)|\.(test|spec)\.[^.]+$/i.test(filePath);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function buildCoverageInventory(ingest = {}) {
|
|
26
|
+
const indexedFiles = Array.isArray(ingest.indexedFiles?.files) ? ingest.indexedFiles.files : [];
|
|
27
|
+
const normalized = indexedFiles.map((file) => ({
|
|
28
|
+
...file,
|
|
29
|
+
path: toPosixPath(file.path),
|
|
30
|
+
loc: Number(file.loc || 0),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const testFiles = normalized.filter((file) => isTestPath(file.path));
|
|
34
|
+
const nonTestCodeFiles = normalized.filter((file) => !isTestPath(file.path) && file.loc > 0);
|
|
35
|
+
const ratio = nonTestCodeFiles.length > 0 ? testFiles.length / nonTestCodeFiles.length : 0;
|
|
36
|
+
const likelyGaps = nonTestCodeFiles
|
|
37
|
+
.filter((file) => file.loc >= 260)
|
|
38
|
+
.filter((file) => {
|
|
39
|
+
const basename = file.path.slice(file.path.lastIndexOf("/") + 1).replace(/\.[^.]+$/, "");
|
|
40
|
+
return !testFiles.some((candidate) => candidate.path.includes(basename));
|
|
41
|
+
})
|
|
42
|
+
.sort((left, right) => right.loc - left.loc)
|
|
43
|
+
.slice(0, 20)
|
|
44
|
+
.map((file) => ({
|
|
45
|
+
path: file.path,
|
|
46
|
+
loc: file.loc,
|
|
47
|
+
language: file.language,
|
|
48
|
+
severity: file.loc >= 550 ? "P1" : "P2",
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
testFileCount: testFiles.length,
|
|
53
|
+
codeFileCount: nonTestCodeFiles.length,
|
|
54
|
+
ratio,
|
|
55
|
+
testFiles: testFiles.slice(0, 30).map((file) => file.path),
|
|
56
|
+
likelyGaps,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function deriveTestingFindings({ findings = [], coverageInventory } = {}) {
|
|
61
|
+
const derived = [];
|
|
62
|
+
const scopedFindings = Array.isArray(findings) ? findings : [];
|
|
63
|
+
for (const finding of scopedFindings) {
|
|
64
|
+
const haystack = `${normalizeString(finding.message)} ${normalizeString(finding.ruleId)} ${normalizeString(
|
|
65
|
+
finding.file
|
|
66
|
+
)}`.toLowerCase();
|
|
67
|
+
if (/test|coverage|assert|typecheck|lint|static analysis|flaky|fixture/.test(haystack)) {
|
|
68
|
+
derived.push(finding);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const gap of coverageInventory.likelyGaps || []) {
|
|
73
|
+
const exists = derived.some((finding) => toPosixPath(finding.file) === gap.path);
|
|
74
|
+
if (exists) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
derived.push({
|
|
78
|
+
severity: gap.severity,
|
|
79
|
+
file: gap.path,
|
|
80
|
+
line: 1,
|
|
81
|
+
message: `High-risk module has no obvious colocated test coverage (${gap.loc} LOC).`,
|
|
82
|
+
excerpt: `${gap.language || "code"} module ${gap.path}`,
|
|
83
|
+
ruleId: "SL-TST-001",
|
|
84
|
+
suggestedFix: "Add deterministic unit/integration coverage with failure-path assertions.",
|
|
85
|
+
layer: "testing",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return derived.slice(0, 120);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function estimateTestingScore(coverageInventory = {}, summary = {}) {
|
|
93
|
+
const ratio = Number(coverageInventory.ratio || 0);
|
|
94
|
+
const ratioScore = Math.min(60, Math.round(ratio * 100));
|
|
95
|
+
const penalty = Number(summary.P1 || 0) * 10 + Number(summary.P2 || 0) * 5 + Number(summary.P3 || 0) * 1;
|
|
96
|
+
return Math.max(0, Math.min(100, ratioScore + 40 - penalty));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function buildRecommendations({ coverageInventory = {}, summary = {} } = {}) {
|
|
100
|
+
const recommendations = [];
|
|
101
|
+
if (Number(summary.P1 || 0) > 0) {
|
|
102
|
+
recommendations.push("Stabilize high-risk modules with P1 test gaps before feature expansion.");
|
|
103
|
+
}
|
|
104
|
+
if (coverageInventory.likelyGaps.length > 0) {
|
|
105
|
+
recommendations.push("Create targeted tests for top LOC modules lacking colocated test companions.");
|
|
106
|
+
}
|
|
107
|
+
if (coverageInventory.ratio < 0.2) {
|
|
108
|
+
recommendations.push("Increase test-to-code ratio with deterministic smoke and integration suites.");
|
|
109
|
+
}
|
|
110
|
+
if (recommendations.length === 0) {
|
|
111
|
+
recommendations.push("Testing posture is stable; continue enforcing deterministic regression checks.");
|
|
112
|
+
}
|
|
113
|
+
return recommendations;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function runTestingSpecialist({ findings = [], ingest = {} } = {}) {
|
|
117
|
+
const coverageInventory = buildCoverageInventory(ingest);
|
|
118
|
+
const testingFindings = deriveTestingFindings({
|
|
119
|
+
findings,
|
|
120
|
+
coverageInventory,
|
|
121
|
+
});
|
|
122
|
+
const summary = summarizeSeverity(testingFindings);
|
|
123
|
+
const testingScore = estimateTestingScore(coverageInventory, summary);
|
|
124
|
+
const confidence = testingFindings.length > 0 ? Math.max(0.74, 1 - testingFindings.length * 0.002) : 0.9;
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
schemaVersion: "1.0.0",
|
|
128
|
+
generatedAt: new Date().toISOString(),
|
|
129
|
+
summary: {
|
|
130
|
+
...summary,
|
|
131
|
+
findingCount: testingFindings.length,
|
|
132
|
+
testingScore,
|
|
133
|
+
},
|
|
134
|
+
confidence,
|
|
135
|
+
coverageInventory,
|
|
136
|
+
recommendations: buildRecommendations({
|
|
137
|
+
coverageInventory,
|
|
138
|
+
summary,
|
|
139
|
+
}),
|
|
140
|
+
findings: testingFindings,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function renderTestingSpecialistMarkdown(report = {}) {
|
|
145
|
+
const likelyGaps = (report.coverageInventory?.likelyGaps || [])
|
|
146
|
+
.map((item) => `- ${item.path} (${item.loc} LOC, severity=${item.severity})`)
|
|
147
|
+
.join("\n");
|
|
148
|
+
const recommendations = (report.recommendations || []).map((item) => `- ${item}`).join("\n");
|
|
149
|
+
|
|
150
|
+
return `# TESTING_AGENT_REPORT
|
|
151
|
+
|
|
152
|
+
Generated: ${report.generatedAt}
|
|
153
|
+
Testing score: ${report.summary?.testingScore ?? 0}/100
|
|
154
|
+
Confidence: ${((report.confidence || 0) * 100).toFixed(0)}%
|
|
155
|
+
|
|
156
|
+
Summary:
|
|
157
|
+
- Findings: P0=${report.summary?.P0 ?? 0} P1=${report.summary?.P1 ?? 0} P2=${report.summary?.P2 ?? 0} P3=${report.summary?.P3 ?? 0}
|
|
158
|
+
- Blocking: ${report.summary?.blocking ? "yes" : "no"}
|
|
159
|
+
- Total findings: ${report.summary?.findingCount ?? 0}
|
|
160
|
+
|
|
161
|
+
Coverage inventory:
|
|
162
|
+
- test files: ${report.coverageInventory?.testFileCount ?? 0}
|
|
163
|
+
- code files: ${report.coverageInventory?.codeFileCount ?? 0}
|
|
164
|
+
- ratio: ${((report.coverageInventory?.ratio || 0) * 100).toFixed(1)}%
|
|
165
|
+
|
|
166
|
+
Likely coverage gaps:
|
|
167
|
+
${likelyGaps || "- none"}
|
|
168
|
+
|
|
169
|
+
Recommendations:
|
|
170
|
+
${recommendations || "- none"}
|
|
171
|
+
`;
|
|
172
|
+
}
|