weavatrix 0.3.14 → 1.1.0
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.
- package/LICENSE +1 -0
- package/README.md +132 -200
- package/bin/native/darwin-arm64/weavatrix +0 -0
- package/bin/native/darwin-x64/weavatrix +0 -0
- package/bin/native/linux-arm64/weavatrix +0 -0
- package/bin/native/linux-x64/weavatrix +0 -0
- package/bin/native/win32-arm64/weavatrix.exe +0 -0
- package/bin/native/win32-x64/weavatrix.exe +0 -0
- package/bin/resolve-binary.mjs +73 -0
- package/bin/run-native.mjs +26 -0
- package/bin/weavatrix-mcp.mjs +7 -6
- package/bin/weavatrix.mjs +5 -0
- package/package.json +10 -67
- package/server.json +38 -0
- package/skill/SKILL.md +62 -374
- package/skill/agents/openai.yaml +4 -0
- package/skill/references/tool-routing.md +45 -0
- package/SECURITY.md +0 -101
- package/docs/adr/0001-v0.3-offline-online-split.md +0 -60
- package/docs/agent-task-benchmark.md +0 -58
- package/docs/releases/v0.2.19.md +0 -33
- package/docs/releases/v0.3.0.md +0 -43
- package/docs/releases/v0.3.1.md +0 -30
- package/docs/releases/v0.3.10.md +0 -76
- package/docs/releases/v0.3.11.md +0 -55
- package/docs/releases/v0.3.12.md +0 -44
- package/docs/releases/v0.3.13.md +0 -34
- package/docs/releases/v0.3.14.md +0 -37
- package/docs/releases/v0.3.2.md +0 -15
- package/docs/releases/v0.3.3.md +0 -25
- package/docs/releases/v0.3.4.md +0 -40
- package/docs/releases/v0.3.5.md +0 -50
- package/docs/releases/v0.3.6.md +0 -44
- package/docs/releases/v0.3.7.md +0 -37
- package/docs/releases/v0.3.8.md +0 -21
- package/docs/releases/v0.3.9.md +0 -36
- package/scripts/run-agent-task-benchmark.mjs +0 -118
- package/src/analysis/allowed-test-runner.js +0 -70
- package/src/analysis/architecture/contract-graph.js +0 -119
- package/src/analysis/architecture/contract-schema.js +0 -110
- package/src/analysis/architecture/contract-storage.js +0 -35
- package/src/analysis/architecture/contract-verification.js +0 -168
- package/src/analysis/architecture-contract.js +0 -16
- package/src/analysis/audit-debt.js +0 -94
- package/src/analysis/audit-extensions.js +0 -60
- package/src/analysis/cargo-dependency-evidence.js +0 -108
- package/src/analysis/cargo-manifests.js +0 -81
- package/src/analysis/change-classification/diff-parser.js +0 -103
- package/src/analysis/change-classification/options.js +0 -40
- package/src/analysis/change-classification/symbol-classifier.js +0 -177
- package/src/analysis/change-classification.js +0 -188
- package/src/analysis/coverage-reports-tarpaulin.js +0 -112
- package/src/analysis/coverage-reports.js +0 -246
- package/src/analysis/cycle-route.js +0 -14
- package/src/analysis/data-flow-evidence.js +0 -114
- package/src/analysis/dead-check.js +0 -300
- package/src/analysis/dead-code-review/candidates.js +0 -194
- package/src/analysis/dead-code-review/policy.js +0 -23
- package/src/analysis/dead-code-review.js +0 -119
- package/src/analysis/dep-check-ecosystems.js +0 -205
- package/src/analysis/dep-check.js +0 -290
- package/src/analysis/dep-rules.js +0 -10
- package/src/analysis/dependency/conventions.js +0 -16
- package/src/analysis/dependency/scoped-dependencies.js +0 -51
- package/src/analysis/dependency/source-references.js +0 -21
- package/src/analysis/duplicate-groups.js +0 -104
- package/src/analysis/duplicates-worker.js +0 -11
- package/src/analysis/duplicates.compute.js +0 -269
- package/src/analysis/duplicates.js +0 -15
- package/src/analysis/duplicates.run.js +0 -52
- package/src/analysis/duplicates.tokenize.js +0 -199
- package/src/analysis/endpoints/common.js +0 -79
- package/src/analysis/endpoints/extract.js +0 -120
- package/src/analysis/endpoints/inventory.js +0 -84
- package/src/analysis/endpoints/mounts.js +0 -129
- package/src/analysis/endpoints-java.js +0 -260
- package/src/analysis/endpoints-rust.js +0 -124
- package/src/analysis/endpoints.js +0 -3
- package/src/analysis/findings.js +0 -70
- package/src/analysis/git-history/analytics.js +0 -177
- package/src/analysis/git-history/collector.js +0 -67
- package/src/analysis/git-history/options.js +0 -66
- package/src/analysis/git-history.js +0 -143
- package/src/analysis/git-ref-graph.js +0 -76
- package/src/analysis/go-dependency-evidence.js +0 -62
- package/src/analysis/graph-analysis.aggregate.js +0 -296
- package/src/analysis/graph-analysis.edges.js +0 -24
- package/src/analysis/graph-analysis.js +0 -7
- package/src/analysis/graph-analysis.refs.js +0 -202
- package/src/analysis/graph-analysis.summaries.js +0 -65
- package/src/analysis/health-capabilities.js +0 -83
- package/src/analysis/hot-path-review.js +0 -266
- package/src/analysis/http-contract-wrappers.js +0 -241
- package/src/analysis/http-contracts/analysis.js +0 -179
- package/src/analysis/http-contracts/client-call-detection.js +0 -82
- package/src/analysis/http-contracts/client-call-parser.js +0 -273
- package/src/analysis/http-contracts/graph-context.js +0 -143
- package/src/analysis/http-contracts/matching.js +0 -61
- package/src/analysis/http-contracts/shared.js +0 -85
- package/src/analysis/http-contracts.js +0 -6
- package/src/analysis/internal-audit/dependency-health.js +0 -113
- package/src/analysis/internal-audit/python-manifests.js +0 -78
- package/src/analysis/internal-audit/repo-files.js +0 -50
- package/src/analysis/internal-audit/supply-chain.js +0 -149
- package/src/analysis/internal-audit.collect.js +0 -213
- package/src/analysis/internal-audit.js +0 -9
- package/src/analysis/internal-audit.reach.js +0 -201
- package/src/analysis/internal-audit.run.js +0 -256
- package/src/analysis/java-source.js +0 -36
- package/src/analysis/jvm-artifact-index.js +0 -87
- package/src/analysis/jvm-dependency-evidence.js +0 -138
- package/src/analysis/jvm-manifests.js +0 -114
- package/src/analysis/manifests.js +0 -123
- package/src/analysis/package-reachability.js +0 -39
- package/src/analysis/source-complexity.ast.js +0 -97
- package/src/analysis/source-complexity.constants.js +0 -55
- package/src/analysis/source-complexity.js +0 -14
- package/src/analysis/source-complexity.report.js +0 -81
- package/src/analysis/source-complexity.walk.js +0 -228
- package/src/analysis/source-correctness/retry-patterns.js +0 -93
- package/src/analysis/source-correctness.js +0 -222
- package/src/analysis/static-test-reachability.js +0 -150
- package/src/analysis/structure/dependency-graph.js +0 -151
- package/src/analysis/structure/findings.js +0 -162
- package/src/analysis/task-retrieval.js +0 -107
- package/src/analysis/transport-contracts.js +0 -224
- package/src/analysis/transport-runtime-evidence.js +0 -155
- package/src/analysis-kit.mjs +0 -17
- package/src/bounds.js +0 -4
- package/src/build-graph.js +0 -209
- package/src/child-env.js +0 -7
- package/src/config.js +0 -5
- package/src/extension/local-services.mjs +0 -75
- package/src/git-exec.js +0 -67
- package/src/graph/build-worker.js +0 -49
- package/src/graph/builder/go-receiver-resolution.js +0 -112
- package/src/graph/builder/js/dynamic-import.js +0 -26
- package/src/graph/builder/js/queries.js +0 -48
- package/src/graph/builder/lang-csharp.js +0 -40
- package/src/graph/builder/lang-css.js +0 -29
- package/src/graph/builder/lang-go.js +0 -140
- package/src/graph/builder/lang-html.js +0 -29
- package/src/graph/builder/lang-java-calls.js +0 -142
- package/src/graph/builder/lang-java.js +0 -215
- package/src/graph/builder/lang-js.js +0 -291
- package/src/graph/builder/lang-python.js +0 -235
- package/src/graph/builder/lang-rust-calls.js +0 -63
- package/src/graph/builder/lang-rust.js +0 -299
- package/src/graph/builder/lang-solidity.js +0 -126
- package/src/graph/builder/lang-sql.js +0 -263
- package/src/graph/builder/pass2-resolution.js +0 -81
- package/src/graph/builder/spec-pkg.js +0 -87
- package/src/graph/community.js +0 -27
- package/src/graph/edge-provenance.js +0 -31
- package/src/graph/file-lock.js +0 -69
- package/src/graph/freshness-probe.js +0 -142
- package/src/graph/graph-filter.js +0 -63
- package/src/graph/incremental-refresh.js +0 -235
- package/src/graph/internal-builder.barrels.js +0 -235
- package/src/graph/internal-builder.build.js +0 -300
- package/src/graph/internal-builder.java.js +0 -33
- package/src/graph/internal-builder.js +0 -12
- package/src/graph/internal-builder.langs.js +0 -157
- package/src/graph/internal-builder.pass2.js +0 -163
- package/src/graph/internal-builder.resolvers.js +0 -286
- package/src/graph/layout.js +0 -51
- package/src/graph/node-id.js +0 -7
- package/src/graph/parser-artifact-boundary.js +0 -55
- package/src/graph/relations.js +0 -4
- package/src/graph/repo-registry.js +0 -124
- package/src/graph/resolvers/rust.js +0 -117
- package/src/infra/infra-items.js +0 -255
- package/src/infra/infra-registry.js +0 -184
- package/src/infra/infra.detect.js +0 -119
- package/src/infra/infra.js +0 -38
- package/src/infra/infra.match.js +0 -102
- package/src/infra/infra.scan.js +0 -93
- package/src/mcp/actions/graph-lifecycle.mjs +0 -148
- package/src/mcp/architecture-bootstrap.mjs +0 -168
- package/src/mcp/architecture-starter.mjs +0 -234
- package/src/mcp/catalog.mjs +0 -233
- package/src/mcp/company-contract-page.mjs +0 -142
- package/src/mcp/company-contract-reconcile.mjs +0 -87
- package/src/mcp/company-contract-verdict.mjs +0 -42
- package/src/mcp/evidence/bun-lock-graph.mjs +0 -209
- package/src/mcp/evidence/duplicate-member-order.mjs +0 -8
- package/src/mcp/evidence/package-graph-common.mjs +0 -115
- package/src/mcp/evidence/package-lock-graph.mjs +0 -92
- package/src/mcp/evidence-snapshot.architecture.mjs +0 -80
- package/src/mcp/evidence-snapshot.common.mjs +0 -158
- package/src/mcp/evidence-snapshot.duplicates.mjs +0 -213
- package/src/mcp/evidence-snapshot.health.mjs +0 -102
- package/src/mcp/evidence-snapshot.inventory.mjs +0 -148
- package/src/mcp/evidence-snapshot.mjs +0 -50
- package/src/mcp/evidence-snapshot.package-graph.mjs +0 -34
- package/src/mcp/evidence-snapshot.structure.mjs +0 -84
- package/src/mcp/extension-api.mjs +0 -77
- package/src/mcp/git-output.mjs +0 -7
- package/src/mcp/graph/context-core.mjs +0 -111
- package/src/mcp/graph/context-seeds.mjs +0 -207
- package/src/mcp/graph/context-state.mjs +0 -85
- package/src/mcp/graph/reverse-reach.mjs +0 -42
- package/src/mcp/graph/tools-core.mjs +0 -143
- package/src/mcp/graph/tools-query.mjs +0 -276
- package/src/mcp/graph-context.mjs +0 -5
- package/src/mcp/graph-diff.mjs +0 -218
- package/src/mcp/health/audit-format.mjs +0 -199
- package/src/mcp/health/audit.mjs +0 -199
- package/src/mcp/health/dead-code.mjs +0 -110
- package/src/mcp/health/duplicates.mjs +0 -121
- package/src/mcp/health/endpoints.mjs +0 -71
- package/src/mcp/health/structure.mjs +0 -220
- package/src/mcp/runtime-version.mjs +0 -31
- package/src/mcp/server/auto-refresh.mjs +0 -66
- package/src/mcp/server/runtime-config.mjs +0 -43
- package/src/mcp/server/shutdown.mjs +0 -40
- package/src/mcp/staleness-notice.mjs +0 -20
- package/src/mcp/sync/evidence-architecture.mjs +0 -54
- package/src/mcp/sync/evidence-common.mjs +0 -88
- package/src/mcp/sync/evidence-duplicates.mjs +0 -97
- package/src/mcp/sync/evidence-health.mjs +0 -56
- package/src/mcp/sync/evidence-packages.mjs +0 -95
- package/src/mcp/sync/payload-common.mjs +0 -97
- package/src/mcp/sync/payload-v2.mjs +0 -54
- package/src/mcp/sync/payload-v3.mjs +0 -79
- package/src/mcp/sync-evidence.mjs +0 -23
- package/src/mcp/sync-payload.mjs +0 -70
- package/src/mcp/tool-result.mjs +0 -54
- package/src/mcp/tools-actions.mjs +0 -11
- package/src/mcp/tools-architecture.mjs +0 -177
- package/src/mcp/tools-company.mjs +0 -251
- package/src/mcp/tools-context.mjs +0 -222
- package/src/mcp/tools-endpoints.mjs +0 -207
- package/src/mcp/tools-graph-hubs.mjs +0 -78
- package/src/mcp/tools-graph.mjs +0 -17
- package/src/mcp/tools-health.mjs +0 -24
- package/src/mcp/tools-history.mjs +0 -28
- package/src/mcp/tools-impact-change.mjs +0 -220
- package/src/mcp/tools-impact-precision.mjs +0 -89
- package/src/mcp/tools-impact.mjs +0 -169
- package/src/mcp/tools-source.mjs +0 -238
- package/src/mcp/tools-verified-change.mjs +0 -200
- package/src/mcp-rg.mjs +0 -50
- package/src/mcp-runtime.mjs +0 -2
- package/src/mcp-server.mjs +0 -298
- package/src/mcp-source-tools.mjs +0 -163
- package/src/path-classification.js +0 -226
- package/src/path-ignore.js +0 -69
- package/src/precision/lsp-client/constants.js +0 -12
- package/src/precision/lsp-client/environment.js +0 -20
- package/src/precision/lsp-client/errors.js +0 -15
- package/src/precision/lsp-client/lifecycle.js +0 -114
- package/src/precision/lsp-client/message-parser.js +0 -81
- package/src/precision/lsp-client/protocol.js +0 -212
- package/src/precision/lsp-client/registry.js +0 -59
- package/src/precision/lsp-client/repo-uri.js +0 -60
- package/src/precision/lsp-client/stdio-client.js +0 -140
- package/src/precision/lsp-client.js +0 -10
- package/src/precision/lsp-overlay/budget-policy.js +0 -78
- package/src/precision/lsp-overlay/build.js +0 -261
- package/src/precision/lsp-overlay/contract.js +0 -61
- package/src/precision/lsp-overlay/reference-results.js +0 -108
- package/src/precision/lsp-overlay/semantic-inputs.js +0 -62
- package/src/precision/lsp-overlay/source-session.js +0 -142
- package/src/precision/lsp-overlay/store.js +0 -150
- package/src/precision/lsp-overlay/target-index.js +0 -152
- package/src/precision/lsp-overlay/target-query.js +0 -55
- package/src/precision/lsp-overlay.js +0 -11
- package/src/precision/symbol-query.js +0 -224
- package/src/precision/typescript-lsp-provider.js +0 -12
- package/src/precision/typescript-provider/client.js +0 -89
- package/src/precision/typescript-provider/discovery.js +0 -124
- package/src/precision/typescript-provider/isolated-runtime.js +0 -39
- package/src/precision/typescript-provider/project-host.js +0 -254
- package/src/precision/typescript-provider/project-safety.js +0 -166
- package/src/precision/typescript-provider/reference-usage.js +0 -53
- package/src/process.js +0 -95
- package/src/repo-path.js +0 -53
- package/src/scan/discover.inventory.js +0 -78
- package/src/scan/discover.js +0 -5
- package/src/scan/discover.list.js +0 -79
- package/src/scan/discover.stack.js +0 -227
- package/src/scan/search.core.js +0 -24
- package/src/scan/search.git.js +0 -102
- package/src/scan/search.js +0 -9
- package/src/scan/search.node.js +0 -83
- package/src/scan/search.preview.js +0 -49
- package/src/scan/search.rg.js +0 -145
- package/src/security/advisory-store.js +0 -172
- package/src/security/installed-jvm-rust.js +0 -46
- package/src/security/installed.js +0 -280
- package/src/security/malware-file-heuristics.js +0 -121
- package/src/security/malware-heuristics.exclusions.js +0 -134
- package/src/security/malware-heuristics.js +0 -15
- package/src/security/malware-heuristics.roots.js +0 -145
- package/src/security/malware-heuristics.scan.js +0 -89
- package/src/security/malware-heuristics.sweep.js +0 -122
- package/src/security/malware-scoring.js +0 -105
- package/src/security/match.js +0 -85
- package/src/security/registry-sig.classify.js +0 -137
- package/src/security/registry-sig.js +0 -18
- package/src/security/registry-sig.rules.js +0 -192
- package/src/security/rust-advisory-report.js +0 -60
- package/src/security/typosquat.js +0 -81
- package/src/util.js +0 -40
- package/src/version.js +0 -7
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import {isStructuralRelation} from '../graph/relations.js'
|
|
2
|
-
import {querySymbolsPrecision} from '../precision/symbol-query.js'
|
|
3
|
-
|
|
4
|
-
export function graphWithExactDirectReferences(g, targetId, overlay) {
|
|
5
|
-
const exact = (overlay?.links || []).filter((link) => String(link?.target || '') === targetId
|
|
6
|
-
&& link.provenance === 'EXACT_LSP')
|
|
7
|
-
const provenEmpty = overlay?.state === 'COMPLETE'
|
|
8
|
-
&& (overlay?.noReferenceSymbols || []).some((id) => String(id) === targetId)
|
|
9
|
-
if (overlay?.state !== 'COMPLETE' || (!exact.length && !provenEmpty)) return null
|
|
10
|
-
const inn = new Map(g.inn)
|
|
11
|
-
const structural = (g.inn.get(targetId) || []).filter((edge) => isStructuralRelation(edge.relation))
|
|
12
|
-
inn.set(targetId, [...structural, ...exact.map((link) => ({
|
|
13
|
-
id: String(link.source),
|
|
14
|
-
relation: link.relation || 'references',
|
|
15
|
-
provenance: 'EXACT_LSP',
|
|
16
|
-
...(link.typeOnly === true ? {typeOnly: true} : {}),
|
|
17
|
-
...(link.compileOnly === true ? {compileOnly: true} : {}),
|
|
18
|
-
}))])
|
|
19
|
-
return {...g, inn}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function refineChangeImpact(g, args, ctx, baseline, computeImpact) {
|
|
23
|
-
const requested = ['auto', 'graph', 'lsp'].includes(args?.precision) ? args.precision : 'auto'
|
|
24
|
-
if (requested === 'graph' || !baseline?.result?.seeds?.ids || !ctx?.repoRoot || !ctx?.graphPath
|
|
25
|
-
|| (requested === 'auto' && g.graphPrecisionMode === 'off')) return baseline
|
|
26
|
-
const targets = baseline.result.seeds.ids.filter((id) => {
|
|
27
|
-
const node = g.byId.get(String(id))
|
|
28
|
-
return node?.selection_start && /\.(?:[cm]?[jt]sx?)$/i.test(String(node.source_file || ''))
|
|
29
|
-
}).slice(0, 16)
|
|
30
|
-
if (!targets.length) return baseline
|
|
31
|
-
return (async () => {
|
|
32
|
-
try {
|
|
33
|
-
const precision = await querySymbolsPrecision({
|
|
34
|
-
repoRoot: ctx.repoRoot,
|
|
35
|
-
graphPath: ctx.graphPath,
|
|
36
|
-
targetIds: targets,
|
|
37
|
-
maxReferences: Math.max(100, Math.min(16_384, Number(args.max_references) || 5_000)),
|
|
38
|
-
timeoutMs: Math.max(1_000, Math.min(60_000, Number(args.timeout_ms) || 45_000)),
|
|
39
|
-
clientFactory: ctx.precisionClientFactory,
|
|
40
|
-
})
|
|
41
|
-
let exactGraph = g
|
|
42
|
-
const verified = []
|
|
43
|
-
for (const target of targets) {
|
|
44
|
-
const next = graphWithExactDirectReferences(exactGraph, String(target), precision.overlay)
|
|
45
|
-
if (!next) continue
|
|
46
|
-
exactGraph = next
|
|
47
|
-
verified.push(String(target))
|
|
48
|
-
}
|
|
49
|
-
const value = verified.length ? computeImpact(exactGraph, args, ctx) : baseline
|
|
50
|
-
const allVerified = verified.length === targets.length
|
|
51
|
-
const status = allVerified ? 'DIRECT_EXACT_TRANSITIVE_GRAPH' : 'PARTIAL'
|
|
52
|
-
value.text = [
|
|
53
|
-
value.text.split('\n')[0],
|
|
54
|
-
`Semantic precision: ${status}; EXACT_LSP verified direct references for ${verified.length}/${targets.length} changed JavaScript/TypeScript symbol(s). Transitive hops remain graph-backed.`,
|
|
55
|
-
...value.text.split('\n').slice(1),
|
|
56
|
-
].join('\n')
|
|
57
|
-
value.result.semanticPrecision = {
|
|
58
|
-
status,
|
|
59
|
-
requestedTargets: targets,
|
|
60
|
-
verifiedTargets: verified,
|
|
61
|
-
exactDirectEdges: (precision.overlay?.links || []).filter((link) => verified.includes(String(link?.target || ''))).length,
|
|
62
|
-
transitiveEvidence: 'GRAPH',
|
|
63
|
-
provider: precision.overlay?.engines?.[0]?.provider || null,
|
|
64
|
-
elapsedMs: precision.elapsedMs,
|
|
65
|
-
}
|
|
66
|
-
if (!allVerified) value.warnings.push({
|
|
67
|
-
code: 'CHANGE_IMPACT_PRECISION_PARTIAL',
|
|
68
|
-
message: precision.overlay?.reason || 'Not every changed symbol had complete exact direct-reference evidence.',
|
|
69
|
-
})
|
|
70
|
-
return value
|
|
71
|
-
} catch (error) {
|
|
72
|
-
const reason = error?.message || 'batch point query failed'
|
|
73
|
-
baseline.result.semanticPrecision = {
|
|
74
|
-
status: 'UNAVAILABLE', requestedTargets: targets, verifiedTargets: [],
|
|
75
|
-
exactDirectEdges: 0, transitiveEvidence: 'GRAPH', reason,
|
|
76
|
-
}
|
|
77
|
-
baseline.warnings.push({
|
|
78
|
-
code: 'CHANGE_IMPACT_PRECISION_UNAVAILABLE',
|
|
79
|
-
message: `Exact changed-symbol references were unavailable; graph evidence was retained (${reason}).`,
|
|
80
|
-
})
|
|
81
|
-
baseline.text = [
|
|
82
|
-
baseline.text.split('\n')[0],
|
|
83
|
-
`Semantic precision: UNAVAILABLE; graph evidence retained (${reason}).`,
|
|
84
|
-
...baseline.text.split('\n').slice(1),
|
|
85
|
-
].join('\n')
|
|
86
|
-
return baseline
|
|
87
|
-
}
|
|
88
|
-
})()
|
|
89
|
-
}
|
package/src/mcp/tools-impact.mjs
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
// Impact tools: transitive blast radius of one node (get_dependents), the structural diff of the
|
|
2
|
-
// last rebuild (graph_diff), and the blast radius of the current change set (change_impact).
|
|
3
|
-
// Hot-reloadable (re-imported by catalog.mjs on change).
|
|
4
|
-
import {readFileSync} from 'node:fs'
|
|
5
|
-
import {
|
|
6
|
-
isSymbol, degreeOf, labelOf, resolveNodeInfo, ambiguityNote,
|
|
7
|
-
rawGraph, prevGraphPathFor, edgeEndpoint, diffGraphs, formatGraphDiff,
|
|
8
|
-
} from './graph-context.mjs'
|
|
9
|
-
import {querySymbolPrecision} from '../precision/symbol-query.js'
|
|
10
|
-
import {graphWithExactDirectReferences, refineChangeImpact} from './tools-impact-precision.mjs'
|
|
11
|
-
import {reverseReach} from './graph/reverse-reach.mjs'
|
|
12
|
-
import {tChangeImpactV2} from './tools-impact-change.mjs'
|
|
13
|
-
import {buildGraphAtGitRef} from '../analysis/git-ref-graph.js'
|
|
14
|
-
|
|
15
|
-
const impactKind = (entry) => {
|
|
16
|
-
if (entry?.runtimeDepth != null) {
|
|
17
|
-
return entry.compileDepth != null ? `runtime + compile-time(d${entry.compileDepth})` : 'runtime'
|
|
18
|
-
}
|
|
19
|
-
return 'compile-time'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Transitive blast-radius: who is affected if this node changes. Walks REVERSE dependency edges
|
|
23
|
-
// (calls/imports/inherits — not structural `contains`) out to `depth`. For a symbol, also seeds its
|
|
24
|
-
// containing file, because importers depend on the file rather than the individual symbol.
|
|
25
|
-
function getDependentsFromGraph(g, {label, depth = 3, max_nodes = 40, include_container_importers = false} = {}, precisionNote = null) {
|
|
26
|
-
const info = resolveNodeInfo(g, label)
|
|
27
|
-
const n = info.node
|
|
28
|
-
if (!n) return `No node found matching "${label}".`
|
|
29
|
-
const note = ambiguityNote(label, info)
|
|
30
|
-
const maxDepth = Math.max(1, Math.min(6, Number(depth) || 3))
|
|
31
|
-
const cap = Math.max(5, Math.min(120, Number(max_nodes) || 40))
|
|
32
|
-
const id = String(n.id)
|
|
33
|
-
const seeds = new Set([id])
|
|
34
|
-
let containingFile = null
|
|
35
|
-
if (include_container_importers === true && isSymbol(id)) {
|
|
36
|
-
const container = (g.inn.get(id) || []).find((e) => e.relation === 'contains')
|
|
37
|
-
if (container) {
|
|
38
|
-
containingFile = String(container.id)
|
|
39
|
-
seeds.add(containingFile)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
const reached = reverseReach(g, seeds, maxDepth)
|
|
43
|
-
const ranked = [...reached.entries()]
|
|
44
|
-
.filter(([nid]) => !seeds.has(nid))
|
|
45
|
-
.map(([nid, entry]) => ({id: nid, d: entry.depth, entry, deg: degreeOf(g, nid)}))
|
|
46
|
-
.sort((a, b) => a.d - b.d || Number(a.entry.compileOnly) - Number(b.entry.compileOnly) || b.deg - a.deg)
|
|
47
|
-
if (!ranked.length) return [note, precisionNote, `No dependents found for ${n.label ?? id} within depth ${maxDepth} — nothing in the graph calls, imports, or inherits it.`].filter(Boolean).join('\n')
|
|
48
|
-
const shown = ranked.slice(0, cap)
|
|
49
|
-
const runtimeCount = ranked.filter((entry) => !entry.entry.compileOnly).length
|
|
50
|
-
const compileCount = ranked.length - runtimeCount
|
|
51
|
-
return [
|
|
52
|
-
note,
|
|
53
|
-
precisionNote,
|
|
54
|
-
`Dependents of ${n.label ?? id} (reverse calls/imports/inherits, depth ≤${maxDepth}): ${ranked.length} found (${runtimeCount} runtime, ${compileCount} compile-time-only), showing ${shown.length} by proximity + connectivity.`,
|
|
55
|
-
containingFile ? `Includes importers of its containing file ${labelOf(g, containingFile)} by explicit request.` : null,
|
|
56
|
-
...shown.map((r) => ` [d${r.d} ${impactKind(r.entry)}] ${r.entry.relation || 'rel'} [${r.entry.provenance || 'UNKNOWN'}] ${labelOf(g, r.id)} (deg ${r.deg}) [${r.id}]`),
|
|
57
|
-
].filter(Boolean).join('\n')
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function tGetDependents(g, args = {}, ctx = {}) {
|
|
61
|
-
const info = resolveNodeInfo(g, args.label)
|
|
62
|
-
const id = String(info.node?.id || '')
|
|
63
|
-
const requested = ['auto', 'graph', 'lsp'].includes(args.precision) ? args.precision : 'auto'
|
|
64
|
-
const canQuery = isSymbol(id) && ctx.repoRoot && ctx.graphPath
|
|
65
|
-
&& (requested === 'lsp' || (requested === 'auto' && g.graphPrecisionMode !== 'off'))
|
|
66
|
-
if (!canQuery) {
|
|
67
|
-
const note = isSymbol(id) && requested !== 'graph'
|
|
68
|
-
? 'Semantic precision: graph-only; an exact point query was unavailable or disabled.' : null
|
|
69
|
-
return getDependentsFromGraph(g, args, note)
|
|
70
|
-
}
|
|
71
|
-
return (async () => {
|
|
72
|
-
try {
|
|
73
|
-
const result = await querySymbolPrecision({
|
|
74
|
-
repoRoot: ctx.repoRoot,
|
|
75
|
-
graphPath: ctx.graphPath,
|
|
76
|
-
targetId: id,
|
|
77
|
-
maxReferences: Math.max(100, Math.min(5_000, Number(args.max_references) || 1_000)),
|
|
78
|
-
timeoutMs: Math.max(1_000, Math.min(60_000, Number(args.timeout_ms) || 30_000)),
|
|
79
|
-
clientFactory: ctx.precisionClientFactory,
|
|
80
|
-
})
|
|
81
|
-
const exactGraph = graphWithExactDirectReferences(g, id, result.overlay)
|
|
82
|
-
if (exactGraph) {
|
|
83
|
-
const count = (result.overlay.links || []).filter((link) => String(link?.target || '') === id).length
|
|
84
|
-
return getDependentsFromGraph(
|
|
85
|
-
exactGraph,
|
|
86
|
-
args,
|
|
87
|
-
`Semantic precision: EXACT_LSP point query${result.cached ? ' (cache hit)' : ''}; ${count} classified direct reference edge(s).`,
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
|
-
// Never echo COMPLETE here ('COMPLETE; exact absence was not proven' is a contradiction),
|
|
91
|
-
// but keep NONE/UNAVAILABLE truthful — PARTIAL implies probing actually began.
|
|
92
|
-
const fallbackState = result.overlay?.state === 'NONE' ? 'NONE'
|
|
93
|
-
: result.overlay?.state === 'UNAVAILABLE' ? 'UNAVAILABLE' : 'PARTIAL'
|
|
94
|
-
return getDependentsFromGraph(
|
|
95
|
-
g,
|
|
96
|
-
args,
|
|
97
|
-
fallbackState === 'NONE'
|
|
98
|
-
? `Semantic precision: NONE; exact references are not available for this language/target, so graph edges are retained (${result.overlay?.reason || 'no eligible JavaScript/TypeScript semantic targets'}).`
|
|
99
|
-
: fallbackState === 'UNAVAILABLE'
|
|
100
|
-
? `Semantic precision: UNAVAILABLE; graph evidence retained (${result.overlay?.reason || 'semantic point query unavailable'}).`
|
|
101
|
-
: `Semantic precision: PARTIAL; exact absence was not proven, so graph edges are retained (${result.overlay?.reason || 'incomplete project coverage'}).`,
|
|
102
|
-
)
|
|
103
|
-
} catch (error) {
|
|
104
|
-
return getDependentsFromGraph(
|
|
105
|
-
g,
|
|
106
|
-
args,
|
|
107
|
-
`Semantic precision: UNAVAILABLE; graph evidence retained (${error?.message || 'point query failed'}).`,
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
})()
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Re-query the last rebuild's before/after pair (graph.prev.json vs graph.json), optionally scoped.
|
|
114
|
-
export async function tGraphDiff(g, args, ctx) {
|
|
115
|
-
const current = rawGraph(ctx)
|
|
116
|
-
const currentMode = ['full', 'no-tests', 'tests-only'].includes(current?.graphBuildMode)
|
|
117
|
-
? current.graphBuildMode
|
|
118
|
-
: 'full'
|
|
119
|
-
let prev
|
|
120
|
-
let baselineLabel = 'previous rebuild state'
|
|
121
|
-
if (args.base_ref) {
|
|
122
|
-
if (!ctx.repoRoot) return 'A Git-ref graph diff needs the active repository root.'
|
|
123
|
-
const built = await buildGraphAtGitRef(ctx.repoRoot, args.base_ref, {mode: currentMode})
|
|
124
|
-
if (!built.ok) return `Could not build the baseline graph: ${built.error}`
|
|
125
|
-
prev = built.graph
|
|
126
|
-
baselineLabel = `${built.ref} (${built.commit.slice(0, 12)})`
|
|
127
|
-
} else {
|
|
128
|
-
const prevPath = prevGraphPathFor(ctx.graphPath)
|
|
129
|
-
try { prev = JSON.parse(readFileSync(prevPath, 'utf8')) } catch {
|
|
130
|
-
return `No previous graph state at ${prevPath} — pass base_ref (for example HEAD~1 or main), or run rebuild_graph to save one automatically.`
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (!args.base_ref) {
|
|
134
|
-
const previousMode = ['full', 'no-tests', 'tests-only'].includes(prev?.graphBuildMode)
|
|
135
|
-
? prev.graphBuildMode
|
|
136
|
-
: 'full'
|
|
137
|
-
if (previousMode !== currentMode) {
|
|
138
|
-
return [
|
|
139
|
-
`Graph diff unavailable: previous graph mode is ${previousMode}, current graph mode is ${currentMode}.`,
|
|
140
|
-
'Those node/edge universes are not comparable. Run rebuild_graph once more in the same mode, or pass base_ref to build a mode-matched immutable baseline.',
|
|
141
|
-
].join('\n')
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
const filter = args.path ? String(args.path).replace(/\\/g, '/') : null
|
|
145
|
-
const scope = (graph) => filter ? {
|
|
146
|
-
nodes: (graph.nodes || []).filter((n) => String(n.id).startsWith(filter)),
|
|
147
|
-
links: (graph.links || []).filter((l) => String(edgeEndpoint(l.source)).startsWith(filter) || String(edgeEndpoint(l.target)).startsWith(filter)),
|
|
148
|
-
edgeTypesV: graph.edgeTypesV || 0,
|
|
149
|
-
edgeProvenanceV: graph.edgeProvenanceV || 0,
|
|
150
|
-
barrelResolutionV: graph.barrelResolutionV || 0,
|
|
151
|
-
extractorSchemaV: graph.extractorSchemaV || 0,
|
|
152
|
-
} : graph
|
|
153
|
-
return [
|
|
154
|
-
`Graph diff (${baselineLabel} → current)${filter ? `, scoped to ${filter}` : ''}:`,
|
|
155
|
-
`Build mode: ${currentMode}`,
|
|
156
|
-
formatGraphDiff(diffGraphs(scope(prev), scope(current)))
|
|
157
|
-
].join('\n')
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ---- change impact -------------------------------------------------------------------------------
|
|
161
|
-
// Blast radius of a change, without any GitHub API: diff the CURRENT change (branch
|
|
162
|
-
// commits since the merge-base + staged/unstaged + untracked) against a base ref, map the changed
|
|
163
|
-
// files and their symbols onto the graph, and walk REVERSE dependency edges — everything the change
|
|
164
|
-
// can break, ranked by proximity + connectivity, with file-level test coverage attached so the
|
|
165
|
-
// untested part of the blast radius stands out. The pre-PR review, in one call.
|
|
166
|
-
export function tChangeImpact(g, args, ctx) {
|
|
167
|
-
const baseline = tChangeImpactV2(g, args, ctx)
|
|
168
|
-
return refineChangeImpact(g, args, ctx, baseline, tChangeImpactV2)
|
|
169
|
-
}
|
package/src/mcp/tools-source.mjs
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import {readFileSync, statSync} from 'node:fs'
|
|
2
|
-
import {boundedInteger} from '../bounds.js'
|
|
3
|
-
import {resolveRepoPath} from '../repo-path.js'
|
|
4
|
-
import {isStructuralRelation} from '../graph/relations.js'
|
|
5
|
-
import {querySymbolPrecision} from '../precision/symbol-query.js'
|
|
6
|
-
import {toolResult} from './tool-result.mjs'
|
|
7
|
-
import {degreeOf, isSymbol, labelOf, resolveNodeInfo} from './graph-context.mjs'
|
|
8
|
-
|
|
9
|
-
const MAX_EXCERPT_CHARS = 4_000
|
|
10
|
-
const MAX_LOCATION_SAMPLES = 5
|
|
11
|
-
const MAX_GRAPH_OCCURRENCES = 100
|
|
12
|
-
|
|
13
|
-
const sourceLine = (node) => {
|
|
14
|
-
const match = /L(\d+)/.exec(String(node?.source_location || ''))
|
|
15
|
-
return match ? Number(match[1]) : 1
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function candidateNodes(g, query, limit = 20) {
|
|
19
|
-
const text = String(query || '').trim().toLowerCase()
|
|
20
|
-
if (!text) return []
|
|
21
|
-
const exact = g.byLabel.get(text)
|
|
22
|
-
const nodes = exact?.length ? exact : g.nodes.filter((node) =>
|
|
23
|
-
String(node.id).toLowerCase().includes(text) || String(node.label || '').toLowerCase().includes(text))
|
|
24
|
-
return nodes.slice(0, limit).map((node) => ({
|
|
25
|
-
id: String(node.id), label: String(node.label || node.id), file: node.source_file || null,
|
|
26
|
-
...(node.symbol_space ? {space: node.symbol_space} : {}),
|
|
27
|
-
}))
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function sourceExcerpt(repoRoot, file, focusLine, contextLines) {
|
|
31
|
-
const resolved = resolveRepoPath(repoRoot, file)
|
|
32
|
-
if (!resolved.ok) return null
|
|
33
|
-
let size
|
|
34
|
-
try { size = statSync(resolved.path).size } catch { return null }
|
|
35
|
-
if (size > 2 * 1024 * 1024) return null
|
|
36
|
-
let lines
|
|
37
|
-
try { lines = readFileSync(resolved.path, 'utf8').split(/\r?\n/) } catch { return null }
|
|
38
|
-
const focus = Math.max(1, Math.min(lines.length, Number(focusLine) || 1))
|
|
39
|
-
const startLine = Math.max(1, focus - contextLines)
|
|
40
|
-
const endLine = Math.min(lines.length, focus + contextLines)
|
|
41
|
-
const rawText = lines.slice(startLine - 1, endLine).join('\n')
|
|
42
|
-
return {
|
|
43
|
-
file,
|
|
44
|
-
focusLine: focus,
|
|
45
|
-
startLine,
|
|
46
|
-
endLine,
|
|
47
|
-
text: rawText.length > MAX_EXCERPT_CHARS ? `${rawText.slice(0, MAX_EXCERPT_CHARS)}\n… [excerpt truncated]` : rawText,
|
|
48
|
-
truncated: rawText.length > MAX_EXCERPT_CHARS,
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function reverseImpact(g, targetId, depth = 3, cap = 40) {
|
|
53
|
-
const seen = new Map([[targetId, 0]])
|
|
54
|
-
const queue = [targetId]
|
|
55
|
-
for (let cursor = 0; cursor < queue.length; cursor++) {
|
|
56
|
-
const current = queue[cursor]
|
|
57
|
-
const currentDepth = seen.get(current)
|
|
58
|
-
if (currentDepth >= depth) continue
|
|
59
|
-
for (const edge of g.inn.get(current) || []) {
|
|
60
|
-
if (isStructuralRelation(edge.relation) || edge.barrelProxy === true) continue
|
|
61
|
-
const id = String(edge.id)
|
|
62
|
-
if (seen.has(id)) continue
|
|
63
|
-
seen.set(id, currentDepth + 1)
|
|
64
|
-
queue.push(id)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return [...seen.entries()]
|
|
68
|
-
.filter(([id]) => id !== targetId)
|
|
69
|
-
.map(([id, distance]) => ({id, label: labelOf(g, id), distance, degree: degreeOf(g, id)}))
|
|
70
|
-
.sort((left, right) => left.distance - right.distance || right.degree - left.degree)
|
|
71
|
-
.slice(0, cap)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function graphOccurrences(g, targetId) {
|
|
75
|
-
const all = (g.inn.get(targetId) || [])
|
|
76
|
-
.filter((edge) => !isStructuralRelation(edge.relation) && edge.barrelProxy !== true)
|
|
77
|
-
.map((edge) => ({
|
|
78
|
-
source: String(edge.id),
|
|
79
|
-
label: labelOf(g, edge.id),
|
|
80
|
-
relation: edge.relation || 'references',
|
|
81
|
-
provenance: edge.provenance || 'UNKNOWN',
|
|
82
|
-
...(Number.isInteger(edge.line) ? {line: edge.line} : {}),
|
|
83
|
-
...(edge.typeOnly === true ? {typeOnly: true} : {}),
|
|
84
|
-
...(edge.compileOnly === true ? {compileOnly: true} : {}),
|
|
85
|
-
}))
|
|
86
|
-
return {total: all.length, shown: all.slice(0, MAX_GRAPH_OCCURRENCES)}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function groupLocations(g, locations, cap) {
|
|
90
|
-
const groups = new Map()
|
|
91
|
-
for (const location of locations) {
|
|
92
|
-
const source = String(location.source || location.file || '')
|
|
93
|
-
if (!source) continue
|
|
94
|
-
const node = g.byId.get(source)
|
|
95
|
-
const file = String(location.file || node?.source_file || (isSymbol(source) ? source.split('#')[0] : source))
|
|
96
|
-
let group = groups.get(source)
|
|
97
|
-
if (!group) {
|
|
98
|
-
group = {id: source, label: String(node?.label || source), file, count: 0, classifications: {}, locations: []}
|
|
99
|
-
groups.set(source, group)
|
|
100
|
-
}
|
|
101
|
-
group.count++
|
|
102
|
-
const classification = String(location.classification || 'unknown')
|
|
103
|
-
group.classifications[classification] = (group.classifications[classification] || 0) + 1
|
|
104
|
-
if (group.locations.length < MAX_LOCATION_SAMPLES) group.locations.push({
|
|
105
|
-
line: location.line,
|
|
106
|
-
character: location.character,
|
|
107
|
-
...(Number.isInteger(location.endLine) ? {endLine: location.endLine} : {}),
|
|
108
|
-
...(Number.isInteger(location.endCharacter) ? {endCharacter: location.endCharacter} : {}),
|
|
109
|
-
classification,
|
|
110
|
-
})
|
|
111
|
-
}
|
|
112
|
-
const all = [...groups.values()].sort((left, right) => right.count - left.count || left.file.localeCompare(right.file) || left.id.localeCompare(right.id))
|
|
113
|
-
return {all, shown: all.slice(0, cap)}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function textFor(result) {
|
|
117
|
-
if (result.status === 'NOT_FOUND') return `No symbol found matching "${result.query}".`
|
|
118
|
-
if (result.status === 'AMBIGUOUS') return [
|
|
119
|
-
`Symbol "${result.query}" is ambiguous (${result.candidates.length} candidate(s) shown). Supply an exact node ID:`,
|
|
120
|
-
...result.candidates.map((candidate) => ` ${candidate.label} [${candidate.id}]`),
|
|
121
|
-
].join('\n')
|
|
122
|
-
const definition = result.definition
|
|
123
|
-
const lines = [
|
|
124
|
-
`Symbol inspection: ${definition.label} [${definition.id}]`,
|
|
125
|
-
`Definition: ${definition.file}:${definition.line}`,
|
|
126
|
-
`Evidence: ${result.evidence.state}${result.evidence.cached ? ' (cache hit)' : ''}; ${result.exact.occurrences} exact reference occurrence(s) in ${result.exact.files} file(s) / ${result.exact.containers} container(s).`,
|
|
127
|
-
`Graph: ${result.graph.occurrenceTotal} direct occurrence edge(s) (${result.graph.occurrences.length} shown); ${result.graph.impact.length} dependent(s) shown.`,
|
|
128
|
-
]
|
|
129
|
-
if (result.evidence.reason) lines.push(`Completeness: ${result.evidence.reason}`)
|
|
130
|
-
if (result.exact.zeroReferences) lines.push('Exact result: zero non-declaration references in the completely covered project universe.')
|
|
131
|
-
if (result.exact.groups.length) {
|
|
132
|
-
lines.push('Reference containers:')
|
|
133
|
-
for (const group of result.exact.groups) lines.push(` ${group.count} site(s) ${group.label} [${group.id}]`)
|
|
134
|
-
}
|
|
135
|
-
if (result.source.definition) {
|
|
136
|
-
const source = result.source.definition
|
|
137
|
-
lines.push('', `Definition source (${source.file}:${source.startLine}-${source.endLine}):`, source.text)
|
|
138
|
-
}
|
|
139
|
-
for (const source of result.source.callers) {
|
|
140
|
-
lines.push('', `Caller source (${source.file}:${source.startLine}-${source.endLine}, focus ${source.focusLine}):`, source.text)
|
|
141
|
-
}
|
|
142
|
-
return lines.join('\n')
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export async function tInspectSymbol(g, args = {}, ctx = {}) {
|
|
146
|
-
const query = String(args.label || '').trim()
|
|
147
|
-
const info = resolveNodeInfo(g, query)
|
|
148
|
-
if (!info.node) return toolResult(textFor({status: 'NOT_FOUND', query}), {status: 'NOT_FOUND', query})
|
|
149
|
-
if (info.matches > 1 && !g.byId.has(query)) {
|
|
150
|
-
const result = {status: 'AMBIGUOUS', query, candidates: candidateNodes(g, query)}
|
|
151
|
-
return toolResult(textFor(result), result, {completeness: {status: 'blocked', reason: 'ambiguous symbol'}})
|
|
152
|
-
}
|
|
153
|
-
const node = info.node
|
|
154
|
-
const targetId = String(node.id)
|
|
155
|
-
if (!isSymbol(targetId)) {
|
|
156
|
-
const result = {status: 'UNSUPPORTED', query, candidates: [{id: targetId, label: node.label || targetId, file: node.source_file || targetId}]}
|
|
157
|
-
return toolResult('inspect_symbol requires a symbol node, not a file node.', result)
|
|
158
|
-
}
|
|
159
|
-
const maxReferences = boundedInteger(args.max_references, 1_000, 1, 5_000)
|
|
160
|
-
const maxContainers = boundedInteger(args.max_containers, 15, 1, 50)
|
|
161
|
-
const contextLines = boundedInteger(args.context_lines, 8, 0, 40)
|
|
162
|
-
const timeoutMs = boundedInteger(args.timeout_ms, 30_000, 1_000, 60_000)
|
|
163
|
-
const precision = ['auto', 'graph', 'lsp'].includes(args.precision) ? args.precision : 'auto'
|
|
164
|
-
const shouldQuery = precision === 'lsp' || (precision === 'auto' && g.graphPrecisionMode !== 'off')
|
|
165
|
-
let overlay = null
|
|
166
|
-
let cached = false
|
|
167
|
-
let elapsedMs = 0
|
|
168
|
-
let queryError = null
|
|
169
|
-
if (shouldQuery) {
|
|
170
|
-
try {
|
|
171
|
-
const queryResult = await querySymbolPrecision({repoRoot: ctx.repoRoot, graphPath: ctx.graphPath, targetId, maxReferences, timeoutMs})
|
|
172
|
-
overlay = queryResult.overlay
|
|
173
|
-
cached = queryResult.cached
|
|
174
|
-
elapsedMs = queryResult.elapsedMs
|
|
175
|
-
} catch (error) {
|
|
176
|
-
queryError = error?.message || 'exact symbol query failed'
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
const locations = Array.isArray(overlay?.locations) ? overlay.locations : []
|
|
180
|
-
const grouped = groupLocations(g, locations, maxContainers)
|
|
181
|
-
const files = new Set(locations.map((location) => String(location.file || '')).filter(Boolean))
|
|
182
|
-
let state = 'GRAPH_ONLY'
|
|
183
|
-
if (overlay?.state === 'COMPLETE') state = 'EXACT'
|
|
184
|
-
else if (overlay?.state === 'PARTIAL') state = 'PARTIAL'
|
|
185
|
-
else if (overlay?.state === 'OFF') state = 'OFF'
|
|
186
|
-
else if (shouldQuery) state = 'UNAVAILABLE'
|
|
187
|
-
const reason = queryError || overlay?.reason || (shouldQuery ? null : 'semantic precision was not requested')
|
|
188
|
-
const graphRefs = graphOccurrences(g, targetId)
|
|
189
|
-
const impact = reverseImpact(g, targetId)
|
|
190
|
-
const definition = {
|
|
191
|
-
id: targetId,
|
|
192
|
-
label: String(node.label || targetId),
|
|
193
|
-
kind: String(node.symbol_kind || 'symbol'),
|
|
194
|
-
space: String(node.symbol_space || 'value'),
|
|
195
|
-
exported: node.exported === true,
|
|
196
|
-
file: String(node.source_file || targetId.split('#')[0]),
|
|
197
|
-
line: sourceLine(node),
|
|
198
|
-
...(node.source_range ? {range: node.source_range} : {}),
|
|
199
|
-
...(node.complexity ? {complexity: node.complexity} : {}),
|
|
200
|
-
}
|
|
201
|
-
const definitionSource = sourceExcerpt(ctx.repoRoot, definition.file, definition.line, contextLines)
|
|
202
|
-
const callerSources = grouped.shown.slice(0, 5).map((group) => sourceExcerpt(ctx.repoRoot, group.file, group.locations[0]?.line || 1, contextLines)).filter(Boolean)
|
|
203
|
-
const result = {
|
|
204
|
-
status: 'OK',
|
|
205
|
-
definition,
|
|
206
|
-
evidence: {state, cached, elapsedMs, reason, provider: overlay?.engines?.[0]?.provider || null},
|
|
207
|
-
exact: {
|
|
208
|
-
occurrences: locations.length,
|
|
209
|
-
occurrencesWithDefinition: locations.length + 1,
|
|
210
|
-
files: files.size,
|
|
211
|
-
containers: grouped.all.length,
|
|
212
|
-
groups: grouped.shown,
|
|
213
|
-
zeroReferences: overlay?.noReferenceSymbols?.includes(targetId) === true,
|
|
214
|
-
capped: overlay?.coverage?.truncated === true || grouped.all.length > grouped.shown.length,
|
|
215
|
-
},
|
|
216
|
-
graph: {
|
|
217
|
-
occurrences: graphRefs.shown,
|
|
218
|
-
occurrenceTotal: graphRefs.total,
|
|
219
|
-
occurrencesCapped: graphRefs.total > graphRefs.shown.length,
|
|
220
|
-
impact,
|
|
221
|
-
},
|
|
222
|
-
source: {definition: definitionSource, callers: callerSources},
|
|
223
|
-
}
|
|
224
|
-
const warnings = state === 'PARTIAL' || state === 'UNAVAILABLE'
|
|
225
|
-
? [{code: 'SYMBOL_PRECISION_INCOMPLETE', message: reason || `semantic precision is ${state.toLowerCase()}`}]
|
|
226
|
-
: []
|
|
227
|
-
return toolResult(textFor(result), result, {
|
|
228
|
-
warnings,
|
|
229
|
-
completeness: {
|
|
230
|
-
status: state === 'EXACT' && !result.exact.capped ? 'complete' : state.toLowerCase(),
|
|
231
|
-
referenceLimit: maxReferences,
|
|
232
|
-
containerLimit: maxContainers,
|
|
233
|
-
returnedContainers: grouped.shown.length,
|
|
234
|
-
locationSamplesPerContainer: MAX_LOCATION_SAMPLES,
|
|
235
|
-
graphOccurrenceLimit: MAX_GRAPH_OCCURRENCES,
|
|
236
|
-
},
|
|
237
|
-
})
|
|
238
|
-
}
|