weavatrix 0.2.12 → 0.2.14

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 (132) hide show
  1. package/README.md +90 -21
  2. package/SECURITY.md +2 -2
  3. package/docs/releases/v0.2.14.md +93 -0
  4. package/package.json +2 -2
  5. package/skill/SKILL.md +108 -43
  6. package/src/analysis/allowed-test-runner.js +20 -9
  7. package/src/analysis/architecture/contract-graph.js +119 -0
  8. package/src/analysis/architecture/contract-schema.js +110 -0
  9. package/src/analysis/architecture/contract-storage.js +35 -0
  10. package/src/analysis/architecture/contract-verification.js +168 -0
  11. package/src/analysis/architecture-contract.js +16 -343
  12. package/src/analysis/change-classification/diff-parser.js +103 -0
  13. package/src/analysis/change-classification/options.js +40 -0
  14. package/src/analysis/change-classification/symbol-classifier.js +177 -0
  15. package/src/analysis/change-classification.js +120 -519
  16. package/src/analysis/dead-code-review/policy.js +23 -0
  17. package/src/analysis/dead-code-review.js +9 -19
  18. package/src/analysis/dep-check.js +10 -57
  19. package/src/analysis/dep-rules.js +10 -303
  20. package/src/analysis/dependency/scoped-dependencies.js +40 -0
  21. package/src/analysis/endpoints/common.js +62 -0
  22. package/src/analysis/endpoints/extract.js +107 -0
  23. package/src/analysis/endpoints/inventory.js +84 -0
  24. package/src/analysis/endpoints/mounts.js +129 -0
  25. package/src/analysis/endpoints-java.js +80 -3
  26. package/src/analysis/endpoints.js +3 -448
  27. package/src/analysis/git-history/analytics.js +177 -0
  28. package/src/analysis/git-history/collector.js +109 -0
  29. package/src/analysis/git-history/options.js +68 -0
  30. package/src/analysis/git-history.js +128 -577
  31. package/src/analysis/health-capabilities.js +82 -0
  32. package/src/analysis/http-contracts/analysis.js +169 -0
  33. package/src/analysis/http-contracts/client-call-detection.js +81 -0
  34. package/src/analysis/http-contracts/client-call-parser.js +255 -0
  35. package/src/analysis/http-contracts/graph-context.js +143 -0
  36. package/src/analysis/http-contracts/matching.js +61 -0
  37. package/src/analysis/http-contracts/shared.js +86 -0
  38. package/src/analysis/http-contracts.js +6 -822
  39. package/src/analysis/internal-audit/dependency-health.js +111 -0
  40. package/src/analysis/internal-audit/python-manifests.js +65 -0
  41. package/src/analysis/internal-audit/repo-files.js +55 -0
  42. package/src/analysis/internal-audit/supply-chain.js +132 -0
  43. package/src/analysis/internal-audit.collect.js +5 -106
  44. package/src/analysis/internal-audit.run.js +113 -200
  45. package/src/analysis/jvm-dependency-evidence.js +69 -0
  46. package/src/analysis/source-correctness/retry-patterns.js +93 -0
  47. package/src/analysis/source-correctness.js +225 -0
  48. package/src/analysis/structure/dependency-graph.js +156 -0
  49. package/src/analysis/structure/findings.js +142 -0
  50. package/src/graph/builder/go-receiver-resolution.js +112 -0
  51. package/src/graph/builder/js/queries.js +48 -0
  52. package/src/graph/builder/lang-go.js +89 -4
  53. package/src/graph/builder/lang-js.js +4 -44
  54. package/src/graph/builder/pass2-resolution.js +68 -0
  55. package/src/graph/freshness-probe.js +2 -1
  56. package/src/graph/incremental-refresh.js +3 -2
  57. package/src/graph/internal-builder.build.js +22 -183
  58. package/src/graph/internal-builder.pass2.js +161 -0
  59. package/src/graph/internal-builder.resolvers.js +2 -105
  60. package/src/graph/resolvers/rust.js +117 -0
  61. package/src/mcp/actions/advisories.mjs +18 -0
  62. package/src/mcp/actions/graph-lifecycle.mjs +148 -0
  63. package/src/mcp/actions/graph-sync.mjs +195 -0
  64. package/src/mcp/actions/hosted-architecture.mjs +57 -0
  65. package/src/mcp/architecture-bootstrap.mjs +168 -0
  66. package/src/mcp/architecture-starter.mjs +234 -0
  67. package/src/mcp/catalog.mjs +22 -6
  68. package/src/mcp/evidence/bun-lock-graph.mjs +209 -0
  69. package/src/mcp/evidence/package-graph-common.mjs +115 -0
  70. package/src/mcp/evidence/package-lock-graph.mjs +92 -0
  71. package/src/mcp/evidence-snapshot.package-graph.mjs +5 -474
  72. package/src/mcp/graph/context-core.mjs +111 -0
  73. package/src/mcp/graph/context-seeds.mjs +208 -0
  74. package/src/mcp/graph/context-state.mjs +86 -0
  75. package/src/mcp/graph/tools-core.mjs +143 -0
  76. package/src/mcp/graph/tools-query.mjs +223 -0
  77. package/src/mcp/graph-context.mjs +4 -496
  78. package/src/mcp/health/audit-format.mjs +172 -0
  79. package/src/mcp/health/audit.mjs +171 -0
  80. package/src/mcp/health/dead-code.mjs +110 -0
  81. package/src/mcp/health/duplicates.mjs +83 -0
  82. package/src/mcp/health/endpoints.mjs +42 -0
  83. package/src/mcp/health/structure.mjs +219 -0
  84. package/src/mcp/runtime-version.mjs +32 -0
  85. package/src/mcp/server/auto-refresh.mjs +66 -0
  86. package/src/mcp/server/runtime-config.mjs +43 -0
  87. package/src/mcp/server/shutdown.mjs +40 -0
  88. package/src/mcp/sync/evidence-architecture.mjs +54 -0
  89. package/src/mcp/sync/evidence-common.mjs +88 -0
  90. package/src/mcp/sync/evidence-duplicates.mjs +100 -0
  91. package/src/mcp/sync/evidence-health.mjs +56 -0
  92. package/src/mcp/sync/evidence-packages.mjs +95 -0
  93. package/src/mcp/sync/payload-common.mjs +97 -0
  94. package/src/mcp/sync/payload-v2.mjs +53 -0
  95. package/src/mcp/sync/payload-v3.mjs +79 -0
  96. package/src/mcp/sync-evidence.mjs +7 -402
  97. package/src/mcp/sync-payload.mjs +10 -244
  98. package/src/mcp/tools-actions.mjs +18 -414
  99. package/src/mcp/tools-architecture.mjs +18 -70
  100. package/src/mcp/tools-context.mjs +56 -15
  101. package/src/mcp/tools-endpoints.mjs +4 -1
  102. package/src/mcp/tools-graph.mjs +17 -330
  103. package/src/mcp/tools-health.mjs +24 -705
  104. package/src/mcp/tools-verified-change.mjs +12 -4
  105. package/src/mcp-server.mjs +49 -146
  106. package/src/precision/lsp-client/constants.js +12 -0
  107. package/src/precision/lsp-client/environment.js +20 -0
  108. package/src/precision/lsp-client/errors.js +15 -0
  109. package/src/precision/lsp-client/lifecycle.js +127 -0
  110. package/src/precision/lsp-client/message-parser.js +81 -0
  111. package/src/precision/lsp-client/protocol.js +212 -0
  112. package/src/precision/lsp-client/registry.js +58 -0
  113. package/src/precision/lsp-client/repo-uri.js +60 -0
  114. package/src/precision/lsp-client/stdio-client.js +151 -0
  115. package/src/precision/lsp-client.js +10 -682
  116. package/src/precision/lsp-overlay/build.js +238 -0
  117. package/src/precision/lsp-overlay/contract.js +61 -0
  118. package/src/precision/lsp-overlay/reference-results.js +108 -0
  119. package/src/precision/lsp-overlay/semantic-inputs.js +62 -0
  120. package/src/precision/lsp-overlay/source-session.js +134 -0
  121. package/src/precision/lsp-overlay/store.js +150 -0
  122. package/src/precision/lsp-overlay/target-index.js +147 -0
  123. package/src/precision/lsp-overlay/target-query.js +55 -0
  124. package/src/precision/lsp-overlay.js +11 -868
  125. package/src/precision/typescript-lsp-provider.js +12 -682
  126. package/src/precision/typescript-provider/client.js +69 -0
  127. package/src/precision/typescript-provider/discovery.js +124 -0
  128. package/src/precision/typescript-provider/project-host.js +249 -0
  129. package/src/precision/typescript-provider/project-safety.js +161 -0
  130. package/src/precision/typescript-provider/reference-usage.js +53 -0
  131. package/src/version.js +7 -0
  132. package/docs/releases/v0.2.12.md +0 -45
@@ -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
+ }