weavatrix 0.2.13 → 0.2.15

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 (145) hide show
  1. package/README.md +99 -39
  2. package/SECURITY.md +2 -2
  3. package/docs/releases/v0.2.15.md +37 -0
  4. package/package.json +2 -2
  5. package/skill/SKILL.md +57 -38
  6. package/src/analysis/architecture/contract-graph.js +119 -0
  7. package/src/analysis/architecture/contract-schema.js +110 -0
  8. package/src/analysis/architecture/contract-storage.js +35 -0
  9. package/src/analysis/architecture/contract-verification.js +168 -0
  10. package/src/analysis/architecture-contract.js +16 -343
  11. package/src/analysis/change-classification/diff-parser.js +103 -0
  12. package/src/analysis/change-classification/options.js +40 -0
  13. package/src/analysis/change-classification/symbol-classifier.js +177 -0
  14. package/src/analysis/change-classification.js +120 -519
  15. package/src/analysis/dead-code-review/policy.js +23 -0
  16. package/src/analysis/dead-code-review.js +9 -19
  17. package/src/analysis/dep-check.js +14 -71
  18. package/src/analysis/dep-rules.js +10 -303
  19. package/src/analysis/dependency/conventions.js +16 -0
  20. package/src/analysis/dependency/scoped-dependencies.js +45 -0
  21. package/src/analysis/dependency/source-references.js +21 -0
  22. package/src/analysis/duplicates.tokenize.js +12 -2
  23. package/src/analysis/endpoints/common.js +62 -0
  24. package/src/analysis/endpoints/extract.js +107 -0
  25. package/src/analysis/endpoints/inventory.js +84 -0
  26. package/src/analysis/endpoints/mounts.js +129 -0
  27. package/src/analysis/endpoints-java.js +80 -3
  28. package/src/analysis/endpoints.js +3 -448
  29. package/src/analysis/git-history/analytics.js +177 -0
  30. package/src/analysis/git-history/collector.js +109 -0
  31. package/src/analysis/git-history/options.js +68 -0
  32. package/src/analysis/git-history.js +128 -577
  33. package/src/analysis/health-capabilities.js +82 -0
  34. package/src/analysis/http-contracts/analysis.js +169 -0
  35. package/src/analysis/http-contracts/client-call-detection.js +81 -0
  36. package/src/analysis/http-contracts/client-call-parser.js +255 -0
  37. package/src/analysis/http-contracts/graph-context.js +143 -0
  38. package/src/analysis/http-contracts/matching.js +61 -0
  39. package/src/analysis/http-contracts/shared.js +86 -0
  40. package/src/analysis/http-contracts.js +6 -822
  41. package/src/analysis/internal-audit/dependency-health.js +111 -0
  42. package/src/analysis/internal-audit/python-manifests.js +65 -0
  43. package/src/analysis/internal-audit/repo-files.js +55 -0
  44. package/src/analysis/internal-audit/supply-chain.js +132 -0
  45. package/src/analysis/internal-audit.collect.js +5 -106
  46. package/src/analysis/internal-audit.reach.js +20 -0
  47. package/src/analysis/internal-audit.run.js +114 -200
  48. package/src/analysis/jvm-dependency-evidence.js +69 -0
  49. package/src/analysis/source-correctness/retry-patterns.js +93 -0
  50. package/src/analysis/source-correctness.js +225 -0
  51. package/src/analysis/structure/dependency-graph.js +156 -0
  52. package/src/analysis/structure/findings.js +142 -0
  53. package/src/analysis/task-retrieval.js +3 -14
  54. package/src/graph/builder/go-receiver-resolution.js +112 -0
  55. package/src/graph/builder/js/queries.js +48 -0
  56. package/src/graph/builder/lang-go.js +89 -4
  57. package/src/graph/builder/lang-js.js +4 -44
  58. package/src/graph/builder/pass2-resolution.js +68 -0
  59. package/src/graph/freshness-probe.js +2 -1
  60. package/src/graph/incremental-refresh.js +3 -2
  61. package/src/graph/internal-builder.build.js +22 -183
  62. package/src/graph/internal-builder.pass2.js +161 -0
  63. package/src/graph/internal-builder.resolvers.js +2 -105
  64. package/src/graph/resolvers/rust.js +117 -0
  65. package/src/mcp/actions/advisories.mjs +18 -0
  66. package/src/mcp/actions/graph-lifecycle.mjs +148 -0
  67. package/src/mcp/actions/graph-sync.mjs +195 -0
  68. package/src/mcp/actions/hosted-architecture.mjs +57 -0
  69. package/src/mcp/architecture-bootstrap.mjs +168 -0
  70. package/src/mcp/architecture-starter.mjs +234 -0
  71. package/src/mcp/catalog.mjs +20 -6
  72. package/src/mcp/evidence/bun-lock-graph.mjs +209 -0
  73. package/src/mcp/evidence/duplicate-member-order.mjs +8 -0
  74. package/src/mcp/evidence/package-graph-common.mjs +115 -0
  75. package/src/mcp/evidence/package-lock-graph.mjs +92 -0
  76. package/src/mcp/evidence-snapshot.duplicates.mjs +3 -7
  77. package/src/mcp/evidence-snapshot.package-graph.mjs +5 -474
  78. package/src/mcp/git-output.mjs +10 -0
  79. package/src/mcp/graph/context-core.mjs +111 -0
  80. package/src/mcp/graph/context-seeds.mjs +197 -0
  81. package/src/mcp/graph/context-state.mjs +86 -0
  82. package/src/mcp/graph/reverse-reach.mjs +42 -0
  83. package/src/mcp/graph/tools-core.mjs +143 -0
  84. package/src/mcp/graph/tools-query.mjs +223 -0
  85. package/src/mcp/graph-context.mjs +4 -496
  86. package/src/mcp/health/audit-format.mjs +172 -0
  87. package/src/mcp/health/audit.mjs +171 -0
  88. package/src/mcp/health/dead-code.mjs +110 -0
  89. package/src/mcp/health/duplicates.mjs +83 -0
  90. package/src/mcp/health/endpoints.mjs +42 -0
  91. package/src/mcp/health/structure.mjs +219 -0
  92. package/src/mcp/runtime-version.mjs +32 -0
  93. package/src/mcp/server/auto-refresh.mjs +66 -0
  94. package/src/mcp/server/runtime-config.mjs +43 -0
  95. package/src/mcp/server/shutdown.mjs +40 -0
  96. package/src/mcp/sync/evidence-architecture.mjs +54 -0
  97. package/src/mcp/sync/evidence-common.mjs +88 -0
  98. package/src/mcp/sync/evidence-duplicates.mjs +97 -0
  99. package/src/mcp/sync/evidence-health.mjs +56 -0
  100. package/src/mcp/sync/evidence-packages.mjs +95 -0
  101. package/src/mcp/sync/payload-common.mjs +97 -0
  102. package/src/mcp/sync/payload-v2.mjs +53 -0
  103. package/src/mcp/sync/payload-v3.mjs +79 -0
  104. package/src/mcp/sync-evidence.mjs +7 -402
  105. package/src/mcp/sync-payload.mjs +10 -244
  106. package/src/mcp/tools-actions.mjs +18 -414
  107. package/src/mcp/tools-architecture.mjs +18 -70
  108. package/src/mcp/tools-context.mjs +56 -15
  109. package/src/mcp/tools-endpoints.mjs +4 -1
  110. package/src/mcp/tools-graph.mjs +17 -331
  111. package/src/mcp/tools-health.mjs +24 -705
  112. package/src/mcp/tools-impact-change.mjs +2 -38
  113. package/src/mcp/tools-impact.mjs +2 -43
  114. package/src/mcp-server.mjs +35 -146
  115. package/src/path-classification.js +14 -0
  116. package/src/precision/lsp-client/constants.js +12 -0
  117. package/src/precision/lsp-client/environment.js +20 -0
  118. package/src/precision/lsp-client/errors.js +15 -0
  119. package/src/precision/lsp-client/lifecycle.js +127 -0
  120. package/src/precision/lsp-client/message-parser.js +81 -0
  121. package/src/precision/lsp-client/protocol.js +212 -0
  122. package/src/precision/lsp-client/registry.js +58 -0
  123. package/src/precision/lsp-client/repo-uri.js +60 -0
  124. package/src/precision/lsp-client/stdio-client.js +151 -0
  125. package/src/precision/lsp-client.js +10 -682
  126. package/src/precision/lsp-overlay/build.js +238 -0
  127. package/src/precision/lsp-overlay/contract.js +61 -0
  128. package/src/precision/lsp-overlay/reference-results.js +108 -0
  129. package/src/precision/lsp-overlay/semantic-inputs.js +62 -0
  130. package/src/precision/lsp-overlay/source-session.js +134 -0
  131. package/src/precision/lsp-overlay/store.js +150 -0
  132. package/src/precision/lsp-overlay/target-index.js +147 -0
  133. package/src/precision/lsp-overlay/target-query.js +55 -0
  134. package/src/precision/lsp-overlay.js +11 -868
  135. package/src/precision/typescript-lsp-provider.js +12 -682
  136. package/src/precision/typescript-provider/client.js +69 -0
  137. package/src/precision/typescript-provider/discovery.js +124 -0
  138. package/src/precision/typescript-provider/project-host.js +249 -0
  139. package/src/precision/typescript-provider/project-safety.js +161 -0
  140. package/src/precision/typescript-provider/reference-usage.js +53 -0
  141. package/src/security/malware-heuristics.sweep.js +1 -1
  142. package/src/security/registry-sig.classify.js +2 -1
  143. package/src/security/registry-sig.rules.js +3 -3
  144. package/src/version.js +7 -0
  145. package/docs/releases/v0.2.13.md +0 -48
