weavatrix 0.2.3 → 0.2.5

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.
Files changed (36) hide show
  1. package/README.md +91 -20
  2. package/SECURITY.md +18 -0
  3. package/package.json +3 -1
  4. package/skill/SKILL.md +49 -13
  5. package/src/analysis/dead-check.js +48 -2
  6. package/src/analysis/dead-code-review.js +22 -8
  7. package/src/analysis/duplicates.compute.js +11 -6
  8. package/src/analysis/git-ref-graph.js +10 -2
  9. package/src/analysis/hot-path-review.js +228 -0
  10. package/src/analysis/internal-audit.run.js +36 -0
  11. package/src/analysis/source-complexity.report.js +5 -0
  12. package/src/analysis/source-complexity.walk.js +64 -10
  13. package/src/build-graph.js +49 -10
  14. package/src/graph/build-worker.js +21 -1
  15. package/src/graph/builder/lang-java.js +1 -0
  16. package/src/graph/builder/lang-js.js +24 -0
  17. package/src/graph/builder/lang-python.js +161 -4
  18. package/src/graph/freshness-probe.js +3 -3
  19. package/src/graph/incremental-refresh.js +1 -1
  20. package/src/graph/internal-builder.barrels.js +33 -4
  21. package/src/graph/internal-builder.build.js +52 -8
  22. package/src/mcp/catalog.mjs +14 -11
  23. package/src/mcp/graph-context.mjs +143 -14
  24. package/src/mcp/sync-payload.mjs +2 -1
  25. package/src/mcp/tools-actions.mjs +59 -20
  26. package/src/mcp/tools-company.mjs +11 -3
  27. package/src/mcp/tools-graph.mjs +12 -6
  28. package/src/mcp/tools-health.mjs +112 -11
  29. package/src/mcp/tools-impact.mjs +24 -6
  30. package/src/mcp/tools-source.mjs +237 -0
  31. package/src/mcp-server.mjs +99 -11
  32. package/src/path-classification.js +2 -2
  33. package/src/precision/lsp-client.js +682 -0
  34. package/src/precision/lsp-overlay.js +872 -0
  35. package/src/precision/symbol-query.js +137 -0
  36. package/src/precision/typescript-lsp-provider.js +682 -0
