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
@@ -1,245 +1,14 @@
1
- // Versioned, explicit wire schema for sync_graph. Never forward graph.json wholesale: it is a local
2
- // cache file and may contain future fields or attacker-injected data that are not safe to upload.
1
+ // Versioned, explicit wire schema for sync_graph. Never forward graph.json wholesale.
3
2
  import {sanitizeEvidenceSnapshot} from './sync-evidence.mjs';
4
- import {edgeProvenance} from '../graph/edge-provenance.js';
3
+ import {
4
+ MAX_SYNC_EXTERNAL_IMPORTS, MAX_SYNC_LINKS, MAX_SYNC_NODES, assertArrayLimit,
5
+ } from './sync/payload-common.mjs';
6
+ import {sanitizeExternalImport, sanitizeLink, sanitizeNode} from './sync/payload-v2.mjs';
7
+ import {sanitizeExternalImportV3, sanitizeLinkV3, sanitizeNodeV3} from './sync/payload-v3.mjs';
5
8
 
6
- const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
7
- const ABSOLUTE_PATH_FRAGMENT = /(?:^|[\/\s"'`(=])[a-z]:[\\/]|(?:^|[\s"'`(=])(?:\\\\[^\\/\s]+(?:[\\/]|$)|file:(?:\/\/)?[\\/]|\/(?!\/)[^\s])/i;
8
-
9
- export const MAX_SYNC_BODY_BYTES = 8 * 1024 * 1024;
10
- export const MAX_SYNC_NODES = 25_000;
11
- export const MAX_SYNC_LINKS = 100_000;
12
- export const MAX_SYNC_EXTERNAL_IMPORTS = 50_000;
13
-
14
- function metadataString(value, max = 4096) {
15
- return typeof value === 'string' && value.length > 0 && value.length <= max && !CONTROL_CHARS.test(value)
16
- ? value
17
- : undefined;
18
- }
19
-
20
- // graph.json is derived data and may be edited independently of the repository. Never trust a path
21
- // merely because it occupies an allowlisted field: sync only accepts canonical-looking repo-relative
22
- // paths, on every host OS. Graph IDs append `#symbol@line`, so validate their file portion separately
23
- // while preserving the complete ID on the wire.
24
- function repoRelativePathString(value, max = 4096) {
25
- const path = metadataString(value, max);
26
- if (!path) return undefined;
27
- if (/^(?:[a-z][a-z0-9+.-]*:|[\\/])/i.test(path)) return undefined; // URI, drive path, POSIX or UNC absolute
28
- const segments = path.split(/[\\/]/);
29
- if (segments.some((segment) => segment === '.' || segment === '..')) return undefined;
30
- return path;
31
- }
32
-
33
- function graphIdString(value) {
34
- const id = metadataString(value);
35
- if (!id) return undefined;
36
- const hash = id.indexOf('#');
37
- const file = hash < 0 ? id : id.slice(0, hash);
38
- return repoRelativePathString(file) ? id : undefined;
39
- }
40
-
41
- // Optional display metadata is still attacker-controlled graph data. Keep useful labels/import
42
- // specifiers, but never let an absolute host path hide inside one of those free-text fields.
43
- function privacySafeText(value, max = 4096) {
44
- const text = metadataString(value, max);
45
- if (!text) return undefined;
46
- return ABSOLUTE_PATH_FRAGMENT.test(text) ? undefined : text;
47
- }
48
-
49
- function repoPathV3(value, max = 4096) {
50
- const path = repoRelativePathString(value, max);
51
- return path ? path.replace(/\\/g, '/') : undefined;
52
- }
53
-
54
- function graphIdV3(value) {
55
- const id = metadataString(value);
56
- if (!id) return undefined;
57
- const hash = id.indexOf('#');
58
- const file = hash < 0 ? id : id.slice(0, hash);
59
- const safeFile = repoPathV3(file);
60
- if (!safeFile) return undefined;
61
- if (hash < 0) return safeFile;
62
- const suffix = id.slice(hash);
63
- // Builder IDs are `#symbol@line` (optionally with a short collision suffix). A symbol suffix
64
- // never needs a path separator or whitespace; rejecting both closes an otherwise opaque channel.
65
- if (suffix.length > 512 || !/^#[^\\/\s\u0000-\u001f\u007f]{1,511}$/u.test(suffix)) return undefined;
66
- return `${safeFile}${suffix}`;
67
- }
68
-
69
- function safeToken(value, max = 256) {
70
- const token = metadataString(value, max);
71
- if (!token || !/^[\p{L}\p{N}_.:@+\-#$<>()\[\],]+$/u.test(token)) return undefined;
72
- return token;
73
- }
74
-
75
- function packageName(value) {
76
- const name = metadataString(value, 256);
77
- return name && /^(?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*$/i.test(name) ? name : undefined;
78
- }
79
-
80
- function externalSpecifier(value) {
81
- const spec = metadataString(value, 512);
82
- if (!spec || ABSOLUTE_PATH_FRAGMENT.test(spec) || /^(?:[a-z]:[\\/]|[\\/]|\.\.?[\\/])/i.test(spec)) return undefined;
83
- return /^[a-z0-9@][a-z0-9@._:/+\-]*$/i.test(spec) ? spec : undefined;
84
- }
85
-
86
- function finiteNumber(value) {
87
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
88
- }
89
-
90
- function setIf(out, key, value) {
91
- if (value !== undefined) out[key] = value;
92
- }
93
-
94
- const COMPLEXITY_NUMBERS = [
95
- 'startLine', 'endLine', 'loc', 'params', 'objectFields', 'branches', 'cyclomatic',
96
- 'loops', 'maxLoopDepth', 'returns', 'awaits', 'callCount', 'externalCalls',
97
- 'asyncBoundaries', 'allocations', 'objectLiterals', 'spreadCopies', 'sorts',
98
- 'linearOps', 'allocationsInLoops', 'copiesInLoops', 'linearOpsInLoops',
99
- 'sortsInLoops', 'recursionInLoops', 'timeRank', 'timeScore', 'memoryRank', 'memoryScore',
100
- ];
101
-
102
- function sanitizeComplexity(value) {
103
- if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
104
- const out = {};
105
- for (const key of COMPLEXITY_NUMBERS) setIf(out, key, finiteNumber(value[key]));
106
- if (typeof value.recursion === 'boolean') out.recursion = value.recursion;
107
- for (const key of ['family', 'scope', 'complexityScope', 'confidence']) {
108
- setIf(out, key, metadataString(value[key], 32));
109
- }
110
- return Object.keys(out).length ? out : undefined;
111
- }
112
-
113
- function sanitizeNode(value) {
114
- if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
115
- const id = graphIdString(value.id);
116
- if (!id) return null;
117
- const out = {id};
118
- setIf(out, 'label', metadataString(value.label, 1024));
119
- setIf(out, 'file_type', metadataString(value.file_type, 32));
120
- setIf(out, 'source_file', repoRelativePathString(value.source_file));
121
- const sourceLocation = metadataString(value.source_location, 32);
122
- const sourceEnd = metadataString(value.source_end, 32);
123
- if (sourceLocation && /^L\d+$/.test(sourceLocation)) out.source_location = sourceLocation;
124
- if (sourceEnd && /^L\d+$/.test(sourceEnd)) out.source_end = sourceEnd;
125
- const community = finiteNumber(value.community);
126
- if (community !== undefined && Number.isInteger(community) && community >= 0) out.community = community;
127
- if (typeof value.exported === 'boolean') out.exported = value.exported;
128
- if (typeof value.decorated === 'boolean') out.decorated = value.decorated;
129
- setIf(out, 'complexity', sanitizeComplexity(value.complexity));
130
- return out;
131
- }
132
-
133
- function sanitizeLink(value) {
134
- if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
135
- const source = graphIdString(value.source);
136
- const target = graphIdString(value.target);
137
- if (!source || !target) return null;
138
- const out = {source, target};
139
- setIf(out, 'relation', metadataString(value.relation, 32));
140
- setIf(out, 'confidence', metadataString(value.confidence, 32));
141
- if (value.typeOnly === true) out.typeOnly = true;
142
- if (value.compileOnly === true) out.compileOnly = true;
143
- const line = finiteNumber(value.line);
144
- if (line !== undefined && Number.isInteger(line) && line >= 0) out.line = line;
145
- setIf(out, 'specifier', metadataString(value.specifier));
146
- return out;
147
- }
148
-
149
- function sanitizeExternalImport(value) {
150
- if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
151
- const file = repoRelativePathString(value.file);
152
- if (!file) return null;
153
- const out = {file};
154
- for (const key of ['spec', 'target']) setIf(out, key, metadataString(value[key]));
155
- for (const key of ['pkg', 'kind', 'ecosystem']) setIf(out, key, metadataString(value[key], 256));
156
- const line = finiteNumber(value.line);
157
- if (line !== undefined && Number.isInteger(line) && line >= 0) out.line = line;
158
- for (const key of ['builtin', 'dynamic', 'unresolved']) {
159
- if (typeof value[key] === 'boolean') out[key] = value[key];
160
- }
161
- return out;
162
- }
163
-
164
- function sanitizeNodeV3(value) {
165
- const out = sanitizeNode(value);
166
- if (!out) return null;
167
- const id = graphIdV3(value.id);
168
- if (!id) return null;
169
- const sourceFile = value.source_file == null ? undefined : repoPathV3(value.source_file);
170
- if (value.source_file != null && !sourceFile) return null;
171
- const hash = id.indexOf('#');
172
- const idFile = hash < 0 ? id : id.slice(0, hash);
173
- if (sourceFile && idFile !== sourceFile) return null;
174
- out.id = id;
175
- const label = privacySafeText(value.label, 1024);
176
- if (label) out.label = label;
177
- else delete out.label;
178
- const fileType = safeToken(value.file_type, 32);
179
- if (fileType) out.file_type = fileType;
180
- else delete out.file_type;
181
- if (sourceFile) out.source_file = sourceFile;
182
- else delete out.source_file;
183
- for (const key of ['symbol_kind', 'symbol_space', 'member_of', 'visibility']) setIf(out, key, safeToken(value[key], 128));
184
- if (out.complexity) {
185
- for (const key of ['family', 'scope', 'complexityScope', 'confidence']) {
186
- const safe = safeToken(value.complexity?.[key], 32);
187
- if (safe) out.complexity[key] = safe;
188
- else delete out.complexity[key];
189
- }
190
- }
191
- return out;
192
- }
193
-
194
- function sanitizeLinkV3(value) {
195
- const out = sanitizeLink(value);
196
- if (!out) return null;
197
- const source = graphIdV3(value.source);
198
- const target = graphIdV3(value.target);
199
- if (!source || !target) return null;
200
- out.source = source;
201
- out.target = target;
202
- for (const key of ['relation', 'confidence']) {
203
- const safe = safeToken(value[key], 32);
204
- if (safe) out[key] = safe;
205
- else delete out[key];
206
- }
207
- const provenance = edgeProvenance(value);
208
- if (provenance !== 'UNKNOWN') out.provenance = provenance;
209
- const specifier = privacySafeText(value.specifier, 1024);
210
- if (specifier) out.specifier = specifier;
211
- else delete out.specifier;
212
- return out;
213
- }
214
-
215
- function sanitizeExternalImportV3(value) {
216
- const out = sanitizeExternalImport(value);
217
- if (!out) return null;
218
- const file = repoPathV3(value.file);
219
- if (!file) return null;
220
- out.file = file;
221
- setIf(out, 'spec', externalSpecifier(value.spec));
222
- if (!externalSpecifier(value.spec)) delete out.spec;
223
- setIf(out, 'pkg', packageName(value.pkg));
224
- if (!packageName(value.pkg)) delete out.pkg;
225
- for (const key of ['kind', 'ecosystem']) {
226
- const safe = safeToken(value[key], 64);
227
- if (safe) out[key] = safe;
228
- else delete out[key];
229
- }
230
- if (value.target != null) {
231
- const target = repoPathV3(value.target);
232
- if (target) out.target = target;
233
- else delete out.target;
234
- }
235
- if (value.typeOnly === true) out.typeOnly = true;
236
- return out;
237
- }
238
-
239
- function assertArrayLimit(raw, key, limit) {
240
- const count = Array.isArray(raw[key]) ? raw[key].length : 0;
241
- if (count > limit) throw new Error(`${key} has ${count} entries; maximum is ${limit}`);
242
- }
9
+ export {
10
+ MAX_SYNC_BODY_BYTES, MAX_SYNC_EXTERNAL_IMPORTS, MAX_SYNC_LINKS, MAX_SYNC_NODES,
11
+ } from './sync/payload-common.mjs';
243
12
 
244
13
  export function createSyncPayload(raw) {
245
14
  if (!raw || typeof raw !== 'object' || Array.isArray(raw) || raw.repoBoundaryV !== 1) {
@@ -251,8 +20,7 @@ export function createSyncPayload(raw) {
251
20
  const nodes = Array.isArray(raw.nodes) ? raw.nodes.map(sanitizeNode).filter(Boolean) : [];
252
21
  const links = Array.isArray(raw.links) ? raw.links.map(sanitizeLink).filter(Boolean) : [];
253
22
  const externalImports = Array.isArray(raw.externalImports)
254
- ? raw.externalImports.map(sanitizeExternalImport).filter(Boolean)
255
- : [];
23
+ ? raw.externalImports.map(sanitizeExternalImport).filter(Boolean) : [];
256
24
  return {
257
25
  syncPayloadV: 2,
258
26
  repoBoundaryV: 1,
@@ -266,8 +34,6 @@ export function createSyncPayload(raw) {
266
34
  }
267
35
 
268
36
  export function createSyncPayloadV3(raw, evidence) {
269
- // Reuse the v2 schema gates, but construct v3 arrays independently so the stricter path/identity
270
- // rules cannot change graph-only compatibility for existing endpoints.
271
37
  const base = createSyncPayload(raw);
272
38
  if (!Number.isInteger(raw.edgeProvenanceV) || raw.edgeProvenanceV < 1) {
273
39
  throw new Error('graph predates edge provenance metadata');