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,112 @@
1
+ import {goTypeName} from './lang-go.js'
2
+
3
+ const dirOf = (file) => file.includes('/') ? file.slice(0, file.lastIndexOf('/')) : ''
4
+
5
+ export function createGoReceiverResolution({resolution, nodeById, importedLocals}) {
6
+ const {dirSymbols, dirMethods, dirMethodsByName, dirTypes, resolveCall} = resolution
7
+ const typeRef = (rawType, contextFile, fallbackDir = dirOf(contextFile)) => {
8
+ const raw = String(rawType || '').trim()
9
+ if (!raw) return null
10
+ const dot = raw.indexOf('.')
11
+ if (dot > 0) {
12
+ const imported = importedLocals.get(contextFile)?.get(raw.slice(0, dot))
13
+ return imported?.targetDir ? {dir: imported.targetDir, name: raw.slice(dot + 1)} : null
14
+ }
15
+ return {dir: fallbackDir, name: raw}
16
+ }
17
+ const fieldType = (receiver, fieldName) => {
18
+ if (!receiver?.name || !fieldName) return null
19
+ const typeId = dirTypes.get(receiver.dir)?.get(receiver.name)
20
+ const typeNode = typeId && nodeById.get(typeId)
21
+ const rawFieldType = typeNode?.field_types?.[fieldName]
22
+ return rawFieldType ? typeRef(rawFieldType, typeNode.source_file, receiver.dir) : null
23
+ }
24
+ const exactMethod = (receiver, methodName) => {
25
+ const candidates = receiver && dirMethods.get(receiver.dir)?.get(receiver.name)?.get(methodName)
26
+ return candidates?.length === 1 ? candidates[0] : null
27
+ }
28
+ const uniqueMethod = (dir, methodName) => {
29
+ const candidates = dirMethodsByName.get(dir)?.get(methodName) || []
30
+ return candidates.length === 1 ? candidates[0] : null
31
+ }
32
+ const returnType = (targetId, callerFile) => {
33
+ const target = targetId && nodeById.get(targetId)
34
+ return target?.return_type
35
+ ? typeRef(target.return_type, target.source_file || callerFile, dirOf(target.source_file || callerFile))
36
+ : null
37
+ }
38
+ const fieldChildren = (node, name) => {
39
+ const children = node?.childrenForFieldName?.(name)
40
+ if (Array.isArray(children) && children.length) return children
41
+ const child = node?.childForFieldName?.(name)
42
+ return child ? [child] : []
43
+ }
44
+ const receiverBindings = (selector, file) => {
45
+ const bindings = new Map()
46
+ let scope = selector
47
+ while (scope?.parent && !['function_declaration', 'method_declaration'].includes(scope.type)) scope = scope.parent
48
+ if (!scope || !['function_declaration', 'method_declaration'].includes(scope.type)) return bindings
49
+ const callRow = selector.startPosition.row
50
+ const containsCall = (node) => node.startIndex <= selector.startIndex && node.endIndex >= selector.endIndex
51
+ const expressionType = (node) => {
52
+ if (!node) return null
53
+ const declared = goTypeName(node)
54
+ if (declared) return typeRef(declared, file)
55
+ if (['parenthesized_expression', 'unary_expression'].includes(node.type)) {
56
+ return expressionType(node.childForFieldName?.('operand') || node.namedChildren?.[0])
57
+ }
58
+ if (node.type === 'composite_literal') return typeRef(goTypeName(node.childForFieldName?.('type')), file)
59
+ if (node.type === 'identifier') return bindings.get(node.text) || null
60
+ if (node.type === 'selector_expression') return fieldType(expressionType(node.childForFieldName?.('operand')), node.childForFieldName?.('field')?.text)
61
+ if (node.type !== 'call_expression') return null
62
+ const fn = node.childForFieldName?.('function')
63
+ if (fn?.type === 'identifier') {
64
+ const returned = returnType(resolveCall(fn.text, file), file)
65
+ if (returned) return returned
66
+ if (dirTypes.get(dirOf(file))?.has(fn.text)) return typeRef(fn.text, file)
67
+ if (/^New[A-Z_]/.test(fn.text)) return typeRef(fn.text.slice(3), file)
68
+ }
69
+ if (fn?.type === 'selector_expression') {
70
+ const operand = fn.childForFieldName?.('operand'), member = fn.childForFieldName?.('field')?.text
71
+ const imported = operand?.type === 'identifier' && importedLocals.get(file)?.get(operand.text)
72
+ if (imported?.targetDir && member) {
73
+ const returned = returnType(dirSymbols.get(imported.targetDir)?.get(member), file)
74
+ if (returned) return returned
75
+ if (/^New[A-Z_]/.test(member)) return {dir: imported.targetDir, name: member.slice(3)}
76
+ }
77
+ }
78
+ return null
79
+ }
80
+ const bindTyped = (node) => {
81
+ const reference = typeRef(goTypeName(node.childForFieldName?.('type')), file)
82
+ const names = fieldChildren(node, 'name').filter((name) => ['identifier', 'field_identifier'].includes(name.type))
83
+ if (reference) for (const name of names) bindings.set(name.text, reference)
84
+ else if (node.type === 'var_spec') {
85
+ const values = node.childForFieldName?.('value')?.namedChildren || []
86
+ for (let index = 0; index < Math.min(names.length, values.length); index++) {
87
+ const inferred = expressionType(values[index])
88
+ if (inferred) bindings.set(names[index].text, inferred)
89
+ }
90
+ }
91
+ }
92
+ const bindAssignment = (node) => {
93
+ const left = node.childForFieldName?.('left')?.namedChildren || []
94
+ const right = node.childForFieldName?.('right')?.namedChildren || []
95
+ for (let index = 0; index < Math.min(left.length, right.length); index++) {
96
+ if (left[index].type !== 'identifier') continue
97
+ const inferred = expressionType(right[index])
98
+ if (inferred) bindings.set(left[index].text, inferred)
99
+ }
100
+ }
101
+ const visit = (node) => {
102
+ if (!node || node.startPosition.row > callRow) return
103
+ if (node !== scope && ['function_declaration', 'method_declaration', 'func_literal'].includes(node.type) && !containsCall(node)) return
104
+ if (['parameter_declaration', 'var_spec'].includes(node.type)) bindTyped(node)
105
+ else if (['short_var_declaration', 'assignment_statement'].includes(node.type)) bindAssignment(node)
106
+ for (const child of node.namedChildren || []) visit(child)
107
+ }
108
+ visit(scope)
109
+ return bindings
110
+ }
111
+ return {dirOf, fieldType, exactMethod, uniqueMethod, receiverBindings}
112
+ }
@@ -0,0 +1,48 @@
1
+ export const CALLABLE = /arrow_function|function|function_expression|generator_function/
2
+
3
+ export const FUNCS = `
4
+ (function_declaration name: (identifier) @fn)
5
+ (generator_function_declaration name: (identifier) @fn)
6
+ (class_declaration name: (_) @class)
7
+ (method_definition name: (_) @method)`
8
+
9
+ export const TOPVARS = `
10
+ (program (lexical_declaration (variable_declarator) @decl))
11
+ (program (variable_declaration (variable_declarator) @decl))
12
+ (program (export_statement (lexical_declaration (variable_declarator) @decl)))
13
+ (program (export_statement (variable_declaration (variable_declarator) @decl)))`
14
+
15
+ export const TYPES = `
16
+ (interface_declaration name: (_) @interface)
17
+ (type_alias_declaration name: (_) @type)
18
+ (enum_declaration name: (_) @enum)`
19
+
20
+ export const REQUIRE = `(variable_declarator name: (_) @lhs value: (call_expression function: (identifier) @req arguments: (arguments (string (string_fragment) @src))))`
21
+
22
+ export function parseExportSpecifiers(raw) {
23
+ return String(raw || '').split(',').map((part) => {
24
+ const text = part.trim()
25
+ const typeOnly = /^type\s+/.test(text)
26
+ const clean = text.replace(/^type\s+/, '').trim()
27
+ const match = clean.match(/^([A-Za-z_$][\w$]*|default)(?:\s+as\s+([A-Za-z_$][\w$]*|default))?$/)
28
+ return match ? {imported: match[1], exported: match[2] || match[1], typeOnly} : null
29
+ }).filter(Boolean)
30
+ }
31
+
32
+ export function importTypeOnly(node) {
33
+ if (/^\s*import\s+type\b/.test(node.text)) return true
34
+ const clause = node.namedChildren.find((child) => child.type === 'import_clause')
35
+ if (!clause) return false
36
+ const parts = clause.namedChildren
37
+ if (parts.some((child) => child.type === 'identifier' || child.type === 'namespace_import')) return false
38
+ const named = parts.find((child) => child.type === 'named_imports')
39
+ const specifiers = named?.namedChildren.filter((child) => child.type === 'import_specifier') || []
40
+ return specifiers.length > 0 && specifiers.every((specifier) => /^\s*type\b/.test(specifier.text))
41
+ }
42
+
43
+ export function reexportTypeOnly(node) {
44
+ if (/^\s*export\s+type\b/.test(node.text)) return true
45
+ const clause = node.namedChildren.find((child) => child.type === 'export_clause')
46
+ const specifiers = clause?.namedChildren.filter((child) => child.type === 'export_specifier') || []
47
+ return specifiers.length > 0 && specifiers.every((specifier) => /^\s*type\b/.test(specifier.text))
48
+ }
@@ -5,6 +5,61 @@
5
5
  // in the orchestrator; cross-pkg `pkg.Func()` is a selector — see resolveGoPackage/pass2). No inheritance.