@@ -0,0 +1,137 @@
1
+ import {existsSync, readFileSync, statSync} from 'node:fs'
2
+ import {dirname, resolve} from 'node:path'
3
+ import {atomicWriteFileSync, withFileLock} from '../graph/file-lock.js'
4
+ import {
5
+ buildLspPrecisionOverlay,
6
+ precisionOverlayMatches,
7
+ precisionSemanticInputsMatch,
8
+ } from './lsp-overlay.js'
9
+
10
+ export const SYMBOL_PRECISION_CACHE_V = 1
11
+ export const SYMBOL_PRECISION_CACHE_FILE = 'precision-symbols.json'
12
+
13
+ const MAX_CACHE_ENTRIES = 32
14
+ const MAX_CACHE_BYTES = 8 * 1024 * 1024
15
+ const MAX_CACHE_READ_BYTES = 16 * 1024 * 1024
16
+ const inFlight = new Map()
17
+
18
+ const boundedInteger = (value, fallback, minimum, maximum) => {
19
+ const number = Number(value)
20
+ return Math.max(minimum, Math.min(maximum, Number.isFinite(number) ? Math.floor(number) : fallback))
21
+ }
22
+
23
+ export function symbolPrecisionCachePath(graphPath) {
24
+ return resolve(dirname(graphPath), SYMBOL_PRECISION_CACHE_FILE)
25
+ }
26
+
27
+ function loadRawGraph(graphPath) {
28
+ const graph = JSON.parse(readFileSync(graphPath, 'utf8'))
29
+ if (!graph || !Array.isArray(graph.nodes) || !Array.isArray(graph.links)) {
30
+ throw new Error('the active graph is not a valid Weavatrix graph')
31
+ }
32
+ return graph
33
+ }
34
+
35
+ function emptyCache() {
36
+ return {symbolPrecisionCacheV: SYMBOL_PRECISION_CACHE_V, entries: []}
37
+ }
38
+
39
+ function readCache(path) {
40
+ if (!existsSync(path)) return emptyCache()
41
+ try {
42
+ if (statSync(path).size > MAX_CACHE_READ_BYTES) return emptyCache()
43
+ const value = JSON.parse(readFileSync(path, 'utf8'))
44
+ if (Number(value?.symbolPrecisionCacheV) !== SYMBOL_PRECISION_CACHE_V || !Array.isArray(value.entries)) return emptyCache()
45
+ return {symbolPrecisionCacheV: SYMBOL_PRECISION_CACHE_V, entries: value.entries.slice(0, MAX_CACHE_ENTRIES)}
46
+ } catch {
47
+ return emptyCache()
48
+ }
49
+ }
50
+
51
+ function writeBoundedCache(path, entries) {
52
+ const kept = entries
53
+ .filter((entry) => entry && typeof entry === 'object')
54
+ .sort((left, right) => Number(right.usedAt) - Number(left.usedAt))
55
+ .slice(0, MAX_CACHE_ENTRIES)
56
+ let body
57
+ do {
58
+ body = JSON.stringify({symbolPrecisionCacheV: SYMBOL_PRECISION_CACHE_V, entries: kept})
59
+ if (Buffer.byteLength(body) <= MAX_CACHE_BYTES || !kept.length) break
60
+ kept.pop()
61
+ } while (true)
62
+ atomicWriteFileSync(path, body, 'utf8')
63
+ }
64
+
65
+ function cacheMatch(entry, {targetId, graph, repoRoot, request}) {
66
+ if (String(entry?.targetId || '') !== targetId || !entry.overlay) return false
67
+ if (!precisionOverlayMatches(entry.overlay, graph, {request})) return false
68
+ return precisionSemanticInputsMatch(entry.overlay, repoRoot, graph)
69
+ }
70
+
71
+ async function storeEntry(path, entry) {
72
+ await withFileLock(`${path}.lock`, async () => {
73
+ const current = readCache(path)
74
+ const entries = current.entries.filter((item) => String(item?.targetId || '') !== entry.targetId)
75
+ entries.unshift(entry)
76
+ writeBoundedCache(path, entries)
77
+ })
78
+ }
79
+
80
+ function cacheable(overlay) {
81
+ if (overlay?.state === 'COMPLETE') return true
82
+ return overlay?.state === 'PARTIAL'
83
+ && overlay?.reason === 'semantic precision stopped at a configured safety limit'
84
+ }
85
+
86
+ export async function querySymbolPrecision({
87
+ repoRoot,
88
+ graphPath,
89
+ targetId,
90
+ maxReferences = 1_000,
91
+ timeoutMs = 30_000,
92
+ clientFactory,
93
+ } = {}) {
94
+ if (!repoRoot || !graphPath || !targetId) throw new Error('symbol precision requires repoRoot, graphPath, and targetId')
95
+ const boundedReferences = boundedInteger(maxReferences, 1_000, 1, 5_000)
96
+ const boundedTimeout = boundedInteger(timeoutMs, 30_000, 1_000, 60_000)
97
+ const request = {maxSymbols: 1, maxReferences: boundedReferences, maxLinks: boundedReferences}
98
+ const rawGraph = loadRawGraph(graphPath)
99
+ const graph = rawGraph.graphPrecisionMode === 'off' ? {...rawGraph, graphPrecisionMode: 'lsp'} : rawGraph
100
+ const target = graph.nodes.find((node) => String(node?.id || '') === String(targetId))
101
+ if (!target) throw new Error('the selected symbol is not present in the active raw graph')
102
+ if (!target.selection_start || !/\.(?:[cm]?[jt]sx?)$/i.test(String(target.source_file || ''))) {
103
+ throw new Error('exact symbol precision currently supports JavaScript and TypeScript symbols with source selections')
104
+ }
105
+
106
+ const id = String(targetId)
107
+ const cachePath = symbolPrecisionCachePath(graphPath)
108
+ const startedAt = Date.now()
109
+ const cached = readCache(cachePath).entries.find((entry) => cacheMatch(entry, {targetId: id, graph, repoRoot, request}))
110
+ if (cached) return {overlay: cached.overlay, cached: true, elapsedMs: Date.now() - startedAt, cachePath}
111
+
112
+ const flightKey = `${graphPath}\0${graph.graphRevision || ''}\0${id}\0${boundedReferences}`
113
+ if (inFlight.has(flightKey)) return inFlight.get(flightKey)
114
+ const operation = (async () => {
115
+ const overlay = await buildLspPrecisionOverlay({
116
+ repoRoot,
117
+ graph,
118
+ mode: 'lsp',
119
+ maxSymbols: 1,
120
+ maxReferences: boundedReferences,
121
+ maxLinks: boundedReferences,
122
+ timeoutMs: boundedTimeout,
123
+ targetIds: [id],
124
+ clientFactory,
125
+ })
126
+ if (cacheable(overlay)) {
127
+ await storeEntry(cachePath, {targetId: id, usedAt: Date.now(), overlay})
128
+ }
129
+ return {overlay, cached: false, elapsedMs: Date.now() - startedAt, cachePath}
130
+ })()
131
+ inFlight.set(flightKey, operation)
132
+ try {
133
+ return await operation
134
+ } finally {
135
+ inFlight.delete(flightKey)
136
+ }
137
+ }