ruvector 0.2.23 → 0.2.25
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/bin/cli.js +211 -63
- package/dist/analysis/complexity.d.ts +52 -0
- package/dist/analysis/complexity.d.ts.map +1 -0
- package/dist/analysis/complexity.js +146 -0
- package/dist/analysis/index.d.ts +15 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +38 -0
- package/dist/analysis/patterns.d.ts +71 -0
- package/dist/analysis/patterns.d.ts.map +1 -0
- package/dist/analysis/patterns.js +243 -0
- package/dist/analysis/security.d.ts +51 -0
- package/dist/analysis/security.d.ts.map +1 -0
- package/dist/analysis/security.js +139 -0
- package/dist/core/adaptive-embedder.d.ts +156 -0
- package/dist/core/adaptive-embedder.d.ts.map +1 -0
- package/dist/core/adaptive-embedder.js +838 -0
- package/dist/core/agentdb-fast.d.ts +149 -0
- package/dist/core/agentdb-fast.d.ts.map +1 -0
- package/dist/core/agentdb-fast.js +301 -0
- package/dist/core/ast-parser.d.ts +108 -0
- package/dist/core/ast-parser.d.ts.map +1 -0
- package/dist/core/ast-parser.js +602 -0
- package/dist/core/attention-fallbacks.d.ts +321 -0
- package/dist/core/attention-fallbacks.d.ts.map +1 -0
- package/dist/core/attention-fallbacks.js +552 -0
- package/dist/core/cluster-wrapper.d.ts +148 -0
- package/dist/core/cluster-wrapper.d.ts.map +1 -0
- package/dist/core/cluster-wrapper.js +271 -0
- package/dist/core/coverage-router.d.ts +88 -0
- package/dist/core/coverage-router.d.ts.map +1 -0
- package/dist/core/coverage-router.js +315 -0
- package/dist/core/diff-embeddings.d.ts +93 -0
- package/dist/core/diff-embeddings.d.ts.map +1 -0
- package/dist/core/diff-embeddings.js +334 -0
- package/dist/core/diskann-wrapper.d.ts +53 -0
- package/dist/core/diskann-wrapper.d.ts.map +1 -0
- package/dist/core/diskann-wrapper.js +105 -0
- package/dist/core/gnn-wrapper.d.ts +143 -0
- package/dist/core/gnn-wrapper.d.ts.map +1 -0
- package/dist/core/gnn-wrapper.js +213 -0
- package/dist/core/graph-algorithms.d.ts +83 -0
- package/dist/core/graph-algorithms.d.ts.map +1 -0
- package/dist/core/graph-algorithms.js +514 -0
- package/dist/core/graph-wrapper.d.ts +147 -0
- package/dist/core/graph-wrapper.d.ts.map +1 -0
- package/dist/core/graph-wrapper.js +299 -0
- package/dist/core/index.d.ts +50 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +92 -0
- package/dist/core/intelligence-engine.d.ts +258 -0
- package/dist/core/intelligence-engine.d.ts.map +1 -0
- package/dist/core/intelligence-engine.js +1030 -0
- package/dist/core/learning-engine.d.ts +160 -0
- package/dist/core/learning-engine.d.ts.map +1 -0
- package/dist/core/learning-engine.js +589 -0
- package/dist/core/neural-embeddings.d.ts +393 -0
- package/dist/core/neural-embeddings.d.ts.map +1 -0
- package/dist/core/neural-embeddings.js +1091 -0
- package/dist/core/neural-perf.d.ts +331 -0
- package/dist/core/neural-perf.d.ts.map +1 -0
- package/dist/core/neural-perf.js +704 -0
- package/dist/core/onnx/pkg/package.json +3 -0
- package/dist/core/onnx-embedder.d.ts +105 -0
- package/dist/core/onnx-embedder.d.ts.map +1 -0
- package/dist/core/onnx-embedder.js +410 -0
- package/dist/core/onnx-optimized.d.ts +109 -0
- package/dist/core/onnx-optimized.d.ts.map +1 -0
- package/dist/core/onnx-optimized.js +419 -0
- package/dist/core/parallel-intelligence.d.ts +109 -0
- package/dist/core/parallel-intelligence.d.ts.map +1 -0
- package/dist/core/parallel-intelligence.js +340 -0
- package/dist/core/parallel-workers.d.ts +177 -0
- package/dist/core/parallel-workers.d.ts.map +1 -0
- package/dist/core/parallel-workers.js +783 -0
- package/dist/core/router-wrapper.d.ts +75 -0
- package/dist/core/router-wrapper.d.ts.map +1 -0
- package/dist/core/router-wrapper.js +243 -0
- package/dist/core/rvf-wrapper.d.ts +86 -0
- package/dist/core/rvf-wrapper.d.ts.map +1 -0
- package/dist/core/rvf-wrapper.js +102 -0
- package/dist/core/sona-wrapper.d.ts +226 -0
- package/dist/core/sona-wrapper.d.ts.map +1 -0
- package/dist/core/sona-wrapper.js +282 -0
- package/dist/core/tensor-compress.d.ts +134 -0
- package/dist/core/tensor-compress.d.ts.map +1 -0
- package/dist/core/tensor-compress.js +432 -0
- package/dist/index.d.ts +106 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +258 -0
- package/dist/services/embedding-service.d.ts +136 -0
- package/dist/services/embedding-service.d.ts.map +1 -0
- package/dist/services/embedding-service.js +294 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +26 -0
- package/dist/types.d.ts +145 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/workers/benchmark.d.ts +44 -0
- package/dist/workers/benchmark.d.ts.map +1 -0
- package/dist/workers/benchmark.js +230 -0
- package/dist/workers/index.d.ts +10 -0
- package/dist/workers/index.d.ts.map +1 -0
- package/dist/workers/index.js +25 -0
- package/dist/workers/native-worker.d.ts +76 -0
- package/dist/workers/native-worker.d.ts.map +1 -0
- package/dist/workers/native-worker.js +490 -0
- package/dist/workers/types.d.ts +69 -0
- package/dist/workers/types.d.ts.map +1 -0
- package/dist/workers/types.js +7 -0
- package/package.json +8 -7
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cluster Wrapper - Distributed coordination for multi-agent systems
|
|
4
|
+
*
|
|
5
|
+
* Wraps @ruvector/cluster for Raft consensus, auto-sharding,
|
|
6
|
+
* and distributed memory across agents.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.RuvectorCluster = void 0;
|
|
10
|
+
exports.isClusterAvailable = isClusterAvailable;
|
|
11
|
+
exports.createCluster = createCluster;
|
|
12
|
+
let clusterModule = null;
|
|
13
|
+
let loadError = null;
|
|
14
|
+
function getClusterModule() {
|
|
15
|
+
if (clusterModule)
|
|
16
|
+
return clusterModule;
|
|
17
|
+
if (loadError)
|
|
18
|
+
throw loadError;
|
|
19
|
+
try {
|
|
20
|
+
clusterModule = require('@ruvector/cluster');
|
|
21
|
+
return clusterModule;
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
loadError = new Error(`@ruvector/cluster not installed: ${e.message}\n` +
|
|
25
|
+
`Install with: npm install @ruvector/cluster`);
|
|
26
|
+
throw loadError;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function isClusterAvailable() {
|
|
30
|
+
try {
|
|
31
|
+
getClusterModule();
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Distributed cluster for multi-agent coordination
|
|
40
|
+
*/
|
|
41
|
+
class RuvectorCluster {
|
|
42
|
+
constructor(config) {
|
|
43
|
+
this.isLeader = false;
|
|
44
|
+
const cluster = getClusterModule();
|
|
45
|
+
this.nodeId = config.nodeId;
|
|
46
|
+
this.inner = new cluster.Cluster({
|
|
47
|
+
nodeId: config.nodeId,
|
|
48
|
+
address: config.address,
|
|
49
|
+
peers: config.peers ?? [],
|
|
50
|
+
shards: config.shards ?? 16,
|
|
51
|
+
replicationFactor: config.replicationFactor ?? 2,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// ===========================================================================
|
|
55
|
+
// Cluster Lifecycle
|
|
56
|
+
// ===========================================================================
|
|
57
|
+
/**
|
|
58
|
+
* Start the cluster node
|
|
59
|
+
*/
|
|
60
|
+
async start() {
|
|
61
|
+
await this.inner.start();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Stop the cluster node gracefully
|
|
65
|
+
*/
|
|
66
|
+
async stop() {
|
|
67
|
+
await this.inner.stop();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Join an existing cluster
|
|
71
|
+
*/
|
|
72
|
+
async join(peerAddress) {
|
|
73
|
+
return this.inner.join(peerAddress);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Leave the cluster
|
|
77
|
+
*/
|
|
78
|
+
async leave() {
|
|
79
|
+
await this.inner.leave();
|
|
80
|
+
}
|
|
81
|
+
// ===========================================================================
|
|
82
|
+
// Node Management
|
|
83
|
+
// ===========================================================================
|
|
84
|
+
/**
|
|
85
|
+
* Get current node info
|
|
86
|
+
*/
|
|
87
|
+
getNodeInfo() {
|
|
88
|
+
return this.inner.getNodeInfo();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get all cluster nodes
|
|
92
|
+
*/
|
|
93
|
+
getNodes() {
|
|
94
|
+
return this.inner.getNodes();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if this node is the leader
|
|
98
|
+
*/
|
|
99
|
+
isClusterLeader() {
|
|
100
|
+
this.isLeader = this.inner.isLeader();
|
|
101
|
+
return this.isLeader;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get the current leader
|
|
105
|
+
*/
|
|
106
|
+
getLeader() {
|
|
107
|
+
return this.inner.getLeader();
|
|
108
|
+
}
|
|
109
|
+
// ===========================================================================
|
|
110
|
+
// Distributed Operations
|
|
111
|
+
// ===========================================================================
|
|
112
|
+
/**
|
|
113
|
+
* Put a value in distributed storage
|
|
114
|
+
*/
|
|
115
|
+
async put(key, value) {
|
|
116
|
+
return this.inner.put(key, JSON.stringify(value));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get a value from distributed storage
|
|
120
|
+
*/
|
|
121
|
+
async get(key) {
|
|
122
|
+
const result = await this.inner.get(key);
|
|
123
|
+
return result ? JSON.parse(result) : null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Delete a value from distributed storage
|
|
127
|
+
*/
|
|
128
|
+
async delete(key) {
|
|
129
|
+
return this.inner.delete(key);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Atomic compare-and-swap
|
|
133
|
+
*/
|
|
134
|
+
async compareAndSwap(key, expected, newValue) {
|
|
135
|
+
return this.inner.compareAndSwap(key, JSON.stringify(expected), JSON.stringify(newValue));
|
|
136
|
+
}
|
|
137
|
+
// ===========================================================================
|
|
138
|
+
// Sharding
|
|
139
|
+
// ===========================================================================
|
|
140
|
+
/**
|
|
141
|
+
* Get shard information
|
|
142
|
+
*/
|
|
143
|
+
getShards() {
|
|
144
|
+
return this.inner.getShards();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get the shard for a key
|
|
148
|
+
*/
|
|
149
|
+
getShardForKey(key) {
|
|
150
|
+
return this.inner.getShardForKey(key);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Trigger shard rebalancing
|
|
154
|
+
*/
|
|
155
|
+
async rebalance() {
|
|
156
|
+
await this.inner.rebalance();
|
|
157
|
+
}
|
|
158
|
+
// ===========================================================================
|
|
159
|
+
// Distributed Locks
|
|
160
|
+
// ===========================================================================
|
|
161
|
+
/**
|
|
162
|
+
* Acquire a distributed lock
|
|
163
|
+
*/
|
|
164
|
+
async lock(name, timeout = 30000) {
|
|
165
|
+
return this.inner.lock(name, timeout);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Release a distributed lock
|
|
169
|
+
*/
|
|
170
|
+
async unlock(name, token) {
|
|
171
|
+
return this.inner.unlock(name, token);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Extend a lock's TTL
|
|
175
|
+
*/
|
|
176
|
+
async extendLock(name, token, extension = 30000) {
|
|
177
|
+
return this.inner.extendLock(name, token, extension);
|
|
178
|
+
}
|
|
179
|
+
// ===========================================================================
|
|
180
|
+
// Pub/Sub
|
|
181
|
+
// ===========================================================================
|
|
182
|
+
/**
|
|
183
|
+
* Subscribe to a channel
|
|
184
|
+
*/
|
|
185
|
+
subscribe(channel, callback) {
|
|
186
|
+
return this.inner.subscribe(channel, (msg) => {
|
|
187
|
+
callback(JSON.parse(msg));
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Publish to a channel
|
|
192
|
+
*/
|
|
193
|
+
async publish(channel, message) {
|
|
194
|
+
return this.inner.publish(channel, JSON.stringify(message));
|
|
195
|
+
}
|
|
196
|
+
// ===========================================================================
|
|
197
|
+
// Agent Coordination
|
|
198
|
+
// ===========================================================================
|
|
199
|
+
/**
|
|
200
|
+
* Register an agent with the cluster
|
|
201
|
+
*/
|
|
202
|
+
async registerAgent(agentId, capabilities) {
|
|
203
|
+
return this.put(`agent:${agentId}`, {
|
|
204
|
+
id: agentId,
|
|
205
|
+
capabilities,
|
|
206
|
+
node: this.nodeId,
|
|
207
|
+
registeredAt: Date.now(),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Find agents with a capability
|
|
212
|
+
*/
|
|
213
|
+
async findAgents(capability) {
|
|
214
|
+
const agents = await this.inner.scan('agent:*');
|
|
215
|
+
const matching = [];
|
|
216
|
+
for (const key of agents) {
|
|
217
|
+
const agent = await this.get(key);
|
|
218
|
+
if (agent?.capabilities?.includes(capability)) {
|
|
219
|
+
matching.push(agent.id);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return matching;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Assign a task to an agent
|
|
226
|
+
*/
|
|
227
|
+
async assignTask(taskId, agentId, task) {
|
|
228
|
+
const assigned = await this.put(`task:${taskId}`, {
|
|
229
|
+
id: taskId,
|
|
230
|
+
agent: agentId,
|
|
231
|
+
task,
|
|
232
|
+
status: 'assigned',
|
|
233
|
+
assignedAt: Date.now(),
|
|
234
|
+
});
|
|
235
|
+
if (assigned) {
|
|
236
|
+
await this.publish(`agent:${agentId}:tasks`, { type: 'new_task', taskId });
|
|
237
|
+
}
|
|
238
|
+
return assigned;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Complete a task
|
|
242
|
+
*/
|
|
243
|
+
async completeTask(taskId, result) {
|
|
244
|
+
const task = await this.get(`task:${taskId}`);
|
|
245
|
+
if (!task)
|
|
246
|
+
return false;
|
|
247
|
+
return this.put(`task:${taskId}`, {
|
|
248
|
+
...task,
|
|
249
|
+
status: 'completed',
|
|
250
|
+
result,
|
|
251
|
+
completedAt: Date.now(),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// ===========================================================================
|
|
255
|
+
// Stats
|
|
256
|
+
// ===========================================================================
|
|
257
|
+
/**
|
|
258
|
+
* Get cluster statistics
|
|
259
|
+
*/
|
|
260
|
+
stats() {
|
|
261
|
+
return this.inner.stats();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
exports.RuvectorCluster = RuvectorCluster;
|
|
265
|
+
/**
|
|
266
|
+
* Create a cluster node for agent coordination
|
|
267
|
+
*/
|
|
268
|
+
function createCluster(config) {
|
|
269
|
+
return new RuvectorCluster(config);
|
|
270
|
+
}
|
|
271
|
+
exports.default = RuvectorCluster;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage Router - Test coverage-aware agent routing
|
|
3
|
+
*
|
|
4
|
+
* Uses test coverage data to make smarter routing decisions:
|
|
5
|
+
* - Prioritize testing for uncovered code
|
|
6
|
+
* - Route to tester agent for low-coverage files
|
|
7
|
+
* - Suggest test files for modified code
|
|
8
|
+
*/
|
|
9
|
+
export interface CoverageData {
|
|
10
|
+
file: string;
|
|
11
|
+
lines: {
|
|
12
|
+
total: number;
|
|
13
|
+
covered: number;
|
|
14
|
+
percentage: number;
|
|
15
|
+
};
|
|
16
|
+
functions: {
|
|
17
|
+
total: number;
|
|
18
|
+
covered: number;
|
|
19
|
+
percentage: number;
|
|
20
|
+
};
|
|
21
|
+
branches: {
|
|
22
|
+
total: number;
|
|
23
|
+
covered: number;
|
|
24
|
+
percentage: number;
|
|
25
|
+
};
|
|
26
|
+
uncoveredLines: number[];
|
|
27
|
+
uncoveredFunctions: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface CoverageSummary {
|
|
30
|
+
files: Map<string, CoverageData>;
|
|
31
|
+
overall: {
|
|
32
|
+
lines: number;
|
|
33
|
+
functions: number;
|
|
34
|
+
branches: number;
|
|
35
|
+
};
|
|
36
|
+
lowCoverageFiles: string[];
|
|
37
|
+
uncoveredFiles: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface TestSuggestion {
|
|
40
|
+
file: string;
|
|
41
|
+
testFile: string;
|
|
42
|
+
reason: string;
|
|
43
|
+
priority: 'high' | 'medium' | 'low';
|
|
44
|
+
coverage: number;
|
|
45
|
+
uncoveredFunctions: string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Parse Istanbul/NYC JSON coverage report
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseIstanbulCoverage(coveragePath: string): CoverageSummary;
|
|
51
|
+
/**
|
|
52
|
+
* Find coverage report in project
|
|
53
|
+
*/
|
|
54
|
+
export declare function findCoverageReport(projectRoot?: string): string | null;
|
|
55
|
+
/**
|
|
56
|
+
* Get coverage data for a specific file
|
|
57
|
+
*/
|
|
58
|
+
export declare function getFileCoverage(file: string, summary?: CoverageSummary): CoverageData | null;
|
|
59
|
+
/**
|
|
60
|
+
* Suggest tests for files based on coverage
|
|
61
|
+
*/
|
|
62
|
+
export declare function suggestTests(files: string[], summary?: CoverageSummary): TestSuggestion[];
|
|
63
|
+
/**
|
|
64
|
+
* Determine if a file needs the tester agent based on coverage
|
|
65
|
+
*/
|
|
66
|
+
export declare function shouldRouteToTester(file: string, summary?: CoverageSummary): {
|
|
67
|
+
route: boolean;
|
|
68
|
+
reason: string;
|
|
69
|
+
coverage: number;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Get coverage-aware routing weight for agent selection
|
|
73
|
+
*/
|
|
74
|
+
export declare function getCoverageRoutingWeight(file: string, summary?: CoverageSummary): {
|
|
75
|
+
coder: number;
|
|
76
|
+
tester: number;
|
|
77
|
+
reviewer: number;
|
|
78
|
+
};
|
|
79
|
+
declare const _default: {
|
|
80
|
+
parseIstanbulCoverage: typeof parseIstanbulCoverage;
|
|
81
|
+
findCoverageReport: typeof findCoverageReport;
|
|
82
|
+
getFileCoverage: typeof getFileCoverage;
|
|
83
|
+
suggestTests: typeof suggestTests;
|
|
84
|
+
shouldRouteToTester: typeof shouldRouteToTester;
|
|
85
|
+
getCoverageRoutingWeight: typeof getCoverageRoutingWeight;
|
|
86
|
+
};
|
|
87
|
+
export default _default;
|
|
88
|
+
//# sourceMappingURL=coverage-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage-router.d.ts","sourceRoot":"","sources":["../../src/core/coverage-router.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,SAAS,EAAE;QACT,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,QAAQ,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,CA0F3E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAiBrF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,YAAY,GAAG,IAAI,CAqB5F;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,cAAc,EAAE,CAwEzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAgCA;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAuBA;;;;;;;;;AAED,wBAOE"}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Coverage Router - Test coverage-aware agent routing
|
|
4
|
+
*
|
|
5
|
+
* Uses test coverage data to make smarter routing decisions:
|
|
6
|
+
* - Prioritize testing for uncovered code
|
|
7
|
+
* - Route to tester agent for low-coverage files
|
|
8
|
+
* - Suggest test files for modified code
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.parseIstanbulCoverage = parseIstanbulCoverage;
|
|
45
|
+
exports.findCoverageReport = findCoverageReport;
|
|
46
|
+
exports.getFileCoverage = getFileCoverage;
|
|
47
|
+
exports.suggestTests = suggestTests;
|
|
48
|
+
exports.shouldRouteToTester = shouldRouteToTester;
|
|
49
|
+
exports.getCoverageRoutingWeight = getCoverageRoutingWeight;
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
/**
|
|
53
|
+
* Parse Istanbul/NYC JSON coverage report
|
|
54
|
+
*/
|
|
55
|
+
function parseIstanbulCoverage(coveragePath) {
|
|
56
|
+
const files = new Map();
|
|
57
|
+
const lowCoverageFiles = [];
|
|
58
|
+
const uncoveredFiles = [];
|
|
59
|
+
let totalLines = 0, coveredLines = 0;
|
|
60
|
+
let totalFunctions = 0, coveredFunctions = 0;
|
|
61
|
+
let totalBranches = 0, coveredBranches = 0;
|
|
62
|
+
try {
|
|
63
|
+
const coverage = JSON.parse(fs.readFileSync(coveragePath, 'utf8'));
|
|
64
|
+
for (const [file, data] of Object.entries(coverage)) {
|
|
65
|
+
// Skip test files
|
|
66
|
+
if (file.includes('.test.') || file.includes('.spec.') || file.includes('__tests__')) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// Parse statement coverage
|
|
70
|
+
const statements = Object.values(data.s || {});
|
|
71
|
+
const linesCovered = statements.filter(n => n > 0).length;
|
|
72
|
+
const linesTotal = statements.length;
|
|
73
|
+
// Parse function coverage
|
|
74
|
+
const functions = Object.values(data.f || {});
|
|
75
|
+
const fnCovered = functions.filter(n => n > 0).length;
|
|
76
|
+
const fnTotal = functions.length;
|
|
77
|
+
// Parse branch coverage
|
|
78
|
+
const branches = Object.values(data.b || {}).flat();
|
|
79
|
+
const brCovered = branches.filter(n => n > 0).length;
|
|
80
|
+
const brTotal = branches.length;
|
|
81
|
+
// Find uncovered lines
|
|
82
|
+
const uncoveredLines = [];
|
|
83
|
+
for (const [line, count] of Object.entries(data.s || {})) {
|
|
84
|
+
if (count === 0) {
|
|
85
|
+
uncoveredLines.push(parseInt(line));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Find uncovered functions
|
|
89
|
+
const uncoveredFunctions = [];
|
|
90
|
+
const fnMap = data.fnMap || {};
|
|
91
|
+
for (const [fnId, count] of Object.entries(data.f || {})) {
|
|
92
|
+
if (count === 0 && fnMap[fnId]) {
|
|
93
|
+
uncoveredFunctions.push(fnMap[fnId].name || `function_${fnId}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const linePercentage = linesTotal > 0 ? (linesCovered / linesTotal) * 100 : 100;
|
|
97
|
+
const fnPercentage = fnTotal > 0 ? (fnCovered / fnTotal) * 100 : 100;
|
|
98
|
+
const brPercentage = brTotal > 0 ? (brCovered / brTotal) * 100 : 100;
|
|
99
|
+
files.set(file, {
|
|
100
|
+
file,
|
|
101
|
+
lines: { total: linesTotal, covered: linesCovered, percentage: linePercentage },
|
|
102
|
+
functions: { total: fnTotal, covered: fnCovered, percentage: fnPercentage },
|
|
103
|
+
branches: { total: brTotal, covered: brCovered, percentage: brPercentage },
|
|
104
|
+
uncoveredLines,
|
|
105
|
+
uncoveredFunctions,
|
|
106
|
+
});
|
|
107
|
+
totalLines += linesTotal;
|
|
108
|
+
coveredLines += linesCovered;
|
|
109
|
+
totalFunctions += fnTotal;
|
|
110
|
+
coveredFunctions += fnCovered;
|
|
111
|
+
totalBranches += brTotal;
|
|
112
|
+
coveredBranches += brCovered;
|
|
113
|
+
if (linePercentage < 50) {
|
|
114
|
+
lowCoverageFiles.push(file);
|
|
115
|
+
}
|
|
116
|
+
if (linePercentage === 0 && linesTotal > 0) {
|
|
117
|
+
uncoveredFiles.push(file);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (e) {
|
|
122
|
+
// Return empty summary on error
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
files,
|
|
126
|
+
overall: {
|
|
127
|
+
lines: totalLines > 0 ? (coveredLines / totalLines) * 100 : 0,
|
|
128
|
+
functions: totalFunctions > 0 ? (coveredFunctions / totalFunctions) * 100 : 0,
|
|
129
|
+
branches: totalBranches > 0 ? (coveredBranches / totalBranches) * 100 : 0,
|
|
130
|
+
},
|
|
131
|
+
lowCoverageFiles,
|
|
132
|
+
uncoveredFiles,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Find coverage report in project
|
|
137
|
+
*/
|
|
138
|
+
function findCoverageReport(projectRoot = process.cwd()) {
|
|
139
|
+
const possiblePaths = [
|
|
140
|
+
'coverage/coverage-final.json',
|
|
141
|
+
'coverage/coverage-summary.json',
|
|
142
|
+
'.nyc_output/coverage.json',
|
|
143
|
+
'coverage.json',
|
|
144
|
+
'coverage/lcov.info',
|
|
145
|
+
];
|
|
146
|
+
for (const p of possiblePaths) {
|
|
147
|
+
const fullPath = path.join(projectRoot, p);
|
|
148
|
+
if (fs.existsSync(fullPath)) {
|
|
149
|
+
return fullPath;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get coverage data for a specific file
|
|
156
|
+
*/
|
|
157
|
+
function getFileCoverage(file, summary) {
|
|
158
|
+
if (!summary) {
|
|
159
|
+
const reportPath = findCoverageReport();
|
|
160
|
+
if (!reportPath)
|
|
161
|
+
return null;
|
|
162
|
+
summary = parseIstanbulCoverage(reportPath);
|
|
163
|
+
}
|
|
164
|
+
// Try exact match first
|
|
165
|
+
if (summary.files.has(file)) {
|
|
166
|
+
return summary.files.get(file);
|
|
167
|
+
}
|
|
168
|
+
// Try matching by basename
|
|
169
|
+
const basename = path.basename(file);
|
|
170
|
+
for (const [key, data] of summary.files) {
|
|
171
|
+
if (key.endsWith(file) || key.endsWith(basename)) {
|
|
172
|
+
return data;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Suggest tests for files based on coverage
|
|
179
|
+
*/
|
|
180
|
+
function suggestTests(files, summary) {
|
|
181
|
+
if (!summary) {
|
|
182
|
+
const reportPath = findCoverageReport();
|
|
183
|
+
if (reportPath) {
|
|
184
|
+
summary = parseIstanbulCoverage(reportPath);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const suggestions = [];
|
|
188
|
+
for (const file of files) {
|
|
189
|
+
const coverage = summary ? getFileCoverage(file, summary) : null;
|
|
190
|
+
// Determine test file path
|
|
191
|
+
const ext = path.extname(file);
|
|
192
|
+
const base = path.basename(file, ext);
|
|
193
|
+
const dir = path.dirname(file);
|
|
194
|
+
const possibleTestFiles = [
|
|
195
|
+
path.join(dir, `${base}.test${ext}`),
|
|
196
|
+
path.join(dir, `${base}.spec${ext}`),
|
|
197
|
+
path.join(dir, '__tests__', `${base}.test${ext}`),
|
|
198
|
+
path.join('test', `${base}.test${ext}`),
|
|
199
|
+
path.join('tests', `${base}.test${ext}`),
|
|
200
|
+
];
|
|
201
|
+
const existingTestFile = possibleTestFiles.find(t => fs.existsSync(t));
|
|
202
|
+
const testFile = existingTestFile || possibleTestFiles[0];
|
|
203
|
+
if (!coverage) {
|
|
204
|
+
suggestions.push({
|
|
205
|
+
file,
|
|
206
|
+
testFile,
|
|
207
|
+
reason: 'No coverage data - needs test file',
|
|
208
|
+
priority: 'high',
|
|
209
|
+
coverage: 0,
|
|
210
|
+
uncoveredFunctions: [],
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
else if (coverage.lines.percentage < 30) {
|
|
214
|
+
suggestions.push({
|
|
215
|
+
file,
|
|
216
|
+
testFile,
|
|
217
|
+
reason: `Very low coverage (${coverage.lines.percentage.toFixed(1)}%)`,
|
|
218
|
+
priority: 'high',
|
|
219
|
+
coverage: coverage.lines.percentage,
|
|
220
|
+
uncoveredFunctions: coverage.uncoveredFunctions,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
else if (coverage.lines.percentage < 70) {
|
|
224
|
+
suggestions.push({
|
|
225
|
+
file,
|
|
226
|
+
testFile,
|
|
227
|
+
reason: `Low coverage (${coverage.lines.percentage.toFixed(1)}%)`,
|
|
228
|
+
priority: 'medium',
|
|
229
|
+
coverage: coverage.lines.percentage,
|
|
230
|
+
uncoveredFunctions: coverage.uncoveredFunctions,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
else if (coverage.uncoveredFunctions.length > 0) {
|
|
234
|
+
suggestions.push({
|
|
235
|
+
file,
|
|
236
|
+
testFile,
|
|
237
|
+
reason: `${coverage.uncoveredFunctions.length} untested functions`,
|
|
238
|
+
priority: 'low',
|
|
239
|
+
coverage: coverage.lines.percentage,
|
|
240
|
+
uncoveredFunctions: coverage.uncoveredFunctions,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return suggestions.sort((a, b) => {
|
|
245
|
+
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
246
|
+
return priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Determine if a file needs the tester agent based on coverage
|
|
251
|
+
*/
|
|
252
|
+
function shouldRouteToTester(file, summary) {
|
|
253
|
+
const coverage = getFileCoverage(file, summary);
|
|
254
|
+
if (!coverage) {
|
|
255
|
+
return {
|
|
256
|
+
route: true,
|
|
257
|
+
reason: 'No test coverage data available',
|
|
258
|
+
coverage: 0,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (coverage.lines.percentage < 50) {
|
|
262
|
+
return {
|
|
263
|
+
route: true,
|
|
264
|
+
reason: `Low coverage: ${coverage.lines.percentage.toFixed(1)}%`,
|
|
265
|
+
coverage: coverage.lines.percentage,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
if (coverage.uncoveredFunctions.length > 3) {
|
|
269
|
+
return {
|
|
270
|
+
route: true,
|
|
271
|
+
reason: `${coverage.uncoveredFunctions.length} untested functions`,
|
|
272
|
+
coverage: coverage.lines.percentage,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
route: false,
|
|
277
|
+
reason: `Adequate coverage: ${coverage.lines.percentage.toFixed(1)}%`,
|
|
278
|
+
coverage: coverage.lines.percentage,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Get coverage-aware routing weight for agent selection
|
|
283
|
+
*/
|
|
284
|
+
function getCoverageRoutingWeight(file, summary) {
|
|
285
|
+
const coverage = getFileCoverage(file, summary);
|
|
286
|
+
if (!coverage) {
|
|
287
|
+
// No coverage = prioritize testing
|
|
288
|
+
return { coder: 0.3, tester: 0.5, reviewer: 0.2 };
|
|
289
|
+
}
|
|
290
|
+
const pct = coverage.lines.percentage;
|
|
291
|
+
if (pct < 30) {
|
|
292
|
+
// Very low - strongly prioritize testing
|
|
293
|
+
return { coder: 0.2, tester: 0.6, reviewer: 0.2 };
|
|
294
|
+
}
|
|
295
|
+
else if (pct < 60) {
|
|
296
|
+
// Low - moderate testing priority
|
|
297
|
+
return { coder: 0.4, tester: 0.4, reviewer: 0.2 };
|
|
298
|
+
}
|
|
299
|
+
else if (pct < 80) {
|
|
300
|
+
// Okay - balanced
|
|
301
|
+
return { coder: 0.5, tester: 0.3, reviewer: 0.2 };
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// Good - focus on code quality
|
|
305
|
+
return { coder: 0.5, tester: 0.2, reviewer: 0.3 };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
exports.default = {
|
|
309
|
+
parseIstanbulCoverage,
|
|
310
|
+
findCoverageReport,
|
|
311
|
+
getFileCoverage,
|
|
312
|
+
suggestTests,
|
|
313
|
+
shouldRouteToTester,
|
|
314
|
+
getCoverageRoutingWeight,
|
|
315
|
+
};
|