@@ -0,0 +1,109 @@
1
+ import {spawn} from 'node:child_process'
2
+ import {isWeavatrixIgnored} from '../../path-ignore.js'
3
+ import {
4
+ boundedHistoryInteger,
5
+ GIT_HISTORY_DEFAULTS as DEFAULTS,
6
+ GIT_HISTORY_HARD_CAPS as HARD_CAPS,
7
+ safeHistoryPath,
8
+ } from './options.js'
9
+
10
+ const HEADER_SEPARATOR = '\x1e'
11
+ const FIELD_SEPARATOR = '\x1f'
12
+
13
+ export function boundedGitCommand(command, args, options) {
14
+ return new Promise((resolve, reject) => {
15
+ const child = spawn(command, args, {
16
+ cwd: options.cwd, env: options.env, shell: false, windowsHide: true,
17
+ stdio: ['ignore', 'pipe', 'pipe'],
18
+ })
19
+ const stdout = [], stderr = []
20
+ let stdoutBytes = 0, stderrBytes = 0, truncated = false, timedOut = false, settled = false
21
+ const finish = (callback) => {
22
+ if (settled) return
23
+ settled = true
24
+ clearTimeout(timer)
25
+ callback()
26
+ }
27
+ const stop = () => { try { child.kill('SIGKILL') } catch { /* process already exited */ } }
28
+ const timer = setTimeout(() => { timedOut = true; stop() }, options.timeoutMs)
29
+ child.stdout?.on('data', (chunk) => {
30
+ if (truncated) return
31
+ const remaining = options.maxOutputBytes - stdoutBytes
32
+ if (remaining <= 0) { truncated = true; stop(); return }
33
+ const kept = chunk.length <= remaining ? chunk : chunk.subarray(0, remaining)
34
+ stdout.push(kept); stdoutBytes += kept.length
35
+ if (kept.length !== chunk.length) { truncated = true; stop() }
36
+ })
37
+ child.stderr?.on('data', (chunk) => {
38
+ const remaining = 64 * 1024 - stderrBytes
39
+ if (remaining <= 0) return
40
+ const kept = chunk.length <= remaining ? chunk : chunk.subarray(0, remaining)
41
+ stderr.push(kept); stderrBytes += kept.length
42
+ })
43
+ child.on('error', (error) => finish(() => reject(error)))
44
+ child.on('close', (exitCode) => finish(() => {
45
+ if (timedOut) return reject(new Error('git history collection timed out'))
46
+ resolve({
47
+ stdout: Buffer.concat(stdout),
48
+ stderr: Buffer.concat(stderr).toString('utf8'),
49
+ exitCode: Number(exitCode ?? 1),
50
+ truncated,
51
+ })
52
+ }))
53
+ })
54
+ }
55
+
56
+ const statNumber = (value) => value === '-'
57
+ ? {value: 0, binary: true}
58
+ : {value: Number(value), binary: false}
59
+
60
+ export function parseGitNumstatLog(raw, options = {}) {
61
+ const maxFiles = boundedHistoryInteger(options.maxFilesPerCommit, DEFAULTS.maxFilesPerCommit, 2, HARD_CAPS.maxFilesPerCommit)
62
+ const ignoreRules = options.ignoreRules || []
63
+ const text = Buffer.isBuffer(raw) ? raw.toString('utf8') : String(raw || '')
64
+ const segments = text.split(HEADER_SEPARATOR).slice(1)
65
+ if (options.dropLastIncomplete && segments.length) segments.pop()
66
+ const commits = []
67
+ for (const segment of segments) {
68
+ const firstNul = segment.indexOf('\0')
69
+ if (firstNul < 0) continue
70
+ const header = segment.slice(0, firstNul).replace(/^[\r\n]+/, '')
71
+ const separator = header.indexOf(FIELD_SEPARATOR)
72
+ if (separator < 0) continue
73
+ const hash = header.slice(0, separator), timestamp = Number(header.slice(separator + 1))
74
+ if (!/^[a-f0-9]{40,64}$/i.test(hash) || !Number.isInteger(timestamp) || timestamp < 0) continue
75
+ const tokens = segment.slice(firstNul + 1).split('\0'), files = new Map()
76
+ let fileCount = 0, ignoredFiles = 0, invalidPaths = 0, oversized = false
77
+ for (let index = 0; index < tokens.length; index += 1) {
78
+ const token = tokens[index].replace(/^[\r\n]+/, '')
79
+ const match = /^(\d+|-)\t(\d+|-)\t(.*)$/s.exec(token)
80
+ if (!match) continue
81
+ let rawPath = match[3], renamedFrom = null
82
+ if (!rawPath) {
83
+ renamedFrom = safeHistoryPath(tokens[index + 1])
84
+ rawPath = tokens[index + 2]
85
+ index += 2
86
+ }
87
+ const path = safeHistoryPath(rawPath)
88
+ if (!path) { invalidPaths++; continue }
89
+ if (isWeavatrixIgnored(path, ignoreRules)) { ignoredFiles++; continue }
90
+ const additions = statNumber(match[1]), deletions = statNumber(match[2])
91
+ fileCount++
92
+ if (fileCount > maxFiles) { oversized = true; files.clear(); continue }
93
+ if (oversized) continue
94
+ const previous = files.get(path)
95
+ files.set(path, {
96
+ file: path,
97
+ additions: (previous?.additions || 0) + additions.value,
98
+ deletions: (previous?.deletions || 0) + deletions.value,
99
+ binary: Boolean(previous?.binary || additions.binary || deletions.binary),
100
+ ...(renamedFrom ? {renamedFrom} : previous?.renamedFrom ? {renamedFrom: previous.renamedFrom} : {}),
101
+ })
102
+ }
103
+ commits.push({
104
+ hash, timestamp, fileCount, ignoredFiles, invalidPaths, oversized,
105
+ files: oversized ? [] : [...files.values()].sort((a, b) => a.file.localeCompare(b.file)),
106
+ })
107
+ }
108
+ return commits
109
+ }
@@ -0,0 +1,68 @@
1
+ import {isAbsolute} from 'node:path'
2
+
3
+ export const GIT_HISTORY_V = 1
4
+ export const GIT_HISTORY_WINDOWS = Object.freeze([3, 6, 12])
5
+ export const GIT_HISTORY_DEFAULTS = Object.freeze({
6
+ months: 6,
7
+ maxCommits: 500,
8
+ maxFilesPerCommit: 80,
9
+ maxPairs: 100,
10
+ minPairCount: 2,
11
+ maxPairCandidates: 100_000,
12
+ maxOutputBytes: 16 * 1024 * 1024,
13
+ timeoutMs: 20_000,
14
+ })
15
+ export const GIT_HISTORY_HARD_CAPS = Object.freeze({
16
+ maxCommits: 2_000,
17
+ maxFilesPerCommit: 200,
18
+ maxPairs: 500,
19
+ maxPairCandidates: 250_000,
20
+ maxOutputBytes: 64 * 1024 * 1024,
21
+ timeoutMs: 60_000,
22
+ })
23
+ export const GIT_FORMAT = '%x1e%H%x1f%ct'
24
+
25
+ export const graphEndpoint = (value) => value && typeof value === 'object' ? value.id : value
26
+ export const roundHistoryNumber = (value, digits = 4) => {
27
+ if (!Number.isFinite(value)) return 0
28
+ const scale = 10 ** digits
29
+ return Math.round(value * scale) / scale
30
+ }
31
+ export const boundedHistoryInteger = (value, fallback, min, max) => {
32
+ const number = Number(value)
33
+ return Number.isInteger(number) ? Math.max(min, Math.min(max, number)) : fallback
34
+ }
35
+
36
+ export function safeHistoryPath(value) {
37
+ const path = String(value || '').replace(/\\/g, '/').replace(/^\.\//, '')
38
+ if (!path || path.includes('\0') || isAbsolute(path) || /^[a-z]:\//i.test(path)) return null
39
+ if (/[\x00-\x1f\x7f]/.test(path)) return null
40
+ const parts = path.split('/')
41
+ return parts.some((part) => !part || part === '.' || part === '..') ? null : path
42
+ }
43
+
44
+ export function utcMonthsBefore(date, months) {
45
+ const source = new Date(date)
46
+ if (!Number.isFinite(source.getTime())) throw new Error('now must be a valid date')
47
+ const targetMonth = source.getUTCMonth() - months
48
+ const first = new Date(Date.UTC(source.getUTCFullYear(), targetMonth, 1, source.getUTCHours(), source.getUTCMinutes(), source.getUTCSeconds(), source.getUTCMilliseconds()))
49
+ const lastDay = new Date(Date.UTC(first.getUTCFullYear(), first.getUTCMonth() + 1, 0)).getUTCDate()
50
+ first.setUTCDate(Math.min(source.getUTCDate(), lastDay))
51
+ return first
52
+ }
53
+
54
+ export function normalizeGitHistoryOptions(input = {}) {
55
+ const defaults = GIT_HISTORY_DEFAULTS, caps = GIT_HISTORY_HARD_CAPS
56
+ const months = Number(input.months ?? defaults.months)
57
+ if (!GIT_HISTORY_WINDOWS.includes(months)) throw new Error('months must be one of 3, 6 or 12')
58
+ return {
59
+ months,
60
+ maxCommits: boundedHistoryInteger(input.maxCommits, defaults.maxCommits, 1, caps.maxCommits),
61
+ maxFilesPerCommit: boundedHistoryInteger(input.maxFilesPerCommit, defaults.maxFilesPerCommit, 2, caps.maxFilesPerCommit),
62
+ maxPairs: boundedHistoryInteger(input.maxPairs, defaults.maxPairs, 1, caps.maxPairs),
63
+ minPairCount: boundedHistoryInteger(input.minPairCount, defaults.minPairCount, 1, 100),
64
+ maxPairCandidates: boundedHistoryInteger(input.maxPairCandidates, defaults.maxPairCandidates, 100, caps.maxPairCandidates),
65
+ maxOutputBytes: boundedHistoryInteger(input.maxOutputBytes, defaults.maxOutputBytes, 64 * 1024, caps.maxOutputBytes),
66
+ timeoutMs: boundedHistoryInteger(input.timeoutMs, defaults.timeoutMs, 1_000, caps.timeoutMs),
67
+ }
68
+ }