@claude-flow/cli 3.71.4 → 3.72.1
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/helpers/helpers.manifest.json +4 -4
- package/.claude/helpers/hook-handler.cjs +23 -10
- package/.claude/helpers/statusline.cjs +29 -14
- package/.claude/skills/fast-code-edits/SKILL.md +45 -0
- package/.claude/skills/graph-mincut/SKILL.md +32 -0
- package/.claude/skills/injection-defense/SKILL.md +36 -0
- package/.claude/skills/lora-learning/SKILL.md +30 -0
- package/.claude/skills/sublinear-solver/SKILL.md +34 -0
- package/.claude/skills/vector-quantization/SKILL.md +36 -0
- package/README.md +18 -3
- package/bin/cli.js +13 -18
- package/bin/mcp-server.js +13 -18
- package/catalog-manifest.json +4 -4
- package/dist/src/appliance/ruvllm-bridge.d.ts +6 -2
- package/dist/src/appliance/ruvllm-bridge.js +13 -8
- package/dist/src/appliance/rvfa-distribution.js +2 -2
- package/dist/src/benchmarks/gaia-critic.d.ts +1 -7
- package/dist/src/benchmarks/gaia-critic.js +1 -7
- package/dist/src/benchmarks/gaia-decomposer.d.ts +1 -8
- package/dist/src/benchmarks/gaia-decomposer.js +1 -8
- package/dist/src/commands/agent-wasm.js +2 -2
- package/dist/src/commands/analyze.js +1 -1
- package/dist/src/commands/daemon.js +1 -1
- package/dist/src/commands/doctor.js +3 -2
- package/dist/src/commands/guidance.js +9 -9
- package/dist/src/commands/hooks.js +13 -8
- package/dist/src/commands/init.js +16 -16
- package/dist/src/commands/neural.js +87 -77
- package/dist/src/commands/route.js +1 -1
- package/dist/src/commands/ruvector/backup.js +7 -5
- package/dist/src/commands/ruvector/benchmark.js +6 -1
- package/dist/src/commands/ruvector/import.js +19 -5
- package/dist/src/commands/ruvector/init.js +1 -1
- package/dist/src/commands/start.js +1 -1
- package/dist/src/commands/status.js +1 -1
- package/dist/src/commands/task.js +18 -1
- package/dist/src/commands/update.js +1 -1
- package/dist/src/funnel/local-signals.js +7 -2
- package/dist/src/init/executor.js +13 -0
- package/dist/src/init/types.d.ts +6 -0
- package/dist/src/init/types.js +3 -0
- package/dist/src/mcp-client.js +2 -0
- package/dist/src/mcp-protocol.d.ts +90 -0
- package/dist/src/mcp-protocol.js +179 -0
- package/dist/src/mcp-server.d.ts +1 -0
- package/dist/src/mcp-server.js +20 -22
- package/dist/src/mcp-tools/agent-execute-core.js +9 -1
- package/dist/src/mcp-tools/agent-tools.d.ts +13 -0
- package/dist/src/mcp-tools/agent-tools.js +59 -8
- package/dist/src/mcp-tools/agentdb-tools.js +34 -2
- package/dist/src/mcp-tools/browser-argv.d.ts +9 -0
- package/dist/src/mcp-tools/browser-argv.js +33 -0
- package/dist/src/mcp-tools/browser-session-tools.js +4 -0
- package/dist/src/mcp-tools/browser-tools.js +12 -2
- package/dist/src/mcp-tools/coordination-tools.js +5 -4
- package/dist/src/mcp-tools/embeddings-tools.js +12 -4
- package/dist/src/mcp-tools/fast-apply-tools.d.ts +4 -0
- package/dist/src/mcp-tools/fast-apply-tools.js +85 -0
- package/dist/src/mcp-tools/glob-exclusions.d.ts +6 -0
- package/dist/src/mcp-tools/glob-exclusions.js +81 -0
- package/dist/src/mcp-tools/hive-mind-tools.js +104 -35
- package/dist/src/mcp-tools/hooks-tools.js +14 -38
- package/dist/src/mcp-tools/import-limits.d.ts +4 -0
- package/dist/src/mcp-tools/import-limits.js +28 -0
- package/dist/src/mcp-tools/memory-tools.js +21 -42
- package/dist/src/mcp-tools/metaharness-tools.js +1 -1
- package/dist/src/mcp-tools/performance-tools.js +7 -2
- package/dist/src/mcp-tools/realpath-containment.d.ts +3 -0
- package/dist/src/mcp-tools/realpath-containment.js +39 -0
- package/dist/src/mcp-tools/record-store.d.ts +8 -0
- package/dist/src/mcp-tools/record-store.js +98 -1
- package/dist/src/mcp-tools/security-tools.d.ts +9 -0
- package/dist/src/mcp-tools/security-tools.js +39 -3
- package/dist/src/mcp-tools/session-tools.js +5 -3
- package/dist/src/mcp-tools/swarm-tools.js +35 -17
- package/dist/src/mcp-tools/task-tools.js +68 -5
- package/dist/src/mcp-tools/terminal-tools.js +9 -0
- package/dist/src/mcp-tools/workflow-tools.js +13 -12
- package/dist/src/mcp-tools/x-federation-join.js +55 -14
- package/dist/src/memory/intelligence.d.ts +11 -0
- package/dist/src/memory/intelligence.js +84 -6
- package/dist/src/memory/memory-initializer.d.ts +6 -2
- package/dist/src/memory/memory-initializer.js +15 -6
- package/dist/src/mods/command-registry/schema.d.ts +12 -12
- package/dist/src/ruvector/ast-analyzer.d.ts +1 -2
- package/dist/src/ruvector/ast-analyzer.js +4 -12
- package/dist/src/ruvector/coverage-router.d.ts +1 -2
- package/dist/src/ruvector/coverage-router.js +4 -12
- package/dist/src/ruvector/diff-classifier.d.ts +1 -2
- package/dist/src/ruvector/diff-classifier.js +3 -12
- package/dist/src/ruvector/fast-apply.d.ts +46 -0
- package/dist/src/ruvector/fast-apply.js +73 -0
- package/dist/src/ruvector/graph-analyzer.d.ts +11 -1
- package/dist/src/ruvector/graph-analyzer.js +22 -36
- package/dist/src/ruvector/mincut-bridge.d.ts +70 -0
- package/dist/src/ruvector/mincut-bridge.js +172 -0
- package/dist/src/ruvector/model-router.d.ts +6 -1
- package/dist/src/ruvector/model-router.js +16 -4
- package/dist/src/services/distill-oracle.d.ts +1 -1
- package/dist/src/services/distill-oracle.js +2 -2
- package/dist/src/services/worker-daemon.js +30 -12
- package/dist/src/transfer/storage/gcs.js +3 -2
- package/node_modules/@claude-flow/codex/.agents/skills/fast-code-edits/SKILL.md +42 -0
- package/node_modules/@claude-flow/codex/.agents/skills/graph-mincut/SKILL.md +42 -0
- package/node_modules/@claude-flow/codex/.agents/skills/injection-defense/SKILL.md +45 -0
- package/node_modules/@claude-flow/codex/.agents/skills/lora-learning/SKILL.md +39 -0
- package/node_modules/@claude-flow/codex/.agents/skills/sublinear-solver/SKILL.md +38 -0
- package/node_modules/@claude-flow/codex/.agents/skills/vector-quantization/SKILL.md +49 -0
- package/node_modules/@claude-flow/codex/dist/generators/skill-md.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/dist/generators/skill-md.js +6 -0
- package/node_modules/@claude-flow/codex/dist/generators/skill-md.js.map +1 -1
- package/node_modules/@claude-flow/codex/dist/index.d.ts +3 -3
- package/node_modules/@claude-flow/codex/dist/index.js +1 -1
- package/node_modules/@claude-flow/codex/dist/templates/index.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/dist/templates/index.js +37 -0
- package/node_modules/@claude-flow/codex/dist/templates/index.js.map +1 -1
- package/node_modules/@claude-flow/codex/dist/types.d.ts +1 -1
- package/node_modules/@claude-flow/codex/dist/types.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/package.json +1 -1
- package/package.json +11 -9
- package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +2 -1
- package/plugins/ruflo-metaharness/scripts/_darwin.mjs +4 -4
- package/plugins/ruflo-metaharness/scripts/_harness.mjs +1 -1
- package/plugins/ruflo-metaharness/scripts/smoke.sh +4 -4
|
@@ -1,14 +1,53 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
import { join, dirname } from 'node:path';
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
import { join, dirname, isAbsolute, relative, resolve } from 'node:path';
|
|
5
|
+
// Host configuration precedes defaults. Caller key overrides stay under ~/.ruflo;
|
|
6
|
+
// a caller relayWs must match the configured relay. relayHttp keeps ADR-125 precedence.
|
|
7
7
|
const HTTP_BASE = (o) => (o || process.env.RUFLO_X_RELAY_HTTP || 'https://relay.ruv.io').replace(/\/$/, '');
|
|
8
8
|
const RELAY_WS = (o) => o || process.env.RUFLO_X_RELAY_WS || 'wss://relay.ruv.io';
|
|
9
9
|
const KEY_FILE = () => process.env.RUFLO_NOSTR_KEY_FILE || join(homedir(), '.ruflo', 'nostr.key');
|
|
10
10
|
const hex = (b) => Buffer.from(b).toString('hex');
|
|
11
11
|
const unhex = (h) => Uint8Array.from(Buffer.from(h, 'hex'));
|
|
12
|
+
function callerKeyFile(value) {
|
|
13
|
+
// Host configuration is trusted; an MCP argument cannot select a different host file.
|
|
14
|
+
if (value === undefined)
|
|
15
|
+
return KEY_FILE();
|
|
16
|
+
if (typeof value !== 'string' || !value)
|
|
17
|
+
throw new Error('keyFile must name a file under ~/.ruflo');
|
|
18
|
+
const root = resolve(homedir(), '.ruflo');
|
|
19
|
+
const target = resolve(value);
|
|
20
|
+
const rel = relative(root, target);
|
|
21
|
+
if (!rel || rel === '..' || rel.startsWith('../') || rel.startsWith('..\\') || isAbsolute(rel)) {
|
|
22
|
+
throw new Error('keyFile must name a file under ~/.ruflo');
|
|
23
|
+
}
|
|
24
|
+
for (let current = target;; current = dirname(current)) {
|
|
25
|
+
try {
|
|
26
|
+
if (lstatSync(current).isSymbolicLink())
|
|
27
|
+
throw new Error('keyFile must not follow symlinks');
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (error.code !== 'ENOENT')
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
if (relative(root, current) === '')
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
return target;
|
|
37
|
+
}
|
|
38
|
+
function configuredRelay(value) {
|
|
39
|
+
const configured = new URL(RELAY_WS());
|
|
40
|
+
if (value !== undefined && typeof value !== 'string')
|
|
41
|
+
throw new Error('relayWs must be the configured relay URL');
|
|
42
|
+
const requested = new URL(value === undefined ? configured.href : value);
|
|
43
|
+
const localTestRelay = Boolean(process.env.RUFLO_X_RELAY_WS)
|
|
44
|
+
&& configured.protocol === 'ws:' && ['localhost', '127.0.0.1', '[::1]'].includes(configured.hostname);
|
|
45
|
+
if ((!localTestRelay && configured.protocol !== 'wss:') || requested.href !== configured.href
|
|
46
|
+
|| requested.username || requested.password || requested.hash) {
|
|
47
|
+
throw new Error('relayWs must use wss and match the configured relay (explicit loopback ws configuration is allowed)');
|
|
48
|
+
}
|
|
49
|
+
return requested.href;
|
|
50
|
+
}
|
|
12
51
|
async function loadNostrTools() {
|
|
13
52
|
try {
|
|
14
53
|
return (await import('nostr-tools/pure'));
|
|
@@ -24,7 +63,7 @@ export function loadOrCreateKey(nt, file = KEY_FILE()) {
|
|
|
24
63
|
}
|
|
25
64
|
mkdirSync(dirname(file), { recursive: true, mode: 0o700 });
|
|
26
65
|
const sk = nt.generateSecretKey();
|
|
27
|
-
writeFileSync(file, hex(sk), { mode: 0o600 });
|
|
66
|
+
writeFileSync(file, hex(sk), { mode: 0o600, flag: 'wx' });
|
|
28
67
|
return { sk, pubkey: nt.getPublicKey(sk), created: true };
|
|
29
68
|
}
|
|
30
69
|
export function nip98Header(nt, sk, url, method, body) {
|
|
@@ -80,24 +119,26 @@ export const xFederationJoinTools = [{
|
|
|
80
119
|
code: { type: 'string', description: 'Optional private invite code (v2.…), for invite-only relays.' },
|
|
81
120
|
gatewayUrl: { type: 'string', description: 'Public registration gateway origin; defaults to https://x.ruv.io.' },
|
|
82
121
|
relayHttp: { type: 'string', description: 'Relay HTTPS base for the claim; takes precedence over RUFLO_X_RELAY_HTTP.' },
|
|
83
|
-
relayWs: { type: 'string', description: 'Relay wss URL for NIP-42;
|
|
84
|
-
keyFile: { type: 'string', description: '
|
|
122
|
+
relayWs: { type: 'string', description: 'Relay wss URL for NIP-42; must match RUFLO_X_RELAY_WS or the default relay.' },
|
|
123
|
+
keyFile: { type: 'string', description: 'Caller-selected file under ~/.ruflo (no symlinks). Omit to use host RUFLO_NOSTR_KEY_FILE or ~/.ruflo/nostr.key.' }
|
|
85
124
|
}, required: [] },
|
|
86
125
|
handler: async (input) => {
|
|
87
126
|
const i = input;
|
|
88
|
-
const nt = await loadNostrTools();
|
|
89
127
|
// Validate input before the optional-dependency check so a bad code fails fast and identically
|
|
90
128
|
// whether or not nostr-tools is present.
|
|
91
129
|
if (i.code !== undefined && !/^v2\.[A-Za-z0-9._-]{8,}$/.test(i.code))
|
|
92
130
|
throw new Error('invite code must look like v2.<token>');
|
|
131
|
+
const registrationBase = i.code ? undefined : secureBase(i.gatewayUrl || process.env.RUFLO_X_GATEWAY_URL || 'https://x.ruv.io');
|
|
132
|
+
const keyFile = callerKeyFile(i.keyFile);
|
|
133
|
+
const relayWs = configuredRelay(i.relayWs);
|
|
134
|
+
const nt = await loadNostrTools();
|
|
93
135
|
if (!nt)
|
|
94
136
|
return { degraded: true, reason: 'nostr-tools not installed', hint: 'npm i -g nostr-tools (secp256k1 signing is not in node:crypto)' };
|
|
95
|
-
const
|
|
96
|
-
const { sk, pubkey, created } = loadOrCreateKey(nt, i.keyFile);
|
|
137
|
+
const { sk, pubkey, created } = loadOrCreateKey(nt, keyFile);
|
|
97
138
|
// Existing members need no new grant. In particular never downgrade/re-admit
|
|
98
139
|
// their key just because they run join again.
|
|
99
140
|
if (!i.code) {
|
|
100
|
-
const existing = await verifyMembership(nt, sk,
|
|
141
|
+
const existing = await verifyMembership(nt, sk, relayWs);
|
|
101
142
|
if (existing.ok)
|
|
102
143
|
return { ok: true, pubkey, keyCreated: created, membershipVerified: true, alreadyMember: true };
|
|
103
144
|
}
|
|
@@ -108,7 +149,7 @@ export const xFederationJoinTools = [{
|
|
|
108
149
|
r = await fetch(url, { method: 'POST', redirect: 'error', headers: { Authorization: nip98Header(nt, sk, url, 'POST', body), 'Content-Type': 'application/json' }, body, signal: AbortSignal.timeout(20_000) });
|
|
109
150
|
}
|
|
110
151
|
catch {
|
|
111
|
-
const check = await verifyMembership(nt, sk,
|
|
152
|
+
const check = await verifyMembership(nt, sk, relayWs);
|
|
112
153
|
if (check.ok)
|
|
113
154
|
return { ok: true, pubkey, keyCreated: created, membershipVerified: true };
|
|
114
155
|
throw new Error('Admission response unavailable and relay membership unverified. Keep your local key and retry; do not create another identity.');
|
|
@@ -117,12 +158,12 @@ export const xFederationJoinTools = [{
|
|
|
117
158
|
if (!r.ok) {
|
|
118
159
|
// A lost acknowledgment can follow a successful grant. Check with the
|
|
119
160
|
// relay before declaring failure; never silently generate a second key.
|
|
120
|
-
const check = await verifyMembership(nt, sk,
|
|
161
|
+
const check = await verifyMembership(nt, sk, relayWs);
|
|
121
162
|
if (check.ok)
|
|
122
163
|
return { ok: true, pubkey, keyCreated: created, membershipVerified: true };
|
|
123
164
|
throw new Error(`join rejected (${r.status}): ${claim.error ?? claim.message ?? 'unknown'}`);
|
|
124
165
|
}
|
|
125
|
-
const auth = await verifyMembership(nt, sk,
|
|
166
|
+
const auth = await verifyMembership(nt, sk, relayWs);
|
|
126
167
|
return { ok: auth.ok, pubkey, keyCreated: created, role: claim.role ?? 'member', membershipVerified: auth.ok, ...(auth.ok ? {} : { reason: auth.reason }),
|
|
127
168
|
next: 'Publish kind-1 events tagged ["t","ruflo-swarm"] — or run `ruflo federation sync` to read the swarm.' };
|
|
128
169
|
},
|
|
@@ -152,6 +152,12 @@ declare class LocalReasoningBank {
|
|
|
152
152
|
private persistenceEnabled;
|
|
153
153
|
private dirty;
|
|
154
154
|
private saveTimeout;
|
|
155
|
+
/** IDs changed locally since the last successful flush. */
|
|
156
|
+
private dirtyIds;
|
|
157
|
+
/** IDs deleted locally; a disk copy must not be resurrected. */
|
|
158
|
+
private deletedIds;
|
|
159
|
+
/** A local clear() must remove disk entries too. */
|
|
160
|
+
private clearRequested;
|
|
155
161
|
constructor(options: {
|
|
156
162
|
maxSize: number;
|
|
157
163
|
persistence?: boolean;
|
|
@@ -168,6 +174,11 @@ declare class LocalReasoningBank {
|
|
|
168
174
|
private saveToDisk;
|
|
169
175
|
/**
|
|
170
176
|
* Immediately flush patterns to disk
|
|
177
|
+
*
|
|
178
|
+
* Re-reads the file under the cooperating-writer lock and merges only the
|
|
179
|
+
* patterns this process changed, so a long-lived daemon cannot erase patterns
|
|
180
|
+
* written by another hook/process (#4735). Deletions and clear() are carried
|
|
181
|
+
* as tombstones; the write itself is temp-file + rename.
|
|
171
182
|
*/
|
|
172
183
|
flushToDisk(): void;
|
|
173
184
|
/**
|
|
@@ -337,6 +337,12 @@ class LocalReasoningBank {
|
|
|
337
337
|
persistenceEnabled;
|
|
338
338
|
dirty = false;
|
|
339
339
|
saveTimeout = null;
|
|
340
|
+
/** IDs changed locally since the last successful flush. */
|
|
341
|
+
dirtyIds = new Set();
|
|
342
|
+
/** IDs deleted locally; a disk copy must not be resurrected. */
|
|
343
|
+
deletedIds = new Set();
|
|
344
|
+
/** A local clear() must remove disk entries too. */
|
|
345
|
+
clearRequested = false;
|
|
340
346
|
constructor(options) {
|
|
341
347
|
this.maxSize = options.maxSize;
|
|
342
348
|
this.persistenceEnabled = options.persistence !== false;
|
|
@@ -359,6 +365,7 @@ class LocalReasoningBank {
|
|
|
359
365
|
const totalLoaded = data.length;
|
|
360
366
|
// Group by content to deduplicate
|
|
361
367
|
const byContent = new Map();
|
|
368
|
+
const removedIds = [];
|
|
362
369
|
for (const pattern of data) {
|
|
363
370
|
const key = pattern.content;
|
|
364
371
|
const existing = byContent.get(key);
|
|
@@ -372,10 +379,12 @@ class LocalReasoningBank {
|
|
|
372
379
|
(pattern.lastUsedAt ?? 0) > (existing.lastUsedAt ?? 0))) {
|
|
373
380
|
// Merge: adopt the higher usageCount sum
|
|
374
381
|
pattern.usageCount = (pattern.usageCount ?? 0) + (existing.usageCount ?? 0);
|
|
382
|
+
removedIds.push(existing.id);
|
|
375
383
|
byContent.set(key, pattern);
|
|
376
384
|
}
|
|
377
385
|
else {
|
|
378
386
|
existing.usageCount = (existing.usageCount ?? 0) + (pattern.usageCount ?? 0);
|
|
387
|
+
removedIds.push(pattern.id);
|
|
379
388
|
}
|
|
380
389
|
}
|
|
381
390
|
}
|
|
@@ -387,15 +396,28 @@ class LocalReasoningBank {
|
|
|
387
396
|
const removed = totalLoaded - byContent.size;
|
|
388
397
|
if (removed > 0) {
|
|
389
398
|
console.log(`Deduplicated ${removed} patterns (${byContent.size} unique)`);
|
|
390
|
-
// Persist the compacted set immediately so the file shrinks on disk
|
|
399
|
+
// Persist the compacted set immediately so the file shrinks on disk.
|
|
400
|
+
// Mark every kept row dirty so this compaction wins over the disk
|
|
401
|
+
// copies, and tombstone the discarded duplicates.
|
|
402
|
+
for (const pattern of byContent.values())
|
|
403
|
+
this.dirtyIds.add(pattern.id);
|
|
404
|
+
for (const id of removedIds)
|
|
405
|
+
this.deletedIds.add(id);
|
|
391
406
|
this.dirty = true;
|
|
392
407
|
this.flushToDisk();
|
|
393
408
|
}
|
|
394
409
|
}
|
|
395
410
|
}
|
|
396
411
|
}
|
|
397
|
-
catch {
|
|
398
|
-
//
|
|
412
|
+
catch (error) {
|
|
413
|
+
// A corrupt patterns file must be preserved, not silently replaced by
|
|
414
|
+
// an empty bank on the next flush (#4735).
|
|
415
|
+
try {
|
|
416
|
+
const corruptPath = `${getPatternsPath()}.corrupt-${Date.now()}`;
|
|
417
|
+
renameSync(getPatternsPath(), corruptPath);
|
|
418
|
+
console.warn(`[intelligence] Corrupt patterns file moved to ${corruptPath}`);
|
|
419
|
+
}
|
|
420
|
+
catch { /* already absent or rename failed — never overwrite it below */ }
|
|
399
421
|
}
|
|
400
422
|
}
|
|
401
423
|
/**
|
|
@@ -415,6 +437,11 @@ class LocalReasoningBank {
|
|
|
415
437
|
}
|
|
416
438
|
/**
|
|
417
439
|
* Immediately flush patterns to disk
|
|
440
|
+
*
|
|
441
|
+
* Re-reads the file under the cooperating-writer lock and merges only the
|
|
442
|
+
* patterns this process changed, so a long-lived daemon cannot erase patterns
|
|
443
|
+
* written by another hook/process (#4735). Deletions and clear() are carried
|
|
444
|
+
* as tombstones; the write itself is temp-file + rename.
|
|
418
445
|
*/
|
|
419
446
|
flushToDisk() {
|
|
420
447
|
if (!this.persistenceEnabled || !this.dirty)
|
|
@@ -422,11 +449,52 @@ class LocalReasoningBank {
|
|
|
422
449
|
try {
|
|
423
450
|
ensureDataDir();
|
|
424
451
|
const path = getPatternsPath();
|
|
425
|
-
|
|
426
|
-
|
|
452
|
+
withStatsFileLock(path, (canonicalPath) => {
|
|
453
|
+
let diskPatterns = [];
|
|
454
|
+
if (existsSync(canonicalPath)) {
|
|
455
|
+
try {
|
|
456
|
+
const parsed = JSON.parse(readFileSync(canonicalPath, 'utf-8'));
|
|
457
|
+
if (!Array.isArray(parsed))
|
|
458
|
+
throw new Error('patterns.json is not an array');
|
|
459
|
+
diskPatterns = parsed;
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
// Preserve the corrupt original instead of overwriting it with an
|
|
463
|
+
// empty bank; the new file is written only after this rename.
|
|
464
|
+
try {
|
|
465
|
+
const corruptPath = `${canonicalPath}.corrupt-${Date.now()}`;
|
|
466
|
+
renameSync(canonicalPath, corruptPath);
|
|
467
|
+
console.warn(`[intelligence] Corrupt patterns file moved to ${corruptPath}`);
|
|
468
|
+
}
|
|
469
|
+
catch { /* leave the original in place */ }
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const merged = new Map();
|
|
473
|
+
if (!this.clearRequested) {
|
|
474
|
+
for (const pattern of diskPatterns) {
|
|
475
|
+
if (!this.deletedIds.has(pattern.id))
|
|
476
|
+
merged.set(pattern.id, pattern);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
for (const pattern of this.patternList) {
|
|
480
|
+
if (this.clearRequested || this.dirtyIds.has(pattern.id)) {
|
|
481
|
+
merged.set(pattern.id, pattern);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const list = [...merged.values()];
|
|
485
|
+
const tmp = `${canonicalPath}.${process.pid}.${Date.now()}.tmp`;
|
|
486
|
+
writeFileSync(tmp, JSON.stringify(list, null, 2), 'utf-8');
|
|
487
|
+
renameSync(tmp, canonicalPath);
|
|
488
|
+
this.patterns = new Map(list.map((pattern) => [pattern.id, pattern]));
|
|
489
|
+
this.patternList = list;
|
|
490
|
+
this.dirty = false;
|
|
491
|
+
this.dirtyIds.clear();
|
|
492
|
+
this.deletedIds.clear();
|
|
493
|
+
this.clearRequested = false;
|
|
494
|
+
});
|
|
427
495
|
}
|
|
428
496
|
catch (error) {
|
|
429
|
-
// Log but don't throw - persistence failures shouldn't break training
|
|
497
|
+
// Log but don't throw - persistence failures shouldn't break training.
|
|
430
498
|
console.error('Failed to persist patterns:', error);
|
|
431
499
|
}
|
|
432
500
|
}
|
|
@@ -459,6 +527,7 @@ class LocalReasoningBank {
|
|
|
459
527
|
this.patternList[idx] = stored;
|
|
460
528
|
}
|
|
461
529
|
this.patterns.set(pattern.id, stored);
|
|
530
|
+
this.dirtyIds.add(pattern.id);
|
|
462
531
|
this.saveToDisk();
|
|
463
532
|
return false;
|
|
464
533
|
}
|
|
@@ -474,6 +543,7 @@ class LocalReasoningBank {
|
|
|
474
543
|
}
|
|
475
544
|
// Keep the Map in sync with the mutated object
|
|
476
545
|
this.patterns.set(contentDupe.id, contentDupe);
|
|
546
|
+
this.dirtyIds.add(contentDupe.id);
|
|
477
547
|
this.saveToDisk();
|
|
478
548
|
return false;
|
|
479
549
|
}
|
|
@@ -482,11 +552,14 @@ class LocalReasoningBank {
|
|
|
482
552
|
const oldest = this.patternList.shift();
|
|
483
553
|
if (oldest) {
|
|
484
554
|
this.patterns.delete(oldest.id);
|
|
555
|
+
this.deletedIds.add(oldest.id);
|
|
556
|
+
this.dirtyIds.delete(oldest.id);
|
|
485
557
|
}
|
|
486
558
|
}
|
|
487
559
|
this.patternList.push(stored);
|
|
488
560
|
}
|
|
489
561
|
this.patterns.set(pattern.id, stored);
|
|
562
|
+
this.dirtyIds.add(stored.id);
|
|
490
563
|
// Trigger persistence (debounced)
|
|
491
564
|
this.saveToDisk();
|
|
492
565
|
return true;
|
|
@@ -583,6 +656,8 @@ class LocalReasoningBank {
|
|
|
583
656
|
if (idx >= 0) {
|
|
584
657
|
this.patternList.splice(idx, 1);
|
|
585
658
|
}
|
|
659
|
+
this.dirtyIds.delete(id);
|
|
660
|
+
this.deletedIds.add(id);
|
|
586
661
|
this.saveToDisk();
|
|
587
662
|
return true;
|
|
588
663
|
}
|
|
@@ -592,6 +667,9 @@ class LocalReasoningBank {
|
|
|
592
667
|
clear() {
|
|
593
668
|
this.patterns.clear();
|
|
594
669
|
this.patternList = [];
|
|
670
|
+
this.dirtyIds.clear();
|
|
671
|
+
this.deletedIds.clear();
|
|
672
|
+
this.clearRequested = true;
|
|
595
673
|
this.saveToDisk();
|
|
596
674
|
}
|
|
597
675
|
}
|
|
@@ -361,7 +361,9 @@ export declare function loadEmbeddingModel(options?: {
|
|
|
361
361
|
* own embedder is real or stubbed). Set `backend` truthfully by the path that
|
|
362
362
|
* actually produced the vector. Do NOT change the embedding math.
|
|
363
363
|
*/
|
|
364
|
-
export declare function generateEmbedding(text: string
|
|
364
|
+
export declare function generateEmbedding(text: string, options?: {
|
|
365
|
+
normalize?: boolean;
|
|
366
|
+
}): Promise<{
|
|
365
367
|
embedding: number[];
|
|
366
368
|
dimensions: number;
|
|
367
369
|
model: string;
|
|
@@ -382,7 +384,9 @@ export declare function generateEmbedding(text: string): Promise<{
|
|
|
382
384
|
*
|
|
383
385
|
* Keeping the local chain as its own export breaks that cycle structurally.
|
|
384
386
|
*/
|
|
385
|
-
export declare function generateLocalEmbedding(text: string
|
|
387
|
+
export declare function generateLocalEmbedding(text: string, options?: {
|
|
388
|
+
normalize?: boolean;
|
|
389
|
+
}): Promise<{
|
|
386
390
|
embedding: number[];
|
|
387
391
|
dimensions: number;
|
|
388
392
|
model: string;
|
|
@@ -2253,7 +2253,12 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
|
|
|
2253
2253
|
* own embedder is real or stubbed). Set `backend` truthfully by the path that
|
|
2254
2254
|
* actually produced the vector. Do NOT change the embedding math.
|
|
2255
2255
|
*/
|
|
2256
|
-
export async function generateEmbedding(text) {
|
|
2256
|
+
export async function generateEmbedding(text, options) {
|
|
2257
|
+
// #4724: AgentDB embed() has no normalize switch and always returns a unit
|
|
2258
|
+
// vector. A raw request has to use the local chain, which can skip L2.
|
|
2259
|
+
if (options?.normalize === false) {
|
|
2260
|
+
return generateLocalEmbedding(text, { normalize: false });
|
|
2261
|
+
}
|
|
2257
2262
|
// ADR-053: Try AgentDB v3 bridge first
|
|
2258
2263
|
const bridge = await getBridge();
|
|
2259
2264
|
if (bridge) {
|
|
@@ -2283,7 +2288,9 @@ export async function generateEmbedding(text) {
|
|
|
2283
2288
|
*
|
|
2284
2289
|
* Keeping the local chain as its own export breaks that cycle structurally.
|
|
2285
2290
|
*/
|
|
2286
|
-
export async function generateLocalEmbedding(text) {
|
|
2291
|
+
export async function generateLocalEmbedding(text, options) {
|
|
2292
|
+
// Default true keeps the historical unit-norm embedding math.
|
|
2293
|
+
const normalize = options?.normalize !== false;
|
|
2287
2294
|
// Ensure the LOCAL model is loaded. #3375: this must not go through
|
|
2288
2295
|
// loadEmbeddingModel(), which is bridge-first — when the bridge answered
|
|
2289
2296
|
// there, no local model was ever loaded and this function always returned
|
|
@@ -2314,7 +2321,7 @@ export async function generateLocalEmbedding(text) {
|
|
|
2314
2321
|
// Use ONNX model if available
|
|
2315
2322
|
if (state.model && typeof state.model === 'function') {
|
|
2316
2323
|
try {
|
|
2317
|
-
const output = await state.model(text, { pooling: 'mean', normalize
|
|
2324
|
+
const output = await state.model(text, { pooling: 'mean', normalize });
|
|
2318
2325
|
// Handle @xenova/transformers (output.data), ruvector (plain array) and
|
|
2319
2326
|
// typed-array (ReasoningBank Float32Array, #3114) formats. DataView is an
|
|
2320
2327
|
// ArrayBuffer view without an iterator (Array.from gives []), so exclude it.
|
|
@@ -2338,7 +2345,7 @@ export async function generateLocalEmbedding(text) {
|
|
|
2338
2345
|
// Deterministic hash-based fallback (for testing/demo without ONNX).
|
|
2339
2346
|
// AUDIT #3: backend='mock' — these vectors do NOT carry real semantics.
|
|
2340
2347
|
(await import('./embedding-policy.js')).enforceNoStub('memory-initializer.generateLocalEmbedding'); // "no stubs" strict mode
|
|
2341
|
-
const embedding = generateHashEmbedding(text, state.dimensions);
|
|
2348
|
+
const embedding = generateHashEmbedding(text, state.dimensions, normalize);
|
|
2342
2349
|
return {
|
|
2343
2350
|
embedding,
|
|
2344
2351
|
dimensions: state.dimensions,
|
|
@@ -2401,7 +2408,7 @@ export async function generateBatchEmbeddings(texts, options) {
|
|
|
2401
2408
|
* Generate deterministic hash-based embedding
|
|
2402
2409
|
* Not semantic, but deterministic and useful for testing
|
|
2403
2410
|
*/
|
|
2404
|
-
function generateHashEmbedding(text, dimensions) {
|
|
2411
|
+
function generateHashEmbedding(text, dimensions, normalize = true) {
|
|
2405
2412
|
const embedding = new Array(dimensions).fill(0);
|
|
2406
2413
|
// Simple hash-based approach for reproducibility
|
|
2407
2414
|
const words = text.toLowerCase().split(/\s+/);
|
|
@@ -2413,7 +2420,9 @@ function generateHashEmbedding(text, dimensions) {
|
|
|
2413
2420
|
embedding[idx] += Math.sin(charCode * 0.1) * 0.1;
|
|
2414
2421
|
}
|
|
2415
2422
|
}
|
|
2416
|
-
// Normalize to unit vector
|
|
2423
|
+
// Normalize to unit vector unless the caller asked for the raw vector (#4724).
|
|
2424
|
+
if (!normalize)
|
|
2425
|
+
return embedding;
|
|
2417
2426
|
const magnitude = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0)) || 1;
|
|
2418
2427
|
return embedding.map(v => v / magnitude);
|
|
2419
2428
|
}
|
|
@@ -192,8 +192,8 @@ export declare const inventorySourceSchema: z.ZodObject<{
|
|
|
192
192
|
}, "strict", z.ZodTypeAny, {
|
|
193
193
|
digest: string;
|
|
194
194
|
sourcePath: string;
|
|
195
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
196
195
|
package: string;
|
|
196
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
197
197
|
observedInvocation: string;
|
|
198
198
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
199
199
|
shippedIn: string[];
|
|
@@ -201,8 +201,8 @@ export declare const inventorySourceSchema: z.ZodObject<{
|
|
|
201
201
|
}, {
|
|
202
202
|
digest: string;
|
|
203
203
|
sourcePath: string;
|
|
204
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
205
204
|
package: string;
|
|
205
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
206
206
|
observedInvocation: string;
|
|
207
207
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
208
208
|
shippedIn: string[];
|
|
@@ -370,8 +370,8 @@ export declare const catalogEntrySchema: z.ZodObject<{
|
|
|
370
370
|
}, "strict", z.ZodTypeAny, {
|
|
371
371
|
digest: string;
|
|
372
372
|
sourcePath: string;
|
|
373
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
374
373
|
package: string;
|
|
374
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
375
375
|
observedInvocation: string;
|
|
376
376
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
377
377
|
shippedIn: string[];
|
|
@@ -379,8 +379,8 @@ export declare const catalogEntrySchema: z.ZodObject<{
|
|
|
379
379
|
}, {
|
|
380
380
|
digest: string;
|
|
381
381
|
sourcePath: string;
|
|
382
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
383
382
|
package: string;
|
|
383
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
384
384
|
observedInvocation: string;
|
|
385
385
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
386
386
|
shippedIn: string[];
|
|
@@ -391,8 +391,8 @@ export declare const catalogEntrySchema: z.ZodObject<{
|
|
|
391
391
|
sources: {
|
|
392
392
|
digest: string;
|
|
393
393
|
sourcePath: string;
|
|
394
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
395
394
|
package: string;
|
|
395
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
396
396
|
observedInvocation: string;
|
|
397
397
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
398
398
|
shippedIn: string[];
|
|
@@ -432,8 +432,8 @@ export declare const catalogEntrySchema: z.ZodObject<{
|
|
|
432
432
|
sources: {
|
|
433
433
|
digest: string;
|
|
434
434
|
sourcePath: string;
|
|
435
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
436
435
|
package: string;
|
|
436
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
437
437
|
observedInvocation: string;
|
|
438
438
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
439
439
|
shippedIn: string[];
|
|
@@ -636,8 +636,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
|
|
|
636
636
|
}, "strict", z.ZodTypeAny, {
|
|
637
637
|
digest: string;
|
|
638
638
|
sourcePath: string;
|
|
639
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
640
639
|
package: string;
|
|
640
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
641
641
|
observedInvocation: string;
|
|
642
642
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
643
643
|
shippedIn: string[];
|
|
@@ -645,8 +645,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
|
|
|
645
645
|
}, {
|
|
646
646
|
digest: string;
|
|
647
647
|
sourcePath: string;
|
|
648
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
649
648
|
package: string;
|
|
649
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
650
650
|
observedInvocation: string;
|
|
651
651
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
652
652
|
shippedIn: string[];
|
|
@@ -657,8 +657,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
|
|
|
657
657
|
sources: {
|
|
658
658
|
digest: string;
|
|
659
659
|
sourcePath: string;
|
|
660
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
661
660
|
package: string;
|
|
661
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
662
662
|
observedInvocation: string;
|
|
663
663
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
664
664
|
shippedIn: string[];
|
|
@@ -698,8 +698,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
|
|
|
698
698
|
sources: {
|
|
699
699
|
digest: string;
|
|
700
700
|
sourcePath: string;
|
|
701
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
702
701
|
package: string;
|
|
702
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
703
703
|
observedInvocation: string;
|
|
704
704
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
705
705
|
shippedIn: string[];
|
|
@@ -741,8 +741,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
|
|
|
741
741
|
sources: {
|
|
742
742
|
digest: string;
|
|
743
743
|
sourcePath: string;
|
|
744
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
745
744
|
package: string;
|
|
745
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
746
746
|
observedInvocation: string;
|
|
747
747
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
748
748
|
shippedIn: string[];
|
|
@@ -787,8 +787,8 @@ export declare const commandCatalogSchema: z.ZodObject<{
|
|
|
787
787
|
sources: {
|
|
788
788
|
digest: string;
|
|
789
789
|
sourcePath: string;
|
|
790
|
-
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
791
790
|
package: string;
|
|
791
|
+
channel: "repo-project-commands" | "cli-init-template" | "marketplace-plugin" | "source-plugin" | "mod-registration";
|
|
792
792
|
observedInvocation: string;
|
|
793
793
|
binding: "markdown-loader" | "mod-register" | "mod-middleware-on-markdown";
|
|
794
794
|
shippedIn: string[];
|
|
@@ -40,8 +40,7 @@ export interface ASTAnalysis {
|
|
|
40
40
|
}
|
|
41
41
|
export declare class ASTAnalyzer {
|
|
42
42
|
private config;
|
|
43
|
-
private
|
|
44
|
-
private useNative;
|
|
43
|
+
private readonly useNative;
|
|
45
44
|
private analysisCache;
|
|
46
45
|
constructor(config?: Partial<ASTAnalyzerConfig>);
|
|
47
46
|
initialize(): Promise<void>;
|
|
@@ -22,24 +22,16 @@ const LANGUAGE_PATTERNS = {
|
|
|
22
22
|
};
|
|
23
23
|
export class ASTAnalyzer {
|
|
24
24
|
config;
|
|
25
|
-
ruvectorEngine = null;
|
|
26
25
|
useNative = false;
|
|
27
26
|
analysisCache = new Map();
|
|
28
27
|
constructor(config = {}) {
|
|
29
28
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
30
29
|
}
|
|
31
30
|
async initialize() {
|
|
32
|
-
try
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.ruvectorEngine = ruvector.createASTAnalyzer?.(this.config);
|
|
37
|
-
this.useNative = !!this.ruvectorEngine;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
this.useNative = false;
|
|
42
|
-
}
|
|
31
|
+
// Pure-JS analysis. This used to try `import('@ruvector/ast')`, a package that has
|
|
32
|
+
// never been published (npm 404), so it always fell through to here. ruvector@0.3's
|
|
33
|
+
// ASTParser needs the native tree-sitter grammars and is async-only, so it cannot
|
|
34
|
+
// back this synchronous analyzer.
|
|
43
35
|
}
|
|
44
36
|
analyze(code, filePath = 'unknown') {
|
|
45
37
|
const startTime = performance.now();
|
|
@@ -63,8 +63,7 @@ export interface CoverageRouteResult {
|
|
|
63
63
|
}
|
|
64
64
|
export declare class CoverageRouter {
|
|
65
65
|
private config;
|
|
66
|
-
private
|
|
67
|
-
private useNative;
|
|
66
|
+
private readonly useNative;
|
|
68
67
|
private coverageHistory;
|
|
69
68
|
constructor(config?: Partial<CoverageRouterConfig>);
|
|
70
69
|
initialize(): Promise<void>;
|
|
@@ -37,24 +37,16 @@ const DEFAULT_CONFIG = {
|
|
|
37
37
|
};
|
|
38
38
|
export class CoverageRouter {
|
|
39
39
|
config;
|
|
40
|
-
ruvectorEngine = null;
|
|
41
40
|
useNative = false;
|
|
42
41
|
coverageHistory = [];
|
|
43
42
|
constructor(config = {}) {
|
|
44
43
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
45
44
|
}
|
|
46
45
|
async initialize() {
|
|
47
|
-
try
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
this.ruvectorEngine = ruvector.createCoverageRouter?.(this.config);
|
|
52
|
-
this.useNative = !!this.ruvectorEngine;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
this.useNative = false;
|
|
57
|
-
}
|
|
46
|
+
// Pure-JS analysis. This used to try `import('@ruvector/coverage')`, a package that
|
|
47
|
+
// has never been published (npm 404), so it always fell through to here.
|
|
48
|
+
// ruvector@0.3's parseIstanbulCoverage is a plain-JS parser equivalent to the parsers
|
|
49
|
+
// below.
|
|
58
50
|
}
|
|
59
51
|
parseCoverage(data, format = 'json') {
|
|
60
52
|
switch (format) {
|
|
@@ -49,8 +49,7 @@ export interface DiffAnalysis {
|
|
|
49
49
|
}
|
|
50
50
|
export declare class DiffClassifier {
|
|
51
51
|
private config;
|
|
52
|
-
private
|
|
53
|
-
private useNative;
|
|
52
|
+
private readonly useNative;
|
|
54
53
|
private classificationCache;
|
|
55
54
|
constructor(config?: Partial<DiffClassifierConfig>);
|
|
56
55
|
initialize(): Promise<void>;
|
|
@@ -60,24 +60,15 @@ function isSecuritySensitivePath(path) {
|
|
|
60
60
|
}
|
|
61
61
|
export class DiffClassifier {
|
|
62
62
|
config;
|
|
63
|
-
ruvectorEngine = null;
|
|
64
63
|
useNative = false;
|
|
65
64
|
classificationCache = new Map();
|
|
66
65
|
constructor(config = {}) {
|
|
67
66
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
68
67
|
}
|
|
69
68
|
async initialize() {
|
|
70
|
-
try
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (ruvector) {
|
|
74
|
-
this.ruvectorEngine = ruvector.createDiffClassifier?.(this.config);
|
|
75
|
-
this.useNative = !!this.ruvectorEngine;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
this.useNative = false;
|
|
80
|
-
}
|
|
69
|
+
// Pure-JS analysis. This used to try `import('@ruvector/diff')`, a package that has
|
|
70
|
+
// never been published (npm 404), so it always fell through to here. ruvector@0.3's
|
|
71
|
+
// parseDiff is a plain-JS parser equivalent to parseDiff() below.
|
|
81
72
|
}
|
|
82
73
|
parseDiff(diffContent) {
|
|
83
74
|
const files = [];
|