6
6
  import { goSpecToPkg } from "./spec-pkg.js";
7
7
 
8
+ // Return the named Go type behind pointers/generics/parentheses. Qualified names are
9
+ // kept (`speaker.Speaker`) so pass 2 can route them through the import alias table.
10
+ export function goTypeName(node) {
11
+ if (!node) return null;
12
+ if (node.type === "type_identifier") return node.text;
13
+ if (node.type === "qualified_type") return node.text.replace(/\s+/g, "");
14
+ if (node.type === "generic_type") return goTypeName(node.childForFieldName?.("type") || node.namedChildren?.[0]);
15
+ if (["pointer_type", "parenthesized_type", "unary_expression"].includes(node.type)) {
16
+ return goTypeName(node.childForFieldName?.("type") || node.childForFieldName?.("operand") || node.namedChildren?.[0]);
17
+ }
18
+ if ((node.namedChildren?.length || 0) === 1) return goTypeName(node.namedChildren[0]);
19
+ return null;
20
+ }
21
+
22
+ const namedFieldChildren = (node, name) => {
23
+ const children = node?.childrenForFieldName?.(name);
24
+ if (Array.isArray(children) && children.length) return children;
25
+ const child = node?.childForFieldName?.(name);
26
+ return child ? [child] : [];
27
+ };
28
+
29
+ function goStructFields(typeNode) {
30
+ if (typeNode?.type !== "struct_type") return null;
31
+ const fields = {};
32
+ const visit = (node) => {
33
+ if (!node) return;
34
+ if (node.type === "field_declaration") {
35
+ const type = goTypeName(node.childForFieldName?.("type"));
36
+ if (!type) return;
37
+ const names = namedFieldChildren(node, "name");
38
+ if (names.length) for (const name of names) fields[name.text] = type;
39
+ else {
40
+ // An embedded field is addressed by the base type name (`h.Speaker`).
41
+ const embedded = type.split(".").pop();
42
+ if (embedded) fields[embedded] = type;
43
+ }
44
+ return;
45
+ }
46
+ for (const child of node.namedChildren || []) visit(child);
47
+ };
48
+ visit(typeNode);
49
+ return fields;
50
+ }
51
+
52
+ function goResultType(declaration) {
53
+ const result = declaration?.childForFieldName?.("result");
54
+ if (!result) return null;
55
+ if (result.type !== "parameter_list") return goTypeName(result);
56
+ const declarations = (result.namedChildren || []).filter((child) => child.type === "parameter_declaration");
57
+ // A call used in `value, err := New()` contributes its first result to the
58
+ // first binding. Preserve that receiver type instead of discarding every
59
+ // multi-result signature just because a trailing error is present.
60
+ return goTypeName(declarations[0]?.childForFieldName?.("type"));
61
+ }
62
+
8
63
  export default {
9
64
  family: "go",
10
65
  grammars: ["go"],
@@ -20,10 +75,40 @@ export default {
20
75
  const { grammar, tree, fileRel, caps, addSym, addImportEdge, addExternalImport, imports, resolveGoImport, dirFiles, goModule, goRequires } = ctx;
21
76
 
22
77
  // ---- symbols ----
23
- for (const cap of caps(grammar, `(function_declaration name: (identifier) @fn) (method_declaration name: (field_identifier) @method)`, tree.rootNode))
24
- addSym(cap.node.text, cap.node.startPosition.row + 1, true, { sourceNode: cap.node.parent });
25
- for (const cap of caps(grammar, `(source_file (type_declaration (type_spec name: (type_identifier) @type)))`, tree.rootNode))
26
- addSym(cap.node.text, cap.node.startPosition.row + 1, false, { sourceNode: cap.node.parent });
78
+ for (const cap of caps(grammar, `(function_declaration name: (identifier) @fn)`, tree.rootNode)) {
79
+ const declaration = cap.node.parent;
80
+ addSym(cap.node.text, cap.node.startPosition.row + 1, true, {
81
+ sourceNode: declaration,
82
+ selectionNode: cap.node,
83
+ symbolKind: "function",
84
+ returnType: goResultType(declaration),
85
+ });
86
+ }
87
+ for (const cap of caps(grammar, `(method_declaration name: (field_identifier) @method)`, tree.rootNode)) {
88
+ const declaration = cap.node.parent;
89
+ const receiver = declaration.childForFieldName?.("receiver");
90
+ const receiverDeclaration = receiver?.namedChildren?.find((child) => child.type === "parameter_declaration");
91
+ const receiverType = goTypeName(receiverDeclaration?.childForFieldName?.("type"));
92
+ addSym(cap.node.text, cap.node.startPosition.row + 1, true, {
93
+ sourceNode: declaration,
94
+ selectionNode: cap.node,
95
+ symbolKind: "method",
96
+ memberOf: receiverType || undefined,
97
+ receiverType: receiverType || undefined,
98
+ returnType: goResultType(declaration),
99
+ });
100
+ }
101
+ for (const cap of caps(grammar, `(source_file (type_declaration (type_spec name: (type_identifier) @type)))`, tree.rootNode)) {
102
+ const specification = cap.node.parent;
103
+ const typeNode = specification.childForFieldName?.("type");
104
+ addSym(cap.node.text, cap.node.startPosition.row + 1, false, {
105
+ sourceNode: specification,
106
+ selectionNode: cap.node,
107
+ symbolKind: typeNode?.type === "interface_type" ? "interface" : "type",
108
+ symbolSpace: "type",
109
+ fieldTypes: goStructFields(typeNode),
110
+ });
111
+ }
27
112
  for (const cap of caps(grammar, `(source_file (var_declaration (var_spec name: (identifier) @var))) (source_file (const_declaration (const_spec name: (identifier) @const)))`, tree.rootNode))
28
113
  addSym(cap.node.text, cap.node.startPosition.row + 1, false, { sourceNode: cap.node.parent });
29
114
 
@@ -2,33 +2,10 @@
2
2
  // Symbols: functions, generators, classes, methods, top-level const/let/var (data + arrow/fn consts).
3
3
  // Imports: ESM `import`, CJS `require`, path-aliases (tsconfig/vite), and barrel re-exports (`export … from`).
4
4
  // Calls: bare `foo()`. Heritage: `class X extends Y`. Everything runs through the shared ctx (see internal-builder.js).
5
- const CALLABLE = /arrow_function|function|function_expression|generator_function/;
6
-
7
- const FUNCS = `
8
- (function_declaration name: (identifier) @fn)
9
- (generator_function_declaration name: (identifier) @fn)
10
- (class_declaration name: (_) @class)
11
- (method_definition name: (_) @method)`;
12
- const TOPVARS = `
13
- (program (lexical_declaration (variable_declarator) @decl))
14
- (program (variable_declaration (variable_declarator) @decl))
15
- (program (export_statement (lexical_declaration (variable_declarator) @decl)))
16
- (program (export_statement (variable_declaration (variable_declarator) @decl)))`;
17
- const TYPES = `
18
- (interface_declaration name: (_) @interface)
19
- (type_alias_declaration name: (_) @type)
20
- (enum_declaration name: (_) @enum)`;
21
- const REQUIRE = `(variable_declarator name: (_) @lhs value: (call_expression function: (identifier) @req arguments: (arguments (string (string_fragment) @src))))`;
22
-
23
- function parseExportSpecifiers(raw) {
24
- return String(raw || "").split(",").map((part) => {
25
- const text = part.trim();
26
- const typeOnly = /^type\s+/.test(text);
27
- const clean = text.replace(/^type\s+/, "").trim();
28
- const match = clean.match(/^([A-Za-z_$][\w$]*|default)(?:\s+as\s+([A-Za-z_$][\w$]*|default))?$/);
29
- return match ? { imported: match[1], exported: match[2] || match[1], typeOnly } : null;
30
- }).filter(Boolean);
31
- }
5
+ import {
6
+ CALLABLE, FUNCS, TOPVARS, TYPES, REQUIRE, parseExportSpecifiers,
7
+ importTypeOnly, reexportTypeOnly,
8
+ } from './js/queries.js';
32
9
 
33
10
  export default {
34
11
  family: "js",
@@ -162,23 +139,6 @@ export default {
162
139
  });
163
140
  }
164
141
 
165
- const importTypeOnly = (node) => {
166
- if (/^\s*import\s+type\b/.test(node.text)) return true;
167
- const clause = node.namedChildren.find((c) => c.type === "import_clause");
168
- if (!clause) return false; // side-effect import executes the target module
169
- const parts = clause.namedChildren;
170
- if (parts.some((c) => c.type === "identifier" || c.type === "namespace_import")) return false;
171
- const named = parts.find((c) => c.type === "named_imports");
172
- const specs = named?.namedChildren.filter((c) => c.type === "import_specifier") || [];
173
- return specs.length > 0 && specs.every((s) => /^\s*type\b/.test(s.text));
174
- };
175
- const reexportTypeOnly = (node) => {
176
- if (/^\s*export\s+type\b/.test(node.text)) return true;
177
- const clause = node.namedChildren.find((c) => c.type === "export_clause");
178
- const specs = clause?.namedChildren.filter((c) => c.type === "export_specifier") || [];
179
- return specs.length > 0 && specs.every((s) => /^\s*type\b/.test(s.text));
180
- };
181
-
182
142
  // ---- ESM imports ----
183
143
  for (const cap of caps(grammar, `(import_statement) @imp`, tree.rootNode)) {
184
144
  const node = cap.node; const srcNode = field(node, "source");
@@ -0,0 +1,68 @@
1
+ export function createPass2Resolution({symIdsByFileName, nodeById, importedLocals, symByFileName}) {
2
+ const dirSymbols = new Map(), dirMethods = new Map(), dirMethodsByName = new Map(), dirTypes = new Map()
3
+ const sharesDirScope = (file) => file.endsWith('.go') || file.endsWith('.cs') || file.endsWith('.rs')
4
+ for (const [file, names] of symIdsByFileName) {
5
+ if (!sharesDirScope(file)) continue
6
+ const dir = file.includes('/') ? file.slice(0, file.lastIndexOf('/')) : ''
7
+ let symbols = dirSymbols.get(dir)
8
+ if (!symbols) dirSymbols.set(dir, (symbols = new Map()))
9
+ for (const [name, ids] of names) for (const id of ids) {
10
+ const node = nodeById.get(id)
11
+ if (file.endsWith('.go') && node?.member_of && node?.symbol_kind === 'method') {
12
+ let receivers = dirMethods.get(dir)
13
+ if (!receivers) dirMethods.set(dir, (receivers = new Map()))
14
+ let methods = receivers.get(node.member_of)
15
+ if (!methods) receivers.set(node.member_of, (methods = new Map()))
16
+ const exact = methods.get(name) || []
17
+ exact.push(id); methods.set(name, exact)
18
+ let byName = dirMethodsByName.get(dir)
19
+ if (!byName) dirMethodsByName.set(dir, (byName = new Map()))
20
+ const candidates = byName.get(name) || []
21
+ candidates.push(id); byName.set(name, candidates)
22
+ continue
23
+ }
24
+ if (!symbols.has(name)) symbols.set(name, id)
25
+ if (file.endsWith('.go') && node?.symbol_space === 'type') {
26
+ let types = dirTypes.get(dir)
27
+ if (!types) dirTypes.set(dir, (types = new Map()))
28
+ if (!types.has(name)) types.set(name, id)
29
+ }
30
+ }
31
+ }
32
+ const symbolSpace = (node) => {
33
+ const explicit = String(node?.symbol_space || '')
34
+ if (['value', 'type', 'both'].includes(explicit)) return explicit
35
+ const kind = String(node?.symbol_kind || '').toLowerCase()
36
+ if (['interface', 'type'].includes(kind)) return 'type'
37
+ return ['class', 'enum'].includes(kind) ? 'both' : 'value'
38
+ }
39
+ const resolveNamedSymbol = (file, name, space = 'value') => {
40
+ const ids = symIdsByFileName.get(file)?.get(name) || []
41
+ const exact = ids.find((id) => symbolSpace(nodeById.get(id)) === space)
42
+ return exact || ids.find((id) => ['both', space].includes(symbolSpace(nodeById.get(id)))) || null
43
+ }
44
+ const resolveCall = (name, file) => {
45
+ const local = resolveNamedSymbol(file, name, 'value')
46
+ if (local) return local
47
+ if (sharesDirScope(file)) {
48
+ const dir = file.includes('/') ? file.slice(0, file.lastIndexOf('/')) : ''
49
+ const symbols = dirSymbols.get(dir)
50
+ if (symbols?.has(name)) return symbols.get(name)
51
+ }
52
+ const imported = importedLocals.get(file)?.get(name)
53
+ if (!imported?.targetFile) return null
54
+ return resolveNamedSymbol(imported.originFile || imported.targetFile, imported.originName || imported.imported, 'value')
55
+ }
56
+ const javaTypeKinds = new Set(['class', 'interface', 'enum', 'record', 'annotation'])
57
+ const resolveJavaType = (name, file) => {
58
+ const imported = importedLocals.get(file)?.get(name)
59
+ if (imported?.targetFile) {
60
+ const symbols = symByFileName.get(imported.targetFile)
61
+ const target = symbols?.get(imported.imported) || symbols?.get(name)
62
+ if (target && javaTypeKinds.has(nodeById.get(target)?.symbol_kind)) return target
63
+ }
64
+ const target = symByFileName.get(file)?.get(name)
65
+ return target && javaTypeKinds.has(nodeById.get(target)?.symbol_kind) ? target : null
66
+ }
67
+ return {dirSymbols, dirMethods, dirMethodsByName, dirTypes, resolveNamedSymbol, resolveCall, resolveJavaType}
68
+ }
@@ -9,7 +9,7 @@ import {childProcessEnv} from '../child-env.js'
9
9
  import {createRepoBoundary} from '../repo-path.js'
10
10
 
11
11
  export const REPOSITORY_FRESHNESS_PROBE_V = 1
12
- export const GRAPH_BUILDER_SCHEMA_V = 5
12
+ export const GRAPH_BUILDER_SCHEMA_V = 6
13
13
  export const GRAPH_BUILDER_VERSION = (() => {
14
14
  try { return String(createRequire(import.meta.url)('../../package.json').version) }
15
15
  catch { return '0.0.0' }
@@ -23,6 +23,7 @@ const CURRENT_GRAPH_SCHEMA = Object.freeze({
23
23
  edgeTypesV: 2,
24
24
  edgeProvenanceV: 1,
25
25
  complexityV: 2,
26
+ physicalFileLocV: 1,
26
27
  repoBoundaryV: 1,
27
28
  barrelResolutionV: 1,
28
29
  reExportOccurrencesV: 1,
@@ -163,7 +163,7 @@ function mergeScopedGraph(base, scoped, affected, snapshot) {
163
163
  controlHashes: snapshot.controlHashes,
164
164
  graphRevision: snapshot.revision,
165
165
  };
166
- for (const key of ["extImportsV", "edgeTypesV", "edgeProvenanceV", "complexityV", "repoBoundaryV", "barrelResolutionV", "reExportOccurrencesV", "symbolSpacesV", "extractorSchemaV"]) {
166
+ for (const key of ["extImportsV", "edgeTypesV", "edgeProvenanceV", "complexityV", "physicalFileLocV", "repoBoundaryV", "barrelResolutionV", "reExportOccurrencesV", "symbolSpacesV", "extractorSchemaV"]) {
167
167
  merged[key] = Math.max(Number(base[key]) || 0, Number(scoped[key]) || 0);
168
168
  }
169
169
  return merged;
@@ -184,7 +184,8 @@ export async function refreshGraphIncrementally(repoDir, existingGraph, {
184
184
 
185
185
  if (!existingGraph || !existingGraph.fileHashes || !existingGraph.fileExportSignatures
186
186
  || !existingGraph.controlHashes || !existingGraph.jsExportRecords || Number(existingGraph.barrelResolutionV) < 1
187
- || Number(existingGraph.extractorSchemaV) < 5 || Number(existingGraph.reExportOccurrencesV) < 1
187
+ || Number(existingGraph.extractorSchemaV) < 5 || Number(existingGraph.physicalFileLocV) < 1
188
+ || Number(existingGraph.reExportOccurrencesV) < 1
188
189
  || Number(existingGraph.symbolSpacesV) < 1 || Number(existingGraph.edgeProvenanceV) < 1) {
189
190
  return full("incremental-baseline-unavailable");
190
191
  }