monomind 1.10.56 → 1.11.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/.claude/agents/core/coordinator.md +71 -0
- package/.claude/agents/generated/case-analyst.md +50 -0
- package/.claude/agents/generated/copy-editor.md +45 -0
- package/.claude/agents/generated/court-reporter.md +50 -0
- package/.claude/agents/generated/defender.md +51 -0
- package/.claude/agents/generated/editor-in-chief.md +45 -0
- package/.claude/agents/generated/fact-checker.md +45 -0
- package/.claude/agents/generated/judge.md +51 -0
- package/.claude/agents/generated/prosecutor.md +51 -0
- package/.claude/agents/generated/reporter.md +45 -0
- package/.claude/commands/hooks/README.md +1 -1
- package/.claude/commands/hooks/overview.md +1 -1
- package/.claude/commands/mastermind/_repeat.md +1 -1
- package/.claude/commands/mastermind/do.md +3 -1
- package/.claude/commands/mastermind/help.md +2 -2
- package/.claude/commands/mastermind/master.md +39 -6
- package/.claude/commands/mastermind/memory.md +1 -1
- package/.claude/commands/memory/memory-search.md +2 -2
- package/.claude/commands/monitoring/status.md +1 -1
- package/.claude/commands/{browse.md → monobrowse.md} +2 -2
- package/.claude/commands/sparc.md +1 -1
- package/.claude/helpers/handlers/graph-status-handler.cjs +1 -1
- package/.claude/helpers/loop-tracker.cjs +1 -1
- package/.claude/scheduled_tasks.lock +1 -1
- package/.claude/skills/agent-browser-testing/SKILL.md +1 -1
- package/.claude/skills/hooks-automation/SKILL.md +0 -3
- package/.claude/skills/mastermind/build.md +1 -1
- package/.claude/skills/mastermind/code-quality-reviewer-prompt.md +60 -0
- package/.claude/skills/mastermind/content.md +1 -1
- package/.claude/skills/mastermind/createorg.md +79 -2
- package/.claude/skills/mastermind/design.md +3 -1
- package/.claude/skills/mastermind/finance.md +1 -1
- package/.claude/skills/mastermind/implementer-prompt.md +109 -0
- package/.claude/skills/mastermind/marketing.md +1 -1
- package/.claude/skills/mastermind/ops.md +1 -1
- package/.claude/skills/mastermind/plan.md +20 -2
- package/.claude/skills/mastermind/release.md +1 -1
- package/.claude/skills/mastermind/research.md +1 -1
- package/.claude/skills/mastermind/review.md +1 -1
- package/.claude/skills/mastermind/sales.md +1 -1
- package/.claude/skills/mastermind/spec-reviewer-prompt.md +63 -0
- package/.claude/skills/sparc-methodology/SKILL.md +3 -3
- package/.claude/skills/swarm-advanced/SKILL.md +1 -4
- package/.claude-plugin/README.md +1 -2
- package/.claude-plugin/docs/PLUGIN_SUMMARY.md +0 -1
- package/README.md +18 -32
- package/package.json +1 -1
- package/packages/@monomind/cli/README.md +18 -32
- package/packages/@monomind/cli/dist/src/agents/registry-builder.d.ts +1 -7
- package/packages/@monomind/cli/dist/src/agents/registry-builder.js +10 -6
- package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.d.ts +59 -12
- package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.js +67 -13
- package/packages/@monomind/cli/dist/src/benchmarks/pretrain/index.d.ts +0 -13
- package/packages/@monomind/cli/dist/src/commands/agent-wasm.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/commands/agent-wasm.js +5 -5
- package/packages/@monomind/cli/dist/src/commands/agent.js +1 -6
- package/packages/@monomind/cli/dist/src/commands/analyze.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/commands/analyze.js +8 -18
- package/packages/@monomind/cli/dist/src/commands/doctor.js +53 -3
- package/packages/@monomind/cli/dist/src/commands/embeddings.js +9 -33
- package/packages/@monomind/cli/dist/src/commands/hooks.js +24 -76
- package/packages/@monomind/cli/dist/src/commands/index.d.ts +4 -6
- package/packages/@monomind/cli/dist/src/commands/index.js +8 -15
- package/packages/@monomind/cli/dist/src/commands/init.js +2 -2
- package/packages/@monomind/cli/dist/src/commands/issues.js +16 -11
- package/packages/@monomind/cli/dist/src/commands/mcp.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/commands/memory.js +6 -6
- package/packages/@monomind/cli/dist/src/commands/migrate.js +1 -2
- package/packages/@monomind/cli/dist/src/commands/monograph.js +18 -11
- package/packages/@monomind/cli/dist/src/commands/monovector/backup.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/backup.js +25 -25
- package/packages/@monomind/cli/dist/src/commands/monovector/benchmark.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/benchmark.js +14 -14
- package/packages/@monomind/cli/dist/src/commands/monovector/import.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/import.js +21 -21
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/index.d.ts +6 -6
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/index.js +22 -22
- package/packages/@monomind/cli/dist/src/commands/monovector/init.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/init.js +36 -36
- package/packages/@monomind/cli/dist/src/commands/monovector/migrate.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/migrate.js +16 -16
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/optimize.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/optimize.js +10 -10
- package/packages/@monomind/cli/dist/src/commands/monovector/setup.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/setup.js +77 -77
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/status.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/commands/{ruvector → monovector}/status.js +34 -34
- package/packages/@monomind/cli/dist/src/commands/neural.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/commands/neural.js +31 -608
- package/packages/@monomind/cli/dist/src/commands/performance.js +7 -10
- package/packages/@monomind/cli/dist/src/commands/plugins.js +3 -4
- package/packages/@monomind/cli/dist/src/commands/process.js +5 -12
- package/packages/@monomind/cli/dist/src/commands/progress.js +16 -16
- package/packages/@monomind/cli/dist/src/commands/route.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/commands/route.js +23 -23
- package/packages/@monomind/cli/dist/src/commands/status.js +0 -3
- package/packages/@monomind/cli/dist/src/commands/swarm.js +2 -3
- package/packages/@monomind/cli/dist/src/commands/update.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/config-adapter.js +27 -0
- package/packages/@monomind/cli/dist/src/consensus/audit-writer.d.ts +44 -17
- package/packages/@monomind/cli/dist/src/dlq/dlq-replayer.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/index.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/index.js +5 -5
- package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +10 -14
- package/packages/@monomind/cli/dist/src/init/executor.js +17 -27
- package/packages/@monomind/cli/dist/src/init/helpers-generator.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/init/helpers-generator.js +2 -2
- package/packages/@monomind/cli/dist/src/init/types.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-client.js +1 -7
- package/packages/@monomind/cli/dist/src/mcp-server.js +9 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +1 -52
- package/packages/@monomind/cli/dist/src/mcp-tools/analyze-tools.js +5 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/auto-install.d.ts +4 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/autopilot-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.js +6 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/claims-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/coordination-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/coordination-tools.js +51 -54
- package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/embeddings-tools.js +10 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +0 -24
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-tools.js +189 -446
- package/packages/@monomind/cli/dist/src/mcp-tools/index.d.ts +0 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/index.js +0 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.d.ts +334 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +1108 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +76 -34
- package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.js +2 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/ruvllm-tools.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/ruvllm-tools.js +3 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.js +29 -19
- package/packages/@monomind/cli/dist/src/mcp-tools/transfer-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/wasm-agent-tools.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/wasm-agent-tools.js +2 -2
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +14 -8
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +17 -2
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +76 -23
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +37 -39
- package/packages/@monomind/cli/dist/src/monovector/capabilities.d.ts +34 -0
- package/packages/@monomind/cli/dist/src/monovector/capabilities.js +37 -0
- package/packages/@monomind/cli/dist/src/monovector/command-outcomes.d.ts +37 -0
- package/packages/@monomind/cli/dist/src/monovector/command-outcomes.js +87 -0
- package/packages/@monomind/cli/dist/src/monovector/coverage-router.d.ts +103 -0
- package/packages/@monomind/cli/dist/src/monovector/coverage-router.js +337 -0
- package/packages/@monomind/cli/dist/src/monovector/coverage-tools.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/monovector/coverage-tools.js +112 -0
- package/packages/@monomind/cli/dist/src/{ruvector → monovector}/diff-classifier.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/monovector/index.d.ts +61 -0
- package/packages/@monomind/cli/dist/src/monovector/index.js +67 -0
- package/packages/@monomind/cli/dist/src/monovector/init-state.d.ts +35 -0
- package/packages/@monomind/cli/dist/src/monovector/init-state.js +36 -0
- package/packages/@monomind/cli/dist/src/monovector/route-outcomes.d.ts +55 -0
- package/packages/@monomind/cli/dist/src/monovector/route-outcomes.js +137 -0
- package/packages/@monomind/cli/dist/src/parser.js +3 -3
- package/packages/@monomind/cli/dist/src/plugins/store/discovery.js +0 -31
- package/packages/@monomind/cli/dist/src/production/circuit-breaker.d.ts +26 -6
- package/packages/@monomind/cli/dist/src/production/error-handler.d.ts +55 -30
- package/packages/@monomind/cli/dist/src/production/index.d.ts +3 -20
- package/packages/@monomind/cli/dist/src/production/index.js +3 -15
- package/packages/@monomind/cli/dist/src/production/monitoring.d.ts +54 -34
- package/packages/@monomind/cli/dist/src/production/monitoring.js +12 -14
- package/packages/@monomind/cli/dist/src/production/rate-limiter.d.ts +64 -19
- package/packages/@monomind/cli/dist/src/production/rate-limiter.js +5 -5
- package/packages/@monomind/cli/dist/src/production/retry.js +4 -2
- package/packages/@monomind/cli/dist/src/runtime/headless.d.ts +3 -3
- package/packages/@monomind/cli/dist/src/services/claim-service.d.ts +50 -59
- package/packages/@monomind/cli/dist/src/services/claim-service.js +83 -50
- package/packages/@monomind/cli/dist/src/services/config-file-manager.js +8 -1
- package/packages/@monomind/cli/dist/src/services/worker-daemon.js +4 -0
- package/packages/@monomind/cli/dist/src/transfer/ipfs/upload.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/transfer/storage/gcs.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/types.d.ts +13 -0
- package/packages/@monomind/cli/dist/src/update/checker.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/update/index.d.ts +1 -1
- package/packages/@monomind/cli/package.json +13 -13
- package/packages/@monomind/guidance/dist/index.d.ts +1 -1
- package/packages/@monomind/guidance/dist/index.js +1 -1
- package/packages/@monomind/guidance/package.json +1 -2
- package/scripts/build-monovector.sh +10 -0
- package/scripts/publish-monovector.sh +20 -0
- package/.claude/commands/monomind/adr.md +0 -11
- package/.claude/commands/monomind/budget.md +0 -7
- package/.claude/commands/monomind/createtask.md +0 -277
- package/.claude/commands/monomind/do.md +0 -498
- package/.claude/commands/monomind/graph-status.md +0 -7
- package/.claude/commands/monomind/help.md +0 -118
- package/.claude/commands/monomind/idea.md +0 -273
- package/.claude/commands/monomind/improve.md +0 -352
- package/.claude/commands/monomind/loops.md +0 -7
- package/.claude/commands/monomind/memory.md +0 -230
- package/.claude/commands/monomind/repeat.md +0 -257
- package/.claude/commands/monomind/review.md +0 -317
- package/.claude/commands/monomind/specialagents.md +0 -125
- package/.claude/commands/monomind/swarm.md +0 -161
- package/.claude/commands/monomind/understand.md +0 -139
- package/.claude/commands/training/README.md +0 -39
- package/.claude/commands/training/neural-patterns.md +0 -73
- package/.claude/commands/training/neural-train.md +0 -79
- package/packages/@monomind/cli/dist/src/commands/appliance-advanced.d.ts +0 -9
- package/packages/@monomind/cli/dist/src/commands/appliance-advanced.js +0 -238
- package/packages/@monomind/cli/dist/src/commands/appliance.d.ts +0 -8
- package/packages/@monomind/cli/dist/src/commands/appliance.js +0 -406
- package/packages/@monomind/cli/dist/src/commands/ruvector/backup.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/ruvector/benchmark.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/ruvector/import.d.ts +0 -18
- package/packages/@monomind/cli/dist/src/commands/ruvector/init.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/ruvector/migrate.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/ruvector/setup.d.ts +0 -18
- package/packages/@monomind/cli/dist/src/ruvector/agent-wasm.d.ts +0 -182
- package/packages/@monomind/cli/dist/src/ruvector/agent-wasm.js +0 -316
- package/packages/@monomind/cli/dist/src/ruvector/ast-analyzer.d.ts +0 -67
- package/packages/@monomind/cli/dist/src/ruvector/ast-analyzer.js +0 -277
- package/packages/@monomind/cli/dist/src/ruvector/coverage-router.d.ts +0 -160
- package/packages/@monomind/cli/dist/src/ruvector/coverage-router.js +0 -539
- package/packages/@monomind/cli/dist/src/ruvector/coverage-tools.d.ts +0 -33
- package/packages/@monomind/cli/dist/src/ruvector/coverage-tools.js +0 -157
- package/packages/@monomind/cli/dist/src/ruvector/enhanced-model-router.d.ts +0 -146
- package/packages/@monomind/cli/dist/src/ruvector/enhanced-model-router.js +0 -551
- package/packages/@monomind/cli/dist/src/ruvector/flash-attention.d.ts +0 -195
- package/packages/@monomind/cli/dist/src/ruvector/flash-attention.js +0 -643
- package/packages/@monomind/cli/dist/src/ruvector/graph-analyzer.d.ts +0 -187
- package/packages/@monomind/cli/dist/src/ruvector/graph-analyzer.js +0 -929
- package/packages/@monomind/cli/dist/src/ruvector/index.d.ts +0 -56
- package/packages/@monomind/cli/dist/src/ruvector/index.js +0 -58
- package/packages/@monomind/cli/dist/src/ruvector/lora-adapter.d.ts +0 -218
- package/packages/@monomind/cli/dist/src/ruvector/lora-adapter.js +0 -455
- package/packages/@monomind/cli/dist/src/ruvector/model-router.d.ts +0 -222
- package/packages/@monomind/cli/dist/src/ruvector/model-router.js +0 -512
- package/packages/@monomind/cli/dist/src/ruvector/moe-router.d.ts +0 -213
- package/packages/@monomind/cli/dist/src/ruvector/moe-router.js +0 -649
- package/packages/@monomind/cli/dist/src/ruvector/q-learning-router.d.ts +0 -217
- package/packages/@monomind/cli/dist/src/ruvector/q-learning-router.js +0 -712
- package/packages/@monomind/cli/dist/src/ruvector/ruvllm-wasm.d.ts +0 -179
- package/packages/@monomind/cli/dist/src/ruvector/ruvllm-wasm.js +0 -363
- package/packages/@monomind/cli/dist/src/ruvector/semantic-router.d.ts +0 -77
- package/packages/@monomind/cli/dist/src/ruvector/semantic-router.js +0 -178
- package/packages/@monomind/cli/dist/src/ruvector/vector-db.d.ts +0 -69
- package/packages/@monomind/cli/dist/src/ruvector/vector-db.js +0 -243
- package/packages/@monomind/cli/dist/src/services/ruvector-training.d.ts +0 -222
- package/packages/@monomind/cli/dist/src/services/ruvector-training.js +0 -696
- /package/packages/@monomind/cli/dist/src/{ruvector → monovector}/diff-classifier.js +0 -0
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Coverage Router for Test Routing
|
|
3
|
-
*
|
|
4
|
-
* Optimizations:
|
|
5
|
-
* - Async file I/O for non-blocking coverage loading
|
|
6
|
-
* - TTL-based caching of coverage data
|
|
7
|
-
* - Singleton router instance
|
|
8
|
-
*/
|
|
9
|
-
/**
|
|
10
|
-
* Clear coverage cache
|
|
11
|
-
*/
|
|
12
|
-
export declare function clearCoverageCache(): void;
|
|
13
|
-
/**
|
|
14
|
-
* Get coverage cache stats
|
|
15
|
-
*/
|
|
16
|
-
export declare function getCoverageCacheStats(): {
|
|
17
|
-
size: number;
|
|
18
|
-
};
|
|
19
|
-
export interface CoverageRouterConfig {
|
|
20
|
-
minCoverage: number;
|
|
21
|
-
targetCoverage: number;
|
|
22
|
-
incremental: boolean;
|
|
23
|
-
coverageTypes: ('line' | 'branch' | 'function' | 'statement')[];
|
|
24
|
-
}
|
|
25
|
-
export interface FileCoverage {
|
|
26
|
-
path: string;
|
|
27
|
-
lineCoverage: number;
|
|
28
|
-
branchCoverage: number;
|
|
29
|
-
functionCoverage: number;
|
|
30
|
-
statementCoverage: number;
|
|
31
|
-
uncoveredLines: number[];
|
|
32
|
-
totalLines: number;
|
|
33
|
-
coveredLines: number;
|
|
34
|
-
}
|
|
35
|
-
export interface CoverageReport {
|
|
36
|
-
overall: number;
|
|
37
|
-
byType: {
|
|
38
|
-
line: number;
|
|
39
|
-
branch: number;
|
|
40
|
-
function: number;
|
|
41
|
-
statement: number;
|
|
42
|
-
};
|
|
43
|
-
byFile: FileCoverage[];
|
|
44
|
-
lowestCoverage: FileCoverage[];
|
|
45
|
-
highestCoverage: FileCoverage[];
|
|
46
|
-
uncoveredCritical: string[];
|
|
47
|
-
timestamp: number;
|
|
48
|
-
}
|
|
49
|
-
export interface CoverageRouteResult {
|
|
50
|
-
action: 'add-tests' | 'review-coverage' | 'skip' | 'prioritize';
|
|
51
|
-
priority: number;
|
|
52
|
-
targetFiles: string[];
|
|
53
|
-
testTypes: ('unit' | 'integration' | 'e2e')[];
|
|
54
|
-
gaps: Array<{
|
|
55
|
-
file: string;
|
|
56
|
-
currentCoverage: number;
|
|
57
|
-
targetCoverage: number;
|
|
58
|
-
gap: number;
|
|
59
|
-
suggestedTests: string[];
|
|
60
|
-
}>;
|
|
61
|
-
estimatedEffort: number;
|
|
62
|
-
impactScore: number;
|
|
63
|
-
}
|
|
64
|
-
export declare class CoverageRouter {
|
|
65
|
-
private config;
|
|
66
|
-
private ruvectorEngine;
|
|
67
|
-
private useNative;
|
|
68
|
-
private coverageHistory;
|
|
69
|
-
constructor(config?: Partial<CoverageRouterConfig>);
|
|
70
|
-
initialize(): Promise<void>;
|
|
71
|
-
parseCoverage(data: unknown, format?: 'lcov' | 'istanbul' | 'cobertura' | 'json'): CoverageReport;
|
|
72
|
-
route(coverage: CoverageReport, changedFiles?: string[]): CoverageRouteResult;
|
|
73
|
-
getTrend(): {
|
|
74
|
-
direction: 'up' | 'down' | 'stable';
|
|
75
|
-
change: number;
|
|
76
|
-
};
|
|
77
|
-
addToHistory(report: CoverageReport): void;
|
|
78
|
-
getStats(): Record<string, number | boolean>;
|
|
79
|
-
private parseLcov;
|
|
80
|
-
private parseIstanbul;
|
|
81
|
-
private parseCobertura;
|
|
82
|
-
private parseJson;
|
|
83
|
-
private finalizeFileCoverage;
|
|
84
|
-
private buildReport;
|
|
85
|
-
private findCriticalUncovered;
|
|
86
|
-
private calculateGaps;
|
|
87
|
-
private suggestTests;
|
|
88
|
-
private prioritizeFiles;
|
|
89
|
-
private determineAction;
|
|
90
|
-
private calculatePriority;
|
|
91
|
-
private recommendTestTypes;
|
|
92
|
-
private estimateEffort;
|
|
93
|
-
private calculateImpact;
|
|
94
|
-
}
|
|
95
|
-
export declare function createCoverageRouter(config?: Partial<CoverageRouterConfig>): CoverageRouter;
|
|
96
|
-
/**
|
|
97
|
-
* Coverage suggestion result
|
|
98
|
-
*/
|
|
99
|
-
export interface CoverageSuggestResult {
|
|
100
|
-
path: string;
|
|
101
|
-
suggestions: Array<{
|
|
102
|
-
file: string;
|
|
103
|
-
currentCoverage: number;
|
|
104
|
-
targetCoverage: number;
|
|
105
|
-
gap: number;
|
|
106
|
-
priority: number;
|
|
107
|
-
suggestedTests: string[];
|
|
108
|
-
}>;
|
|
109
|
-
totalGap: number;
|
|
110
|
-
estimatedEffort: number;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Coverage gaps result
|
|
114
|
-
*/
|
|
115
|
-
export interface CoverageGapsResult {
|
|
116
|
-
totalGaps: number;
|
|
117
|
-
gaps: Array<{
|
|
118
|
-
file: string;
|
|
119
|
-
currentCoverage: number;
|
|
120
|
-
targetCoverage: number;
|
|
121
|
-
gap: number;
|
|
122
|
-
priority: number;
|
|
123
|
-
suggestedAgent: string;
|
|
124
|
-
}>;
|
|
125
|
-
byAgent: Record<string, string[]>;
|
|
126
|
-
summary: string;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Coverage route options
|
|
130
|
-
*/
|
|
131
|
-
export interface CoverageRouteOptions {
|
|
132
|
-
projectRoot?: string;
|
|
133
|
-
threshold?: number;
|
|
134
|
-
useRuvector?: boolean;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Coverage suggest options
|
|
138
|
-
*/
|
|
139
|
-
export interface CoverageSuggestOptions extends CoverageRouteOptions {
|
|
140
|
-
limit?: number;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Coverage gaps options
|
|
144
|
-
*/
|
|
145
|
-
export interface CoverageGapsOptions extends CoverageRouteOptions {
|
|
146
|
-
groupByAgent?: boolean;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Route a task based on coverage analysis
|
|
150
|
-
*/
|
|
151
|
-
export declare function coverageRoute(task: string, options?: CoverageRouteOptions): Promise<CoverageRouteResult>;
|
|
152
|
-
/**
|
|
153
|
-
* Suggest coverage improvements for a path
|
|
154
|
-
*/
|
|
155
|
-
export declare function coverageSuggest(path: string, options?: CoverageSuggestOptions): Promise<CoverageSuggestResult>;
|
|
156
|
-
/**
|
|
157
|
-
* List all coverage gaps with agent assignments
|
|
158
|
-
*/
|
|
159
|
-
export declare function coverageGaps(options?: CoverageGapsOptions): Promise<CoverageGapsResult>;
|
|
160
|
-
//# sourceMappingURL=coverage-router.d.ts.map
|
|
@@ -1,539 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Coverage Router for Test Routing
|
|
3
|
-
*
|
|
4
|
-
* Optimizations:
|
|
5
|
-
* - Async file I/O for non-blocking coverage loading
|
|
6
|
-
* - TTL-based caching of coverage data
|
|
7
|
-
* - Singleton router instance
|
|
8
|
-
*/
|
|
9
|
-
// ============================================================================
|
|
10
|
-
// Caching for Performance
|
|
11
|
-
// ============================================================================
|
|
12
|
-
import { existsSync } from 'node:fs';
|
|
13
|
-
import { readFile } from 'node:fs/promises';
|
|
14
|
-
import { resolve, normalize, isAbsolute, join } from 'node:path';
|
|
15
|
-
/**
|
|
16
|
-
* Cache for coverage data (1 minute TTL)
|
|
17
|
-
*/
|
|
18
|
-
const coverageDataCache = new Map();
|
|
19
|
-
const COVERAGE_CACHE_TTL_MS = 60 * 1000; // 1 minute
|
|
20
|
-
const COVERAGE_CACHE_MAX = 50;
|
|
21
|
-
/**
|
|
22
|
-
* Clear coverage cache
|
|
23
|
-
*/
|
|
24
|
-
export function clearCoverageCache() {
|
|
25
|
-
coverageDataCache.clear();
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Get coverage cache stats
|
|
29
|
-
*/
|
|
30
|
-
export function getCoverageCacheStats() {
|
|
31
|
-
return { size: coverageDataCache.size };
|
|
32
|
-
}
|
|
33
|
-
const DEFAULT_CONFIG = {
|
|
34
|
-
minCoverage: 70,
|
|
35
|
-
targetCoverage: 85,
|
|
36
|
-
incremental: true,
|
|
37
|
-
coverageTypes: ['line', 'branch', 'function', 'statement'],
|
|
38
|
-
};
|
|
39
|
-
export class CoverageRouter {
|
|
40
|
-
config;
|
|
41
|
-
ruvectorEngine = null;
|
|
42
|
-
useNative = false;
|
|
43
|
-
coverageHistory = [];
|
|
44
|
-
constructor(config = {}) {
|
|
45
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
46
|
-
}
|
|
47
|
-
async initialize() {
|
|
48
|
-
try {
|
|
49
|
-
// @ruvector/coverage is optional - gracefully fallback if not installed
|
|
50
|
-
const ruvector = await import('@ruvector/coverage').catch(() => null);
|
|
51
|
-
if (ruvector) {
|
|
52
|
-
this.ruvectorEngine = ruvector.createCoverageRouter?.(this.config);
|
|
53
|
-
this.useNative = !!this.ruvectorEngine;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
this.useNative = false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
parseCoverage(data, format = 'json') {
|
|
61
|
-
switch (format) {
|
|
62
|
-
case 'lcov': return this.parseLcov(data);
|
|
63
|
-
case 'istanbul': return this.parseIstanbul(data);
|
|
64
|
-
case 'cobertura': return this.parseCobertura(data);
|
|
65
|
-
default: return this.parseJson(data);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
route(coverage, changedFiles) {
|
|
69
|
-
const gaps = this.calculateGaps(coverage);
|
|
70
|
-
const targetFiles = this.prioritizeFiles(coverage, changedFiles);
|
|
71
|
-
const action = this.determineAction(coverage, gaps);
|
|
72
|
-
const priority = this.calculatePriority(coverage, changedFiles);
|
|
73
|
-
const testTypes = this.recommendTestTypes(gaps);
|
|
74
|
-
const estimatedEffort = this.estimateEffort(gaps);
|
|
75
|
-
const impactScore = this.calculateImpact(coverage, targetFiles);
|
|
76
|
-
return { action, priority, targetFiles, testTypes, gaps, estimatedEffort, impactScore };
|
|
77
|
-
}
|
|
78
|
-
getTrend() {
|
|
79
|
-
if (this.coverageHistory.length < 2)
|
|
80
|
-
return { direction: 'stable', change: 0 };
|
|
81
|
-
const recent = this.coverageHistory[this.coverageHistory.length - 1];
|
|
82
|
-
const previous = this.coverageHistory[this.coverageHistory.length - 2];
|
|
83
|
-
const change = recent.overall - previous.overall;
|
|
84
|
-
return { direction: change > 0.5 ? 'up' : change < -0.5 ? 'down' : 'stable', change };
|
|
85
|
-
}
|
|
86
|
-
addToHistory(report) {
|
|
87
|
-
this.coverageHistory.push(report);
|
|
88
|
-
if (this.coverageHistory.length > 10)
|
|
89
|
-
this.coverageHistory.shift();
|
|
90
|
-
}
|
|
91
|
-
getStats() {
|
|
92
|
-
return { useNative: this.useNative, historySize: this.coverageHistory.length, minCoverage: this.config.minCoverage, targetCoverage: this.config.targetCoverage };
|
|
93
|
-
}
|
|
94
|
-
parseLcov(data) {
|
|
95
|
-
const files = [];
|
|
96
|
-
let currentFile = null;
|
|
97
|
-
const lines = data.split('\n');
|
|
98
|
-
for (const line of lines) {
|
|
99
|
-
if (line.startsWith('SF:')) {
|
|
100
|
-
if (currentFile?.path)
|
|
101
|
-
files.push(this.finalizeFileCoverage(currentFile));
|
|
102
|
-
currentFile = { path: line.substring(3), uncoveredLines: [], totalLines: 0, coveredLines: 0 };
|
|
103
|
-
}
|
|
104
|
-
else if (line.startsWith('LF:')) {
|
|
105
|
-
if (currentFile)
|
|
106
|
-
currentFile.totalLines = parseInt(line.substring(3), 10);
|
|
107
|
-
}
|
|
108
|
-
else if (line.startsWith('LH:')) {
|
|
109
|
-
if (currentFile)
|
|
110
|
-
currentFile.coveredLines = parseInt(line.substring(3), 10);
|
|
111
|
-
}
|
|
112
|
-
else if (line.startsWith('DA:')) {
|
|
113
|
-
const [lineNum, hits] = line.substring(3).split(',').map(Number);
|
|
114
|
-
if (currentFile && hits === 0)
|
|
115
|
-
currentFile.uncoveredLines?.push(lineNum);
|
|
116
|
-
}
|
|
117
|
-
else if (line === 'end_of_record') {
|
|
118
|
-
if (currentFile?.path)
|
|
119
|
-
files.push(this.finalizeFileCoverage(currentFile));
|
|
120
|
-
currentFile = null;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return this.buildReport(files);
|
|
124
|
-
}
|
|
125
|
-
parseIstanbul(data) {
|
|
126
|
-
const files = [];
|
|
127
|
-
for (const [path, coverage] of Object.entries(data)) {
|
|
128
|
-
const cov = coverage;
|
|
129
|
-
const statements = cov.s;
|
|
130
|
-
const functions = cov.f;
|
|
131
|
-
const branches = cov.b;
|
|
132
|
-
const statementCovered = Object.values(statements).filter(v => v > 0).length;
|
|
133
|
-
const statementTotal = Object.values(statements).length;
|
|
134
|
-
const functionCovered = Object.values(functions).filter(v => v > 0).length;
|
|
135
|
-
const functionTotal = Object.values(functions).length;
|
|
136
|
-
const branchCovered = Object.values(branches).flat().filter(v => v > 0).length;
|
|
137
|
-
const branchTotal = Object.values(branches).flat().length;
|
|
138
|
-
files.push({
|
|
139
|
-
path, lineCoverage: statementTotal > 0 ? (statementCovered / statementTotal) * 100 : 100,
|
|
140
|
-
branchCoverage: branchTotal > 0 ? (branchCovered / branchTotal) * 100 : 100,
|
|
141
|
-
functionCoverage: functionTotal > 0 ? (functionCovered / functionTotal) * 100 : 100,
|
|
142
|
-
statementCoverage: statementTotal > 0 ? (statementCovered / statementTotal) * 100 : 100,
|
|
143
|
-
uncoveredLines: [], totalLines: statementTotal, coveredLines: statementCovered,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
return this.buildReport(files);
|
|
147
|
-
}
|
|
148
|
-
parseCobertura(data) {
|
|
149
|
-
const files = [];
|
|
150
|
-
const classMatches = data.matchAll(/<class[^>]*filename="([^"]+)"[^>]*line-rate="([^"]+)"[^>]*branch-rate="([^"]+)"[^>]*>/g);
|
|
151
|
-
for (const match of classMatches) {
|
|
152
|
-
files.push({
|
|
153
|
-
path: match[1], lineCoverage: parseFloat(match[2]) * 100, branchCoverage: parseFloat(match[3]) * 100,
|
|
154
|
-
functionCoverage: parseFloat(match[2]) * 100, statementCoverage: parseFloat(match[2]) * 100,
|
|
155
|
-
uncoveredLines: [], totalLines: 0, coveredLines: 0,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
return this.buildReport(files);
|
|
159
|
-
}
|
|
160
|
-
parseJson(data) {
|
|
161
|
-
if (Array.isArray(data))
|
|
162
|
-
return this.buildReport(data);
|
|
163
|
-
const files = [];
|
|
164
|
-
for (const [path, coverage] of Object.entries(data)) {
|
|
165
|
-
const cov = coverage;
|
|
166
|
-
files.push({
|
|
167
|
-
path, lineCoverage: cov.lineCoverage || 0, branchCoverage: cov.branchCoverage || 0,
|
|
168
|
-
functionCoverage: cov.functionCoverage || 0, statementCoverage: cov.statementCoverage || 0,
|
|
169
|
-
uncoveredLines: cov.uncoveredLines || [], totalLines: cov.totalLines || 0, coveredLines: cov.coveredLines || 0,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
return this.buildReport(files);
|
|
173
|
-
}
|
|
174
|
-
finalizeFileCoverage(partial) {
|
|
175
|
-
const lineCoverage = partial.totalLines && partial.totalLines > 0 ? (partial.coveredLines || 0) / partial.totalLines * 100 : 100;
|
|
176
|
-
return { path: partial.path || 'unknown', lineCoverage, branchCoverage: lineCoverage, functionCoverage: lineCoverage, statementCoverage: lineCoverage, uncoveredLines: partial.uncoveredLines || [], totalLines: partial.totalLines || 0, coveredLines: partial.coveredLines || 0 };
|
|
177
|
-
}
|
|
178
|
-
buildReport(files) {
|
|
179
|
-
const totalLines = files.reduce((sum, f) => sum + f.totalLines, 0);
|
|
180
|
-
const coveredLines = files.reduce((sum, f) => sum + f.coveredLines, 0);
|
|
181
|
-
const overall = totalLines > 0 ? (coveredLines / totalLines) * 100 : 100;
|
|
182
|
-
const avgLine = files.length > 0 ? files.reduce((sum, f) => sum + f.lineCoverage, 0) / files.length : 100;
|
|
183
|
-
const avgBranch = files.length > 0 ? files.reduce((sum, f) => sum + f.branchCoverage, 0) / files.length : 100;
|
|
184
|
-
const avgFunction = files.length > 0 ? files.reduce((sum, f) => sum + f.functionCoverage, 0) / files.length : 100;
|
|
185
|
-
const avgStatement = files.length > 0 ? files.reduce((sum, f) => sum + f.statementCoverage, 0) / files.length : 100;
|
|
186
|
-
const sortedByLine = [...files].sort((a, b) => a.lineCoverage - b.lineCoverage);
|
|
187
|
-
return { overall, byType: { line: avgLine, branch: avgBranch, function: avgFunction, statement: avgStatement }, byFile: files, lowestCoverage: sortedByLine.slice(0, 5), highestCoverage: sortedByLine.slice(-5).reverse(), uncoveredCritical: this.findCriticalUncovered(files), timestamp: Date.now() };
|
|
188
|
-
}
|
|
189
|
-
findCriticalUncovered(files) {
|
|
190
|
-
const critical = [];
|
|
191
|
-
const criticalPatterns = [/auth/, /security/, /payment/, /core/, /main/, /index/];
|
|
192
|
-
for (const file of files) {
|
|
193
|
-
if (file.lineCoverage < this.config.minCoverage) {
|
|
194
|
-
for (const pattern of criticalPatterns) {
|
|
195
|
-
if (pattern.test(file.path)) {
|
|
196
|
-
critical.push(file.path);
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return critical.slice(0, 10);
|
|
203
|
-
}
|
|
204
|
-
calculateGaps(coverage) {
|
|
205
|
-
const gaps = [];
|
|
206
|
-
for (const file of coverage.byFile) {
|
|
207
|
-
if (file.lineCoverage < this.config.targetCoverage) {
|
|
208
|
-
const gap = this.config.targetCoverage - file.lineCoverage;
|
|
209
|
-
gaps.push({ file: file.path, currentCoverage: file.lineCoverage, targetCoverage: this.config.targetCoverage, gap, suggestedTests: this.suggestTests(file) });
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return gaps.sort((a, b) => b.gap - a.gap).slice(0, 10);
|
|
213
|
-
}
|
|
214
|
-
suggestTests(file) {
|
|
215
|
-
const suggestions = [];
|
|
216
|
-
if (file.uncoveredLines.length > 10)
|
|
217
|
-
suggestions.push('Add unit tests for uncovered code paths');
|
|
218
|
-
if (file.branchCoverage < 50)
|
|
219
|
-
suggestions.push('Add branch coverage tests (if/else paths)');
|
|
220
|
-
if (file.functionCoverage < 80)
|
|
221
|
-
suggestions.push('Add tests for untested functions');
|
|
222
|
-
if (/api|endpoint|route|handler/.test(file.path))
|
|
223
|
-
suggestions.push('Add integration tests for API endpoints');
|
|
224
|
-
return suggestions.slice(0, 3);
|
|
225
|
-
}
|
|
226
|
-
prioritizeFiles(coverage, changedFiles) {
|
|
227
|
-
let targetFiles = coverage.lowestCoverage.map(f => f.path);
|
|
228
|
-
if (changedFiles && changedFiles.length > 0) {
|
|
229
|
-
const changedWithLowCoverage = coverage.byFile.filter(f => changedFiles.some(cf => f.path.includes(cf))).filter(f => f.lineCoverage < this.config.targetCoverage).map(f => f.path);
|
|
230
|
-
targetFiles = [...new Set([...changedWithLowCoverage, ...targetFiles])];
|
|
231
|
-
}
|
|
232
|
-
return targetFiles.slice(0, 10);
|
|
233
|
-
}
|
|
234
|
-
determineAction(coverage, gaps) {
|
|
235
|
-
if (coverage.overall < this.config.minCoverage)
|
|
236
|
-
return 'prioritize';
|
|
237
|
-
if (gaps.length > 5)
|
|
238
|
-
return 'add-tests';
|
|
239
|
-
if (coverage.overall < this.config.targetCoverage)
|
|
240
|
-
return 'review-coverage';
|
|
241
|
-
return 'skip';
|
|
242
|
-
}
|
|
243
|
-
calculatePriority(coverage, changedFiles) {
|
|
244
|
-
let priority = 5;
|
|
245
|
-
if (coverage.overall < 50)
|
|
246
|
-
priority += 4;
|
|
247
|
-
else if (coverage.overall < 70)
|
|
248
|
-
priority += 2;
|
|
249
|
-
else if (coverage.overall < 85)
|
|
250
|
-
priority += 1;
|
|
251
|
-
priority += Math.min(3, coverage.uncoveredCritical.length);
|
|
252
|
-
if (changedFiles && changedFiles.length > 0) {
|
|
253
|
-
const changedLowCoverage = coverage.byFile.filter(f => changedFiles.some(cf => f.path.includes(cf))).filter(f => f.lineCoverage < this.config.minCoverage);
|
|
254
|
-
priority += Math.min(2, changedLowCoverage.length);
|
|
255
|
-
}
|
|
256
|
-
return Math.min(10, priority);
|
|
257
|
-
}
|
|
258
|
-
recommendTestTypes(gaps) {
|
|
259
|
-
const types = new Set(['unit']);
|
|
260
|
-
for (const gap of gaps) {
|
|
261
|
-
if (/api|endpoint|route|handler|service/.test(gap.file))
|
|
262
|
-
types.add('integration');
|
|
263
|
-
if (/page|component|view|ui/.test(gap.file))
|
|
264
|
-
types.add('e2e');
|
|
265
|
-
}
|
|
266
|
-
return Array.from(types);
|
|
267
|
-
}
|
|
268
|
-
estimateEffort(gaps) {
|
|
269
|
-
let effort = 0;
|
|
270
|
-
for (const gap of gaps)
|
|
271
|
-
effort += (gap.gap / 10) * 0.5;
|
|
272
|
-
return Math.round(effort * 10) / 10;
|
|
273
|
-
}
|
|
274
|
-
calculateImpact(coverage, targetFiles) {
|
|
275
|
-
const potentialGain = targetFiles.reduce((sum, file) => {
|
|
276
|
-
const fileCov = coverage.byFile.find(f => f.path === file);
|
|
277
|
-
return fileCov ? sum + (this.config.targetCoverage - fileCov.lineCoverage) : sum;
|
|
278
|
-
}, 0);
|
|
279
|
-
return Math.min(100, Math.round(potentialGain / targetFiles.length || 0));
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
export function createCoverageRouter(config) {
|
|
283
|
-
return new CoverageRouter(config);
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Route a task based on coverage analysis
|
|
287
|
-
*/
|
|
288
|
-
export async function coverageRoute(task, options = {}) {
|
|
289
|
-
const router = new CoverageRouter({
|
|
290
|
-
targetCoverage: options.threshold || 80,
|
|
291
|
-
});
|
|
292
|
-
// Try to load coverage data
|
|
293
|
-
const coverage = await loadProjectCoverage(options.projectRoot);
|
|
294
|
-
if (!coverage) {
|
|
295
|
-
return {
|
|
296
|
-
action: 'skip',
|
|
297
|
-
priority: 1,
|
|
298
|
-
targetFiles: [],
|
|
299
|
-
testTypes: ['unit'],
|
|
300
|
-
gaps: [],
|
|
301
|
-
estimatedEffort: 0,
|
|
302
|
-
impactScore: 0,
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
return router.route(coverage);
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Suggest coverage improvements for a path
|
|
309
|
-
*/
|
|
310
|
-
export async function coverageSuggest(path, options = {}) {
|
|
311
|
-
const limit = options.limit || 20;
|
|
312
|
-
const threshold = options.threshold || 80;
|
|
313
|
-
const coverage = await loadProjectCoverage(options.projectRoot);
|
|
314
|
-
if (!coverage) {
|
|
315
|
-
return {
|
|
316
|
-
path,
|
|
317
|
-
suggestions: [],
|
|
318
|
-
totalGap: 0,
|
|
319
|
-
estimatedEffort: 0,
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
// Filter files matching the path
|
|
323
|
-
const matchingFiles = coverage.byFile.filter(f => f.path.includes(path));
|
|
324
|
-
const belowThreshold = matchingFiles.filter(f => f.lineCoverage < threshold);
|
|
325
|
-
const suggestions = belowThreshold
|
|
326
|
-
.map(f => ({
|
|
327
|
-
file: f.path,
|
|
328
|
-
currentCoverage: f.lineCoverage,
|
|
329
|
-
targetCoverage: threshold,
|
|
330
|
-
gap: threshold - f.lineCoverage,
|
|
331
|
-
priority: calculateFilePriority(f.path, f.lineCoverage, threshold),
|
|
332
|
-
suggestedTests: suggestTestsForFile(f),
|
|
333
|
-
}))
|
|
334
|
-
.sort((a, b) => b.priority - a.priority)
|
|
335
|
-
.slice(0, limit);
|
|
336
|
-
const totalGap = suggestions.reduce((sum, s) => sum + s.gap, 0);
|
|
337
|
-
const estimatedEffort = totalGap * 0.1; // Rough estimate: 0.1 hours per % gap
|
|
338
|
-
return { path, suggestions, totalGap, estimatedEffort };
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* List all coverage gaps with agent assignments
|
|
342
|
-
*/
|
|
343
|
-
export async function coverageGaps(options = {}) {
|
|
344
|
-
const threshold = options.threshold || 80;
|
|
345
|
-
const groupByAgent = options.groupByAgent !== false;
|
|
346
|
-
const coverage = await loadProjectCoverage(options.projectRoot);
|
|
347
|
-
if (!coverage) {
|
|
348
|
-
return {
|
|
349
|
-
totalGaps: 0,
|
|
350
|
-
gaps: [],
|
|
351
|
-
byAgent: {},
|
|
352
|
-
summary: 'No coverage data found',
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
const belowThreshold = coverage.byFile.filter(f => f.lineCoverage < threshold);
|
|
356
|
-
const gaps = belowThreshold.map(f => ({
|
|
357
|
-
file: f.path,
|
|
358
|
-
currentCoverage: f.lineCoverage,
|
|
359
|
-
targetCoverage: threshold,
|
|
360
|
-
gap: threshold - f.lineCoverage,
|
|
361
|
-
priority: calculateFilePriority(f.path, f.lineCoverage, threshold),
|
|
362
|
-
suggestedAgent: suggestAgentForFile(f.path),
|
|
363
|
-
}));
|
|
364
|
-
const byAgent = {};
|
|
365
|
-
if (groupByAgent) {
|
|
366
|
-
for (const gap of gaps) {
|
|
367
|
-
if (!byAgent[gap.suggestedAgent]) {
|
|
368
|
-
byAgent[gap.suggestedAgent] = [];
|
|
369
|
-
}
|
|
370
|
-
byAgent[gap.suggestedAgent].push(gap.file);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return {
|
|
374
|
-
totalGaps: gaps.length,
|
|
375
|
-
gaps,
|
|
376
|
-
byAgent,
|
|
377
|
-
summary: `${gaps.length} files below ${threshold}% coverage threshold`,
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Validate and normalize path to prevent directory traversal
|
|
382
|
-
* Returns null if path is invalid or attempts traversal
|
|
383
|
-
*/
|
|
384
|
-
function validateProjectPath(inputPath) {
|
|
385
|
-
// resolve, normalize, isAbsolute imported at top of module
|
|
386
|
-
// Default to cwd if not provided
|
|
387
|
-
const basePath = inputPath || process.cwd();
|
|
388
|
-
// Normalize and resolve the path
|
|
389
|
-
const normalizedPath = normalize(basePath);
|
|
390
|
-
const resolvedPath = isAbsolute(normalizedPath) ? normalizedPath : resolve(process.cwd(), normalizedPath);
|
|
391
|
-
// Enforce containment: resolved path must stay within cwd regardless of how it was expressed
|
|
392
|
-
if (!resolvedPath.startsWith(process.cwd())) {
|
|
393
|
-
return null;
|
|
394
|
-
}
|
|
395
|
-
// Additional validation: no null bytes or control characters
|
|
396
|
-
if (/[\x00-\x1f]/.test(resolvedPath)) {
|
|
397
|
-
return null;
|
|
398
|
-
}
|
|
399
|
-
// Limit path length to prevent DoS
|
|
400
|
-
if (resolvedPath.length > 4096) {
|
|
401
|
-
return null;
|
|
402
|
-
}
|
|
403
|
-
return resolvedPath;
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Load project coverage data (async with caching)
|
|
407
|
-
*/
|
|
408
|
-
async function loadProjectCoverage(projectRoot, skipCache) {
|
|
409
|
-
// Validate and normalize the project root path
|
|
410
|
-
const root = validateProjectPath(projectRoot);
|
|
411
|
-
if (!root) {
|
|
412
|
-
// Invalid path detected, return null safely
|
|
413
|
-
return null;
|
|
414
|
-
}
|
|
415
|
-
// Check cache first
|
|
416
|
-
if (!skipCache) {
|
|
417
|
-
const cached = coverageDataCache.get(root);
|
|
418
|
-
if (cached && Date.now() - cached.timestamp < COVERAGE_CACHE_TTL_MS) {
|
|
419
|
-
return cached.report;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
// existsSync, readFile, join, normalize imported at top of module
|
|
423
|
-
// Try common coverage locations (all relative to validated root)
|
|
424
|
-
const coverageLocations = [
|
|
425
|
-
['coverage', 'coverage-final.json'],
|
|
426
|
-
['coverage', 'lcov.info'],
|
|
427
|
-
['.nyc_output', 'coverage.json'],
|
|
428
|
-
['coverage.json'],
|
|
429
|
-
];
|
|
430
|
-
for (const pathParts of coverageLocations) {
|
|
431
|
-
// Join and normalize to prevent traversal in coverage paths
|
|
432
|
-
const coveragePath = normalize(join(root, ...pathParts));
|
|
433
|
-
// Ensure the coverage path is still within or under root
|
|
434
|
-
if (!coveragePath.startsWith(root)) {
|
|
435
|
-
continue;
|
|
436
|
-
}
|
|
437
|
-
if (existsSync(coveragePath)) {
|
|
438
|
-
try {
|
|
439
|
-
const { stat } = await import('node:fs/promises');
|
|
440
|
-
const MAX_COVERAGE_SIZE = 50 * 1024 * 1024; // 50 MB
|
|
441
|
-
const fileStats = await stat(coveragePath);
|
|
442
|
-
if (fileStats.size > MAX_COVERAGE_SIZE)
|
|
443
|
-
continue;
|
|
444
|
-
// Use async file read for non-blocking I/O
|
|
445
|
-
const content = await readFile(coveragePath, 'utf-8');
|
|
446
|
-
const router = new CoverageRouter();
|
|
447
|
-
let report = null;
|
|
448
|
-
if (coveragePath.endsWith('.json')) {
|
|
449
|
-
report = router.parseCoverage(JSON.parse(content), 'istanbul');
|
|
450
|
-
}
|
|
451
|
-
else if (coveragePath.endsWith('.info')) {
|
|
452
|
-
report = router.parseCoverage(content, 'lcov');
|
|
453
|
-
}
|
|
454
|
-
// Cache the result (evict oldest entry if at capacity)
|
|
455
|
-
if (report) {
|
|
456
|
-
if (coverageDataCache.size >= COVERAGE_CACHE_MAX) {
|
|
457
|
-
const firstKey = coverageDataCache.keys().next().value;
|
|
458
|
-
if (firstKey !== undefined)
|
|
459
|
-
coverageDataCache.delete(firstKey);
|
|
460
|
-
}
|
|
461
|
-
coverageDataCache.set(root, { report, timestamp: Date.now() });
|
|
462
|
-
return report;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
catch {
|
|
466
|
-
// Continue to next path
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
return null;
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Calculate priority for a file based on path and coverage
|
|
474
|
-
*/
|
|
475
|
-
function calculateFilePriority(path, coverage, threshold) {
|
|
476
|
-
let priority = 5;
|
|
477
|
-
// Gap-based priority
|
|
478
|
-
const gap = threshold - coverage;
|
|
479
|
-
if (gap > 50)
|
|
480
|
-
priority += 3;
|
|
481
|
-
else if (gap > 30)
|
|
482
|
-
priority += 2;
|
|
483
|
-
else if (gap > 15)
|
|
484
|
-
priority += 1;
|
|
485
|
-
// Path-based priority
|
|
486
|
-
const lowerPath = path.toLowerCase();
|
|
487
|
-
if (/core|main|index/.test(lowerPath))
|
|
488
|
-
priority += 2;
|
|
489
|
-
if (/auth|security|payment/.test(lowerPath))
|
|
490
|
-
priority += 3;
|
|
491
|
-
if (/api|service|controller/.test(lowerPath))
|
|
492
|
-
priority += 1;
|
|
493
|
-
if (/util|helper/.test(lowerPath))
|
|
494
|
-
priority -= 1;
|
|
495
|
-
if (/test|spec|mock/.test(lowerPath))
|
|
496
|
-
priority -= 2;
|
|
497
|
-
return Math.max(1, Math.min(10, priority));
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Suggest tests for a file based on its coverage
|
|
501
|
-
*/
|
|
502
|
-
function suggestTestsForFile(file) {
|
|
503
|
-
const suggestions = [];
|
|
504
|
-
if (file.uncoveredLines.length > 10) {
|
|
505
|
-
suggestions.push('Add unit tests for uncovered code paths');
|
|
506
|
-
}
|
|
507
|
-
if (file.branchCoverage < 50) {
|
|
508
|
-
suggestions.push('Add branch coverage tests (if/else paths)');
|
|
509
|
-
}
|
|
510
|
-
if (file.functionCoverage < 80) {
|
|
511
|
-
suggestions.push('Add tests for untested functions');
|
|
512
|
-
}
|
|
513
|
-
const lowerPath = file.path.toLowerCase();
|
|
514
|
-
if (/api|endpoint|route|handler/.test(lowerPath)) {
|
|
515
|
-
suggestions.push('Add integration tests for API endpoints');
|
|
516
|
-
}
|
|
517
|
-
if (/component|view|ui/.test(lowerPath)) {
|
|
518
|
-
suggestions.push('Add component tests with user interactions');
|
|
519
|
-
}
|
|
520
|
-
return suggestions.slice(0, 3);
|
|
521
|
-
}
|
|
522
|
-
/**
|
|
523
|
-
* Suggest an agent type for a file
|
|
524
|
-
*/
|
|
525
|
-
function suggestAgentForFile(path) {
|
|
526
|
-
const lowerPath = path.toLowerCase();
|
|
527
|
-
if (/api|endpoint|route|controller/.test(lowerPath))
|
|
528
|
-
return 'api-tester';
|
|
529
|
-
if (/component|view|ui|page/.test(lowerPath))
|
|
530
|
-
return 'ui-tester';
|
|
531
|
-
if (/service|repository|model/.test(lowerPath))
|
|
532
|
-
return 'unit-tester';
|
|
533
|
-
if (/integration|e2e/.test(lowerPath))
|
|
534
|
-
return 'e2e-tester';
|
|
535
|
-
if (/util|helper|lib/.test(lowerPath))
|
|
536
|
-
return 'unit-tester';
|
|
537
|
-
return 'tester';
|
|
538
|
-
}
|
|
539
|
-
//# sourceMappingURL=coverage-router.js.map
|