neuronlayer 0.1.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/CONTRIBUTING.md +127 -0
- package/LICENSE +21 -0
- package/README.md +305 -0
- package/dist/index.js +38016 -0
- package/esbuild.config.js +26 -0
- package/package.json +63 -0
- package/src/cli/commands.ts +382 -0
- package/src/core/adr-exporter.ts +253 -0
- package/src/core/architecture/architecture-enforcement.ts +228 -0
- package/src/core/architecture/duplicate-detector.ts +288 -0
- package/src/core/architecture/index.ts +6 -0
- package/src/core/architecture/pattern-learner.ts +306 -0
- package/src/core/architecture/pattern-library.ts +403 -0
- package/src/core/architecture/pattern-validator.ts +324 -0
- package/src/core/change-intelligence/bug-correlator.ts +444 -0
- package/src/core/change-intelligence/change-intelligence.ts +221 -0
- package/src/core/change-intelligence/change-tracker.ts +334 -0
- package/src/core/change-intelligence/fix-suggester.ts +340 -0
- package/src/core/change-intelligence/index.ts +5 -0
- package/src/core/code-verifier.ts +843 -0
- package/src/core/confidence/confidence-scorer.ts +251 -0
- package/src/core/confidence/conflict-checker.ts +289 -0
- package/src/core/confidence/index.ts +5 -0
- package/src/core/confidence/source-tracker.ts +263 -0
- package/src/core/confidence/warning-detector.ts +241 -0
- package/src/core/context-rot/compaction.ts +284 -0
- package/src/core/context-rot/context-health.ts +243 -0
- package/src/core/context-rot/context-rot-prevention.ts +213 -0
- package/src/core/context-rot/critical-context.ts +221 -0
- package/src/core/context-rot/drift-detector.ts +255 -0
- package/src/core/context-rot/index.ts +7 -0
- package/src/core/context.ts +263 -0
- package/src/core/decision-extractor.ts +339 -0
- package/src/core/decisions.ts +69 -0
- package/src/core/deja-vu.ts +421 -0
- package/src/core/engine.ts +1455 -0
- package/src/core/feature-context.ts +726 -0
- package/src/core/ghost-mode.ts +412 -0
- package/src/core/learning.ts +485 -0
- package/src/core/living-docs/activity-tracker.ts +296 -0
- package/src/core/living-docs/architecture-generator.ts +428 -0
- package/src/core/living-docs/changelog-generator.ts +348 -0
- package/src/core/living-docs/component-generator.ts +230 -0
- package/src/core/living-docs/doc-engine.ts +110 -0
- package/src/core/living-docs/doc-validator.ts +282 -0
- package/src/core/living-docs/index.ts +8 -0
- package/src/core/project-manager.ts +297 -0
- package/src/core/summarizer.ts +267 -0
- package/src/core/test-awareness/change-validator.ts +499 -0
- package/src/core/test-awareness/index.ts +5 -0
- package/src/index.ts +49 -0
- package/src/indexing/ast.ts +563 -0
- package/src/indexing/embeddings.ts +85 -0
- package/src/indexing/indexer.ts +245 -0
- package/src/indexing/watcher.ts +78 -0
- package/src/server/gateways/aggregator.ts +374 -0
- package/src/server/gateways/index.ts +473 -0
- package/src/server/gateways/memory-ghost.ts +343 -0
- package/src/server/gateways/memory-query.ts +452 -0
- package/src/server/gateways/memory-record.ts +346 -0
- package/src/server/gateways/memory-review.ts +410 -0
- package/src/server/gateways/memory-status.ts +517 -0
- package/src/server/gateways/memory-verify.ts +392 -0
- package/src/server/gateways/router.ts +434 -0
- package/src/server/gateways/types.ts +610 -0
- package/src/server/mcp.ts +154 -0
- package/src/server/resources.ts +85 -0
- package/src/server/tools.ts +2261 -0
- package/src/storage/database.ts +262 -0
- package/src/storage/tier1.ts +135 -0
- package/src/storage/tier2.ts +764 -0
- package/src/storage/tier3.ts +123 -0
- package/src/types/documentation.ts +619 -0
- package/src/types/index.ts +222 -0
- package/src/utils/config.ts +193 -0
- package/src/utils/files.ts +117 -0
- package/src/utils/time.ts +37 -0
- package/src/utils/tokens.ts +52 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Review Gateway
|
|
3
|
+
*
|
|
4
|
+
* Routes to: validate_pattern, check_conflicts, suggest_existing, check_tests,
|
|
5
|
+
* get_confidence, find_similar_bugs, suggest_test_update, get_related_tests,
|
|
6
|
+
* get_test_coverage
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { MemoryLayerEngine } from '../../core/engine.js';
|
|
10
|
+
import type { MemoryReviewInput, MemoryReviewResponse } from './types.js';
|
|
11
|
+
import { detectReviewAction, getReviewChecks, isErrorMessage } from './router.js';
|
|
12
|
+
import {
|
|
13
|
+
aggregateReviewResults,
|
|
14
|
+
calculateRiskScore,
|
|
15
|
+
getVerdict,
|
|
16
|
+
type PatternValidationResult,
|
|
17
|
+
type ConflictCheckResult,
|
|
18
|
+
type ConfidenceCheckResult,
|
|
19
|
+
type TestCheckResult,
|
|
20
|
+
type ExistingFunctionResult,
|
|
21
|
+
} from './aggregator.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Handle a memory_review gateway call
|
|
25
|
+
*/
|
|
26
|
+
export async function handleMemoryReview(
|
|
27
|
+
engine: MemoryLayerEngine,
|
|
28
|
+
input: MemoryReviewInput
|
|
29
|
+
): Promise<MemoryReviewResponse> {
|
|
30
|
+
const action = detectReviewAction(input);
|
|
31
|
+
const sourcesUsed: string[] = [];
|
|
32
|
+
|
|
33
|
+
// Handle specific actions
|
|
34
|
+
if (action !== 'full') {
|
|
35
|
+
switch (action) {
|
|
36
|
+
case 'pattern':
|
|
37
|
+
return handlePatternOnly(engine, input, sourcesUsed);
|
|
38
|
+
|
|
39
|
+
case 'conflicts':
|
|
40
|
+
return handleConflictsOnly(engine, input, sourcesUsed);
|
|
41
|
+
|
|
42
|
+
case 'tests':
|
|
43
|
+
return handleTestsOnly(engine, input, sourcesUsed);
|
|
44
|
+
|
|
45
|
+
case 'confidence':
|
|
46
|
+
return handleConfidenceOnly(engine, input, sourcesUsed);
|
|
47
|
+
|
|
48
|
+
case 'bugs':
|
|
49
|
+
return handleBugsOnly(engine, input, sourcesUsed);
|
|
50
|
+
|
|
51
|
+
case 'coverage':
|
|
52
|
+
return handleCoverageOnly(engine, input, sourcesUsed);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Full review - run applicable checks in parallel
|
|
57
|
+
return handleFullReview(engine, input, sourcesUsed);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Full comprehensive review
|
|
62
|
+
* Enhanced with Ghost Mode conflict detection
|
|
63
|
+
*/
|
|
64
|
+
async function handleFullReview(
|
|
65
|
+
engine: MemoryLayerEngine,
|
|
66
|
+
input: MemoryReviewInput,
|
|
67
|
+
sourcesUsed: string[]
|
|
68
|
+
): Promise<MemoryReviewResponse> {
|
|
69
|
+
const checks = getReviewChecks(input);
|
|
70
|
+
|
|
71
|
+
// Notify ghost mode of file access for silent tracking
|
|
72
|
+
if (input.file) {
|
|
73
|
+
engine.notifyFileAccess(input.file).catch(() => {});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Build parallel operations
|
|
77
|
+
const operations: Promise<unknown>[] = [];
|
|
78
|
+
const operationKeys: string[] = [];
|
|
79
|
+
|
|
80
|
+
if (checks.runPatterns) {
|
|
81
|
+
sourcesUsed.push('validate_pattern');
|
|
82
|
+
operations.push(Promise.resolve(engine.validatePattern(input.code)));
|
|
83
|
+
operationKeys.push('pattern');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (checks.runConflicts) {
|
|
87
|
+
sourcesUsed.push('check_conflicts');
|
|
88
|
+
operations.push(engine.checkCodeConflicts(input.code));
|
|
89
|
+
operationKeys.push('conflicts');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Always run ghost mode conflict check for proactive intelligence
|
|
93
|
+
sourcesUsed.push('ghost_conflicts');
|
|
94
|
+
operations.push(Promise.resolve(engine.checkGhostConflicts(input.code, input.file)));
|
|
95
|
+
operationKeys.push('ghost');
|
|
96
|
+
|
|
97
|
+
if (checks.runConfidence) {
|
|
98
|
+
sourcesUsed.push('get_confidence');
|
|
99
|
+
operations.push(engine.getConfidence(input.code, input.intent));
|
|
100
|
+
operationKeys.push('confidence');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (checks.runExisting && input.intent) {
|
|
104
|
+
sourcesUsed.push('suggest_existing');
|
|
105
|
+
operations.push(Promise.resolve(engine.suggestExisting(input.intent, 5)));
|
|
106
|
+
operationKeys.push('existing');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (checks.runTests && input.file) {
|
|
110
|
+
sourcesUsed.push('check_tests');
|
|
111
|
+
operations.push(Promise.resolve(engine.checkTests(input.code, input.file)));
|
|
112
|
+
operationKeys.push('tests');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (checks.runBugs && input.error) {
|
|
116
|
+
sourcesUsed.push('find_similar_bugs');
|
|
117
|
+
operations.push(Promise.resolve(engine.findSimilarBugs(input.error, 5)));
|
|
118
|
+
operationKeys.push('bugs');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Run all checks in parallel
|
|
122
|
+
const results = await Promise.all(operations);
|
|
123
|
+
|
|
124
|
+
// Map results to their keys
|
|
125
|
+
const resultMap: Record<string, unknown> = {};
|
|
126
|
+
operationKeys.forEach((key, index) => {
|
|
127
|
+
resultMap[key] = results[index];
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Extract typed results
|
|
131
|
+
const patternResult = resultMap.pattern as PatternValidationResult | undefined;
|
|
132
|
+
const conflictsResult = resultMap.conflicts as ConflictCheckResult | undefined;
|
|
133
|
+
const confidenceResult = resultMap.confidence as ConfidenceCheckResult | undefined;
|
|
134
|
+
const existingResult = resultMap.existing as ExistingFunctionResult[] | undefined;
|
|
135
|
+
const testsResult = resultMap.tests as TestCheckResult | undefined;
|
|
136
|
+
const bugsResult = resultMap.bugs as Array<{
|
|
137
|
+
error: string;
|
|
138
|
+
similarity: number;
|
|
139
|
+
fix: string;
|
|
140
|
+
file?: string;
|
|
141
|
+
}> | undefined;
|
|
142
|
+
const ghostResult = resultMap.ghost as Array<{
|
|
143
|
+
decision: { id: string; title: string };
|
|
144
|
+
warning: string;
|
|
145
|
+
severity: 'low' | 'medium' | 'high';
|
|
146
|
+
matchedTerms: string[];
|
|
147
|
+
}> | undefined;
|
|
148
|
+
|
|
149
|
+
// Calculate risk score and aggregate
|
|
150
|
+
const response = aggregateReviewResults(
|
|
151
|
+
patternResult || null,
|
|
152
|
+
conflictsResult ? {
|
|
153
|
+
hasConflicts: conflictsResult.hasConflicts,
|
|
154
|
+
conflicts: conflictsResult.conflicts,
|
|
155
|
+
} : null,
|
|
156
|
+
confidenceResult || null,
|
|
157
|
+
existingResult || null,
|
|
158
|
+
testsResult || null,
|
|
159
|
+
sourcesUsed
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Add ghost mode conflict warnings (proactive intelligence)
|
|
163
|
+
if (ghostResult && ghostResult.length > 0) {
|
|
164
|
+
// Merge ghost conflicts into the conflicts response
|
|
165
|
+
const ghostConflicts = ghostResult.map(g => ({
|
|
166
|
+
decision_id: g.decision.id,
|
|
167
|
+
decision_title: g.decision.title,
|
|
168
|
+
conflict_description: g.warning,
|
|
169
|
+
severity: g.severity,
|
|
170
|
+
}));
|
|
171
|
+
|
|
172
|
+
if (response.conflicts) {
|
|
173
|
+
// Add ghost conflicts that aren't already in the regular conflicts
|
|
174
|
+
const existingIds = new Set(response.conflicts.conflicts.map(c => c.decision_id));
|
|
175
|
+
const newConflicts = ghostConflicts.filter(c => !existingIds.has(c.decision_id));
|
|
176
|
+
response.conflicts.conflicts.push(...newConflicts);
|
|
177
|
+
response.conflicts.has_conflicts = response.conflicts.conflicts.length > 0;
|
|
178
|
+
} else if (ghostConflicts.length > 0) {
|
|
179
|
+
response.conflicts = {
|
|
180
|
+
has_conflicts: true,
|
|
181
|
+
conflicts: ghostConflicts,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Increase risk score for ghost conflicts
|
|
185
|
+
const highSeverity = ghostConflicts.filter(c => c.severity === 'high').length;
|
|
186
|
+
const mediumSeverity = ghostConflicts.filter(c => c.severity === 'medium').length;
|
|
187
|
+
response.risk_score = Math.min(100, response.risk_score + highSeverity * 20 + mediumSeverity * 10);
|
|
188
|
+
|
|
189
|
+
// Update verdict if needed
|
|
190
|
+
if (response.risk_score >= 70) {
|
|
191
|
+
response.verdict = 'reject';
|
|
192
|
+
} else if (response.risk_score >= 30) {
|
|
193
|
+
response.verdict = 'warning';
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Add similar bugs if found
|
|
199
|
+
if (bugsResult && bugsResult.length > 0) {
|
|
200
|
+
response.similar_bugs = bugsResult.map(b => ({
|
|
201
|
+
error: b.error.slice(0, 100),
|
|
202
|
+
similarity: b.similarity,
|
|
203
|
+
fix: b.fix,
|
|
204
|
+
file: b.file,
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return response;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Pattern validation only
|
|
213
|
+
*/
|
|
214
|
+
async function handlePatternOnly(
|
|
215
|
+
engine: MemoryLayerEngine,
|
|
216
|
+
input: MemoryReviewInput,
|
|
217
|
+
sourcesUsed: string[]
|
|
218
|
+
): Promise<MemoryReviewResponse> {
|
|
219
|
+
sourcesUsed.push('validate_pattern');
|
|
220
|
+
|
|
221
|
+
const result = engine.validatePattern(input.code);
|
|
222
|
+
|
|
223
|
+
const riskScore = calculateRiskScore(result, null, null, null);
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
verdict: getVerdict(riskScore),
|
|
227
|
+
risk_score: riskScore,
|
|
228
|
+
sources_used: sourcesUsed,
|
|
229
|
+
patterns: {
|
|
230
|
+
valid: result.valid,
|
|
231
|
+
score: result.score,
|
|
232
|
+
matched_pattern: result.matchedPattern,
|
|
233
|
+
violations: result.violations,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Conflict check only
|
|
240
|
+
*/
|
|
241
|
+
async function handleConflictsOnly(
|
|
242
|
+
engine: MemoryLayerEngine,
|
|
243
|
+
input: MemoryReviewInput,
|
|
244
|
+
sourcesUsed: string[]
|
|
245
|
+
): Promise<MemoryReviewResponse> {
|
|
246
|
+
sourcesUsed.push('check_conflicts');
|
|
247
|
+
|
|
248
|
+
const result = await engine.checkCodeConflicts(input.code);
|
|
249
|
+
|
|
250
|
+
const riskScore = calculateRiskScore(null, {
|
|
251
|
+
hasConflicts: result.hasConflicts,
|
|
252
|
+
conflicts: result.conflicts,
|
|
253
|
+
}, null, null);
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
verdict: getVerdict(riskScore),
|
|
257
|
+
risk_score: riskScore,
|
|
258
|
+
sources_used: sourcesUsed,
|
|
259
|
+
conflicts: {
|
|
260
|
+
has_conflicts: result.hasConflicts,
|
|
261
|
+
conflicts: result.conflicts.map(c => ({
|
|
262
|
+
decision_id: c.decisionId,
|
|
263
|
+
decision_title: c.decisionTitle,
|
|
264
|
+
conflict_description: c.conflictDescription,
|
|
265
|
+
severity: c.severity,
|
|
266
|
+
})),
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Test check only
|
|
273
|
+
*/
|
|
274
|
+
async function handleTestsOnly(
|
|
275
|
+
engine: MemoryLayerEngine,
|
|
276
|
+
input: MemoryReviewInput,
|
|
277
|
+
sourcesUsed: string[]
|
|
278
|
+
): Promise<MemoryReviewResponse> {
|
|
279
|
+
if (!input.file) {
|
|
280
|
+
return {
|
|
281
|
+
verdict: 'warning',
|
|
282
|
+
risk_score: 50,
|
|
283
|
+
sources_used: sourcesUsed,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
sourcesUsed.push('check_tests');
|
|
288
|
+
|
|
289
|
+
const result = engine.checkTests(input.code, input.file);
|
|
290
|
+
|
|
291
|
+
const riskScore = calculateRiskScore(null, null, result, null);
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
verdict: getVerdict(riskScore),
|
|
295
|
+
risk_score: riskScore,
|
|
296
|
+
sources_used: sourcesUsed,
|
|
297
|
+
test_impact: {
|
|
298
|
+
safe: result.safe,
|
|
299
|
+
coverage_percent: result.coveragePercent,
|
|
300
|
+
would_fail: result.wouldFail.map(f => ({
|
|
301
|
+
test_name: f.test.name,
|
|
302
|
+
test_file: f.test.file,
|
|
303
|
+
reason: f.reason,
|
|
304
|
+
suggested_fix: f.suggestedFix,
|
|
305
|
+
})),
|
|
306
|
+
suggested_updates: result.suggestedTestUpdates.map(u => ({
|
|
307
|
+
file: u.file,
|
|
308
|
+
test_name: u.testName,
|
|
309
|
+
before: u.before,
|
|
310
|
+
after: u.after,
|
|
311
|
+
reason: u.reason,
|
|
312
|
+
})),
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Confidence check only
|
|
319
|
+
*/
|
|
320
|
+
async function handleConfidenceOnly(
|
|
321
|
+
engine: MemoryLayerEngine,
|
|
322
|
+
input: MemoryReviewInput,
|
|
323
|
+
sourcesUsed: string[]
|
|
324
|
+
): Promise<MemoryReviewResponse> {
|
|
325
|
+
sourcesUsed.push('get_confidence');
|
|
326
|
+
|
|
327
|
+
const result = await engine.getConfidence(input.code, input.intent);
|
|
328
|
+
|
|
329
|
+
const riskScore = calculateRiskScore(null, null, null, result);
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
verdict: getVerdict(riskScore),
|
|
333
|
+
risk_score: riskScore,
|
|
334
|
+
sources_used: sourcesUsed,
|
|
335
|
+
confidence: {
|
|
336
|
+
level: result.confidence,
|
|
337
|
+
score: result.score,
|
|
338
|
+
reasoning: result.reasoning,
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Bug search only
|
|
345
|
+
*/
|
|
346
|
+
async function handleBugsOnly(
|
|
347
|
+
engine: MemoryLayerEngine,
|
|
348
|
+
input: MemoryReviewInput,
|
|
349
|
+
sourcesUsed: string[]
|
|
350
|
+
): Promise<MemoryReviewResponse> {
|
|
351
|
+
if (!input.error) {
|
|
352
|
+
return {
|
|
353
|
+
verdict: 'approve',
|
|
354
|
+
risk_score: 0,
|
|
355
|
+
sources_used: sourcesUsed,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
sourcesUsed.push('find_similar_bugs');
|
|
360
|
+
|
|
361
|
+
const bugs = engine.findSimilarBugs(input.error, 5);
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
verdict: bugs.length > 0 ? 'warning' : 'approve',
|
|
365
|
+
risk_score: bugs.length > 0 ? 30 : 0,
|
|
366
|
+
sources_used: sourcesUsed,
|
|
367
|
+
similar_bugs: bugs.map(b => ({
|
|
368
|
+
error: b.error.slice(0, 100),
|
|
369
|
+
similarity: b.similarity,
|
|
370
|
+
fix: b.fix,
|
|
371
|
+
file: b.file,
|
|
372
|
+
})),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Coverage check only
|
|
378
|
+
*/
|
|
379
|
+
async function handleCoverageOnly(
|
|
380
|
+
engine: MemoryLayerEngine,
|
|
381
|
+
input: MemoryReviewInput,
|
|
382
|
+
sourcesUsed: string[]
|
|
383
|
+
): Promise<MemoryReviewResponse> {
|
|
384
|
+
if (!input.file) {
|
|
385
|
+
return {
|
|
386
|
+
verdict: 'warning',
|
|
387
|
+
risk_score: 50,
|
|
388
|
+
sources_used: sourcesUsed,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
sourcesUsed.push('get_test_coverage');
|
|
393
|
+
|
|
394
|
+
const coverage = engine.getTestCoverage(input.file);
|
|
395
|
+
|
|
396
|
+
// Risk based on coverage percentage
|
|
397
|
+
const riskScore = Math.max(0, 100 - coverage.coveragePercent);
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
verdict: getVerdict(riskScore),
|
|
401
|
+
risk_score: riskScore,
|
|
402
|
+
sources_used: sourcesUsed,
|
|
403
|
+
coverage: {
|
|
404
|
+
file: coverage.file,
|
|
405
|
+
total_tests: coverage.totalTests,
|
|
406
|
+
coverage_percent: coverage.coveragePercent,
|
|
407
|
+
uncovered_functions: coverage.uncoveredFunctions,
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
}
|