weavatrix 0.3.13 → 1.0.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 +231 -199
- 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 +8 -65
- 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.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.js +0 -235
- 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 -62
- package/src/analysis/endpoints/extract.js +0 -107
- 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 -295
- 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 -154
- 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 -173
- 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 -174
- package/src/mcp/health/dead-code.mjs +0 -110
- package/src/mcp/health/duplicates.mjs +0 -85
- package/src/mcp/health/endpoints.mjs +0 -71
- package/src/mcp/health/structure.mjs +0 -219
- 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 -220
- 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
package/src/mcp-source-tools.mjs
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
|
|
2
|
-
import { extname, isAbsolute, join, relative, resolve } from 'node:path'
|
|
3
|
-
import { resolveRepoPath } from './repo-path.js'
|
|
4
|
-
import { runCommandSync } from './process.js'
|
|
5
|
-
import { toolResult } from './mcp/tool-result.mjs'
|
|
6
|
-
|
|
7
|
-
const SEARCH_SKIP = new Set(['.git', 'node_modules', 'dist', 'build', 'out', '.next', 'coverage', 'vendor', '.venv', 'venv', 'env', 'target', '__pycache__', '.idea', '.vscode', '.cache', 'bin', 'obj', 'weavatrix-graphs'])
|
|
8
|
-
const BINARY_EXT = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.pdf', '.zip', '.gz', '.tar', '.exe', '.dll', '.so', '.dylib', '.woff', '.woff2', '.ttf', '.eot', '.mp4', '.mp3', '.wasm', '.class', '.jar', '.node', '.bin'])
|
|
9
|
-
const MAX_SEARCH_FILE_BYTES = 1024 * 1024
|
|
10
|
-
const MAX_SOURCE_FILE_BYTES = 2 * 1024 * 1024
|
|
11
|
-
const MAX_SOURCE_CONTEXT_LINES = 1000
|
|
12
|
-
|
|
13
|
-
function rgSearch(repoRoot, resolveRg, query, { isRegex, glob, maxResults }, spawnRg = runCommandSync) {
|
|
14
|
-
const rg = resolveRg()
|
|
15
|
-
if (!rg) return null
|
|
16
|
-
// Exclude node_modules explicitly: --hidden + the default gitignore stack is the only thing that
|
|
17
|
-
// keeps a dependency tree out, which fails for repos that commit node_modules or lack a gitignore.
|
|
18
|
-
// A caller-supplied glob is appended after this and still wins (rg: last matching glob decides).
|
|
19
|
-
const args = ['--line-number', '--no-heading', '--color', 'never', '--hidden', '-g', '!.git/**', '-g', '!node_modules', '-m', '100', '-i']
|
|
20
|
-
if (!isRegex) args.push('--fixed-strings')
|
|
21
|
-
if (glob) args.push('-g', glob)
|
|
22
|
-
// Run from the repository root and search `.` so path globs such as `src/**` are evaluated
|
|
23
|
-
// against repository-relative paths on Windows too. Passing an absolute search root makes rg
|
|
24
|
-
// compare the glob with a drive-prefixed path and silently return no matches.
|
|
25
|
-
args.push('--', query, '.')
|
|
26
|
-
const res = spawnRg(rg, args, { cwd: repoRoot, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024, timeout: 15000 })
|
|
27
|
-
if (res.status !== 0 && res.status !== 1) return null
|
|
28
|
-
const out = []
|
|
29
|
-
for (const line of (res.stdout || '').split(/\r?\n/)) {
|
|
30
|
-
const match = line.match(/^(.*?):(\d+):(.*)$/)
|
|
31
|
-
if (!match) continue
|
|
32
|
-
out.push({
|
|
33
|
-
file: relative(repoRoot, isAbsolute(match[1]) ? match[1] : resolve(repoRoot, match[1])).replace(/\\/g, '/'),
|
|
34
|
-
line: Number(match[2]),
|
|
35
|
-
text: match[3].trim().slice(0, 300),
|
|
36
|
-
})
|
|
37
|
-
if (out.length >= maxResults) break
|
|
38
|
-
}
|
|
39
|
-
return out
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function globToRe(glob) {
|
|
43
|
-
if (!glob) return null
|
|
44
|
-
try {
|
|
45
|
-
return new RegExp(glob.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.'), 'i')
|
|
46
|
-
} catch {
|
|
47
|
-
return null
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function nodeGrep(repoRoot, query, { isRegex, glob, maxResults }) {
|
|
52
|
-
const out = []
|
|
53
|
-
let re = null
|
|
54
|
-
if (isRegex) {
|
|
55
|
-
try { re = new RegExp(query, 'i') } catch { return out }
|
|
56
|
-
}
|
|
57
|
-
const q = String(query).toLowerCase()
|
|
58
|
-
const globRe = globToRe(glob)
|
|
59
|
-
const stack = [repoRoot]
|
|
60
|
-
let filesScanned = 0
|
|
61
|
-
while (stack.length && out.length < maxResults) {
|
|
62
|
-
const dir = stack.pop()
|
|
63
|
-
let entries
|
|
64
|
-
try { entries = readdirSync(dir, { withFileTypes: true }) } catch { continue }
|
|
65
|
-
for (const ent of entries) {
|
|
66
|
-
if (out.length >= maxResults) break
|
|
67
|
-
const full = join(dir, ent.name)
|
|
68
|
-
if (ent.isDirectory()) {
|
|
69
|
-
if (!SEARCH_SKIP.has(ent.name)) stack.push(full)
|
|
70
|
-
continue
|
|
71
|
-
}
|
|
72
|
-
if (!ent.isFile() || BINARY_EXT.has(extname(ent.name).toLowerCase())) continue
|
|
73
|
-
const rel = relative(repoRoot, full).replace(/\\/g, '/')
|
|
74
|
-
if (globRe && !globRe.test(rel)) continue
|
|
75
|
-
let st
|
|
76
|
-
try { st = statSync(full) } catch { continue }
|
|
77
|
-
if (st.size > MAX_SEARCH_FILE_BYTES) continue
|
|
78
|
-
if (++filesScanned > 20000) return out
|
|
79
|
-
let text
|
|
80
|
-
try { text = readFileSync(full, 'utf8') } catch { continue }
|
|
81
|
-
if (text.indexOf('\0') >= 0) continue
|
|
82
|
-
const lines = text.split(/\r?\n/)
|
|
83
|
-
for (let i = 0; i < lines.length; i++) {
|
|
84
|
-
const ln = lines[i]
|
|
85
|
-
if (re ? re.test(ln) : ln.toLowerCase().includes(q)) {
|
|
86
|
-
out.push({ file: rel, line: i + 1, text: ln.trim().slice(0, 300) })
|
|
87
|
-
if (out.length >= maxResults) break
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return out
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function searchCode({ repoRoot, resolveRg, spawnRg = runCommandSync }, { query, is_regex = false, max_results = 40, glob } = {}) {
|
|
96
|
-
if (!query) return 'Provide a "query" string.'
|
|
97
|
-
if (!repoRoot || !existsSync(repoRoot)) return 'Source search unavailable: repo root not provided to this MCP server.'
|
|
98
|
-
const max = Math.max(1, Math.min(200, Number(max_results) || 40))
|
|
99
|
-
const opts = { isRegex: !!is_regex, glob: glob || null, maxResults: max }
|
|
100
|
-
let matches = rgSearch(repoRoot, resolveRg, query, opts, spawnRg)
|
|
101
|
-
const engine = matches ? 'ripgrep' : 'node'
|
|
102
|
-
if (!matches) matches = nodeGrep(repoRoot, query, opts)
|
|
103
|
-
const what = is_regex ? `/${query}/i` : `"${query}"`
|
|
104
|
-
if (!matches.length) return toolResult(
|
|
105
|
-
`No matches for ${what}${glob ? ` in ${glob}` : ''}.`,
|
|
106
|
-
{query, isRegex: !!is_regex, glob: glob || null, engine, matches: []},
|
|
107
|
-
{completeness: {status: 'complete', reason: 'search completed with no matches'}},
|
|
108
|
-
)
|
|
109
|
-
const text = [
|
|
110
|
-
`${matches.length} match${matches.length === 1 ? '' : 'es'} for ${what}${glob ? ` (glob ${glob})` : ''} [${engine}]:`,
|
|
111
|
-
...matches.map((m) => ` ${m.file}:${m.line}: ${m.text}`),
|
|
112
|
-
].join('\n')
|
|
113
|
-
return toolResult(text, {query, isRegex: !!is_regex, glob: glob || null, engine, matches}, {
|
|
114
|
-
completeness: {status: matches.length >= max ? 'bounded' : 'complete', limit: max},
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function readSource({ repoRoot, resolveNode, isSymbol }, g, { label, path, start_line, before = 3, after = 40 } = {}) {
|
|
119
|
-
if (!repoRoot || !existsSync(repoRoot)) return 'Source read unavailable: repo root not provided to this MCP server.'
|
|
120
|
-
let file = null
|
|
121
|
-
let focusLine = null
|
|
122
|
-
let title
|
|
123
|
-
// label resolves first; a path alongside it narrows rather than overrides — the node's focus line
|
|
124
|
-
// survives when both point at the same file (label+path used to silently return the file head).
|
|
125
|
-
const n = label && g ? resolveNode(g, label) : null
|
|
126
|
-
if (n) {
|
|
127
|
-
const nodeFile = String(n.source_file || (isSymbol(n.id) ? String(n.id).split('#')[0] : n.id))
|
|
128
|
-
if (!path || nodeFile.replace(/\\/g, '/') === String(path).replace(/\\/g, '/')) {
|
|
129
|
-
file = nodeFile
|
|
130
|
-
const match = String(n.source_location || '').match(/L(\d+)/)
|
|
131
|
-
focusLine = match ? Number(match[1]) : null
|
|
132
|
-
title = `${n.label ?? n.id} [${n.id}]`
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (!file) {
|
|
136
|
-
if (!path) return label ? `No node found matching "${label}".` : 'Provide "label" or "path".'
|
|
137
|
-
file = String(path)
|
|
138
|
-
title = file
|
|
139
|
-
}
|
|
140
|
-
// explicit anchor wins: window = start_line-before .. start_line+after (how a path read escapes the file head)
|
|
141
|
-
if (start_line != null && Number(start_line) > 0) focusLine = Math.floor(Number(start_line))
|
|
142
|
-
const resolved = resolveRepoPath(repoRoot, file)
|
|
143
|
-
if (resolved.reason === 'escape') return `Refusing to read "${file}": path escapes the repository root.`
|
|
144
|
-
if (resolved.reason === 'not-found') return `File not found: ${file}`
|
|
145
|
-
if (!resolved.ok) return `Could not resolve ${file} inside the repository root.`
|
|
146
|
-
const abs = resolved.path
|
|
147
|
-
let st
|
|
148
|
-
try { st = statSync(abs) } catch (e) { return `Could not inspect ${file}: ${e.message}` }
|
|
149
|
-
if (!st.isFile()) return `Could not read ${file}: not a regular file.`
|
|
150
|
-
if (st.size > MAX_SOURCE_FILE_BYTES) return `Could not read ${file}: file exceeds the ${MAX_SOURCE_FILE_BYTES / 1024 / 1024} MB source-read limit.`
|
|
151
|
-
let text
|
|
152
|
-
try { text = readFileSync(abs, 'utf8') } catch (e) { return `Could not read ${file}: ${e.message}` }
|
|
153
|
-
const lines = text.split(/\r?\n/)
|
|
154
|
-
if (focusLine) focusLine = Math.min(focusLine, lines.length) // an anchor past EOF shows the tail, not nothing
|
|
155
|
-
const b = Math.min(MAX_SOURCE_CONTEXT_LINES, Math.max(0, Number(before) || 0))
|
|
156
|
-
const a = Math.min(MAX_SOURCE_CONTEXT_LINES, Math.max(1, Number(after) || 40))
|
|
157
|
-
const start = focusLine ? Math.max(1, focusLine - b) : 1
|
|
158
|
-
const end = focusLine ? Math.min(lines.length, focusLine + a) : Math.min(lines.length, 1 + b + a)
|
|
159
|
-
const width = String(end).length
|
|
160
|
-
const body = []
|
|
161
|
-
for (let i = start; i <= end; i++) body.push(`${focusLine === i ? '>' : ' '}${String(i).padStart(width)} ${lines[i - 1] ?? ''}`)
|
|
162
|
-
return [`Source: ${title}`, `${file} (lines ${start}-${end} of ${lines.length})`, '', ...body].join('\n')
|
|
163
|
-
}
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
// Repository-semantic path classification. Unlike `.weavatrixignore`, this layer never removes a
|
|
2
|
-
// file from the graph: it labels non-product surfaces so health/clone/test-reachability tools can
|
|
3
|
-
// suppress noise while still explaining the exact rule that matched.
|
|
4
|
-
import { openSync, closeSync, readSync, readFileSync, statSync } from "node:fs";
|
|
5
|
-
import { createRepoBoundary } from "./repo-path.js";
|
|
6
|
-
|
|
7
|
-
export const PATH_CLASS_NAMES = Object.freeze(["test", "e2e", "generated", "mock", "story", "docs", "benchmark", "temp"]);
|
|
8
|
-
export const PATH_CLASS_QUERY_TERMS = Object.freeze({
|
|
9
|
-
test: Object.freeze(["test", "tests", "testing", "unit"]),
|
|
10
|
-
e2e: Object.freeze(["e2e", "playwright", "cypress"]),
|
|
11
|
-
generated: Object.freeze(["generated", "autogenerated", "dist"]),
|
|
12
|
-
mock: Object.freeze(["mock", "mocks", "fixture", "fixtures", "fake"]),
|
|
13
|
-
story: Object.freeze(["story", "stories", "storybook"]),
|
|
14
|
-
docs: Object.freeze(["doc", "docs", "documentation", "readme", "guide"]),
|
|
15
|
-
benchmark: Object.freeze(["benchmark", "benchmarks", "bench"]),
|
|
16
|
-
temp: Object.freeze(["temp", "temporary", "tmp"]),
|
|
17
|
-
});
|
|
18
|
-
export const PATH_CLASS_TASK_QUERY_TERMS = Object.freeze({
|
|
19
|
-
...PATH_CLASS_QUERY_TERMS,
|
|
20
|
-
test: Object.freeze([...PATH_CLASS_QUERY_TERMS.test, "spec", "coverage", "verify"]),
|
|
21
|
-
});
|
|
22
|
-
const PATH_CLASS_SET = new Set(PATH_CLASS_NAMES);
|
|
23
|
-
const MAX_CONFIG_BYTES = 64 * 1024;
|
|
24
|
-
const MAX_RULES_PER_CLASS = 64;
|
|
25
|
-
const MAX_RULE_LENGTH = 256;
|
|
26
|
-
const HEADER_BYTES = 8192;
|
|
27
|
-
|
|
28
|
-
const normalizePath = (value) => String(value || "").replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
|
|
29
|
-
const escapeRegex = (value) => value.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
30
|
-
|
|
31
|
-
function compileGlob(input) {
|
|
32
|
-
let pattern = String(input || "").trim().replace(/\\/g, "/");
|
|
33
|
-
if (!pattern || pattern.length > MAX_RULE_LENGTH || pattern.includes("\0") || pattern.split("/").includes("..")) return null;
|
|
34
|
-
const anchored = pattern.startsWith("/");
|
|
35
|
-
const directory = pattern.endsWith("/");
|
|
36
|
-
pattern = pattern.replace(/^\/+|\/+$/g, "");
|
|
37
|
-
if (!pattern) return null;
|
|
38
|
-
let body = "";
|
|
39
|
-
for (let index = 0; index < pattern.length; index += 1) {
|
|
40
|
-
const char = pattern[index];
|
|
41
|
-
if (char === "*" && pattern[index + 1] === "*") {
|
|
42
|
-
const slash = pattern[index + 2] === "/";
|
|
43
|
-
body += slash ? "(?:.*/)?" : ".*";
|
|
44
|
-
index += slash ? 2 : 1;
|
|
45
|
-
} else if (char === "*") body += "[^/]*";
|
|
46
|
-
else if (char === "?") body += "[^/]";
|
|
47
|
-
else body += escapeRegex(char);
|
|
48
|
-
}
|
|
49
|
-
const prefix = anchored ? "^" : "(?:^|/)";
|
|
50
|
-
const suffix = directory ? "(?:/.*)?$" : "$";
|
|
51
|
-
return new RegExp(`${prefix}${body}${suffix}`, "i");
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const DEFAULT_RULES = [
|
|
55
|
-
{
|
|
56
|
-
category: "e2e",
|
|
57
|
-
pattern: "test-e2e, e2e, Cypress, Playwright or acceptance/integration test roots",
|
|
58
|
-
regex: /(^|\/)(?:test-e2e|e2e|cypress|playwright|tests?[-_](?:e2e|integration|acceptance)|(?:e2e|integration|acceptance)[-_]tests?)(\/|$)|\.(?:e2e|cy)\.[^.\/]+$/i,
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
category: "test",
|
|
62
|
-
pattern: "test/spec roots and conventional test filenames",
|
|
63
|
-
regex: /(^|\/)(?:__tests?__|tests?|spec)(\/|$)|\.(?:test|itest|spec)\.[^.\/]+$|_test\.go$|(^|\/)test(?:_[^/]*)?\.py$/i,
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
category: "generated",
|
|
67
|
-
pattern: "generated/OpenAPI output path",
|
|
68
|
-
regex: /(^|\/)(?:generated|gen|openapi[-_]?generated|generated[-_]?client|api[-_]?client[-_]?generated)(\/|$)/i,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
category: "generated",
|
|
72
|
-
pattern: "conventional build output path",
|
|
73
|
-
regex: /(^|\/)(?:dist|build|out|coverage|\.next)(\/|$)/i,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
category: "mock",
|
|
77
|
-
pattern: "mock/fixture path or mockData filename",
|
|
78
|
-
regex: /(^|\/)(?:__mocks__|mocks?|fixtures?|__fixtures__)(\/|$)|(^|\/)(?:mock[-_.]?data|test[-_.]?data|fake[-_.]?data)\.[^.\/]+$/i,
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
category: "story",
|
|
82
|
-
pattern: "Storybook story",
|
|
83
|
-
regex: /(^|\/)\.storybook(\/|$)|\.stories?\.[^.\/]+$/i,
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
category: "docs",
|
|
87
|
-
pattern: "documentation path or prose file",
|
|
88
|
-
regex: /(^|\/)(?:docs?|documentation)(\/|$)|\.(?:md|markdown|mdx|rst|adoc)$/i,
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
category: "benchmark",
|
|
92
|
-
pattern: "benchmark/benchmarks root",
|
|
93
|
-
regex: /(^|\/)benchmarks?(\/|$)/i,
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
category: "temp",
|
|
97
|
-
pattern: "__temp or .tmp-* working/generated root",
|
|
98
|
-
regex: /(^|\/)(?:__temp|\.tmp(?:[-_][^/]*)?)(\/|$)/i,
|
|
99
|
-
},
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
const GENERATED_HEADER_RE = /(?:@generated\b|auto[- ]generated\b|generated\s+(?:code|file|client).*do not edit|do not edit.*generated|openapi generator)/i;
|
|
103
|
-
|
|
104
|
-
function boundedHeader(repoRoot, path) {
|
|
105
|
-
if (!repoRoot || !path) return "";
|
|
106
|
-
const resolved = createRepoBoundary(repoRoot).resolve(path);
|
|
107
|
-
if (!resolved.ok) return "";
|
|
108
|
-
let fd;
|
|
109
|
-
try {
|
|
110
|
-
fd = openSync(resolved.path, "r");
|
|
111
|
-
const buf = Buffer.alloc(HEADER_BYTES);
|
|
112
|
-
const count = readSync(fd, buf, 0, buf.length, 0);
|
|
113
|
-
return buf.subarray(0, count).toString("utf8");
|
|
114
|
-
} catch { return ""; }
|
|
115
|
-
finally { if (fd != null) try { closeSync(fd); } catch { /* already closed */ } }
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function loadPathClassificationConfig(repoRoot) {
|
|
119
|
-
if (!repoRoot) return { classify: {}, exclude: [], loaded: false };
|
|
120
|
-
const resolved = createRepoBoundary(repoRoot).resolve(".weavatrix.json");
|
|
121
|
-
if (!resolved.ok) return { classify: {}, exclude: [], loaded: false };
|
|
122
|
-
try {
|
|
123
|
-
if (statSync(resolved.path).size > MAX_CONFIG_BYTES) return { classify: {}, exclude: [], loaded: false, error: "config-too-large" };
|
|
124
|
-
const raw = JSON.parse(readFileSync(resolved.path, "utf8"));
|
|
125
|
-
const classify = {};
|
|
126
|
-
for (const category of PATH_CLASS_NAMES) {
|
|
127
|
-
const value = raw?.classify?.[category];
|
|
128
|
-
const patterns = (Array.isArray(value) ? value : typeof value === "string" ? [value] : [])
|
|
129
|
-
.slice(0, MAX_RULES_PER_CLASS)
|
|
130
|
-
.map((pattern) => String(pattern));
|
|
131
|
-
if (patterns.length) classify[category] = patterns;
|
|
132
|
-
}
|
|
133
|
-
// Explicit product opt-in removes only the default benchmark/temp classification. It does not
|
|
134
|
-
// override configured generated/test classes or a deliberate top-level exclude rule.
|
|
135
|
-
const productValue = raw?.classify?.product;
|
|
136
|
-
const productPatterns = (Array.isArray(productValue) ? productValue : typeof productValue === "string" ? [productValue] : [])
|
|
137
|
-
.slice(0, MAX_RULES_PER_CLASS)
|
|
138
|
-
.map((pattern) => String(pattern));
|
|
139
|
-
if (productPatterns.length) classify.product = productPatterns;
|
|
140
|
-
const exclude = (Array.isArray(raw?.exclude) ? raw.exclude : typeof raw?.exclude === "string" ? [raw.exclude] : [])
|
|
141
|
-
.slice(0, MAX_RULES_PER_CLASS)
|
|
142
|
-
.map((pattern) => String(pattern));
|
|
143
|
-
return { classify, exclude, loaded: true };
|
|
144
|
-
} catch { return { classify: {}, exclude: [], loaded: false, error: "invalid-config" }; }
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function configuredRules(config) {
|
|
148
|
-
const rules = [];
|
|
149
|
-
for (const category of PATH_CLASS_NAMES) {
|
|
150
|
-
for (const pattern of config.classify?.[category] || []) {
|
|
151
|
-
const regex = compileGlob(pattern);
|
|
152
|
-
if (regex) rules.push({ category, pattern, regex, source: "config" });
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
const excludes = [];
|
|
156
|
-
for (const pattern of config.exclude || []) {
|
|
157
|
-
const regex = compileGlob(pattern);
|
|
158
|
-
if (regex) excludes.push({ pattern, regex, source: "config" });
|
|
159
|
-
}
|
|
160
|
-
const productRules = [];
|
|
161
|
-
for (const pattern of config.classify?.product || []) {
|
|
162
|
-
const regex = compileGlob(pattern);
|
|
163
|
-
if (regex) productRules.push({ category: "product", pattern, regex, source: "config" });
|
|
164
|
-
}
|
|
165
|
-
return { rules, excludes, productRules };
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function createPathClassifier(repoRoot) {
|
|
169
|
-
const config = loadPathClassificationConfig(repoRoot);
|
|
170
|
-
const compiled = configuredRules(config);
|
|
171
|
-
return {
|
|
172
|
-
config,
|
|
173
|
-
explain(path, options = {}) {
|
|
174
|
-
const normalized = normalizePath(path);
|
|
175
|
-
const matches = [];
|
|
176
|
-
const classes = new Set();
|
|
177
|
-
const productMatches = compiled.productRules
|
|
178
|
-
.filter((rule) => rule.regex.test(normalized))
|
|
179
|
-
.map((rule) => ({ category: "product", source: rule.source, pattern: rule.pattern }));
|
|
180
|
-
matches.push(...productMatches);
|
|
181
|
-
const productOverride = productMatches.length > 0;
|
|
182
|
-
const add = (rule) => {
|
|
183
|
-
classes.add(rule.category);
|
|
184
|
-
if (rule.category === "e2e") classes.add("test");
|
|
185
|
-
matches.push({ category: rule.category, source: rule.source || "default", pattern: rule.pattern });
|
|
186
|
-
};
|
|
187
|
-
for (const rule of compiled.rules) if (rule.regex.test(normalized)) add(rule);
|
|
188
|
-
for (const rule of DEFAULT_RULES) {
|
|
189
|
-
if (productOverride && (rule.category === "benchmark" || rule.category === "temp")) continue;
|
|
190
|
-
if (rule.regex.test(normalized)) add({ ...rule, source: "default" });
|
|
191
|
-
}
|
|
192
|
-
const header = options.content == null ? boundedHeader(repoRoot, normalized) : String(options.content).slice(0, HEADER_BYTES);
|
|
193
|
-
if (GENERATED_HEADER_RE.test(header) && !classes.has("generated")) {
|
|
194
|
-
add({ category: "generated", source: "default", pattern: "generated-file header" });
|
|
195
|
-
}
|
|
196
|
-
const excludeMatches = compiled.excludes
|
|
197
|
-
.filter((rule) => rule.regex.test(normalized))
|
|
198
|
-
.map((rule) => ({ category: "exclude", source: rule.source, pattern: rule.pattern }));
|
|
199
|
-
matches.push(...excludeMatches);
|
|
200
|
-
const orderedClasses = PATH_CLASS_NAMES.filter((category) => classes.has(category));
|
|
201
|
-
return {
|
|
202
|
-
path: normalized,
|
|
203
|
-
classes: orderedClasses,
|
|
204
|
-
excluded: excludeMatches.length > 0,
|
|
205
|
-
productOverride,
|
|
206
|
-
matchedRule: matches[0] || null,
|
|
207
|
-
matchedRules: matches,
|
|
208
|
-
};
|
|
209
|
-
},
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export function explainPathClassification(repoRoot, path, options = {}) {
|
|
214
|
-
return createPathClassifier(repoRoot).explain(path, options);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export function hasPathClass(explanation, ...categories) {
|
|
218
|
-
const present = new Set(explanation?.classes || []);
|
|
219
|
-
return categories.some((category) => PATH_CLASS_SET.has(category) && present.has(category));
|
|
220
|
-
}
|
package/src/path-ignore.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// Repository-local exclusions shared by graph building, audits and clone scanning.
|
|
2
|
-
// `.weavatrixignore` follows the useful Gitignore subset: comments, *, **, ?, root-anchored
|
|
3
|
-
// patterns, directory suffixes and ordered ! re-includes. It never expands paths or reads outside root.
|
|
4
|
-
import { readFileSync } from "node:fs";
|
|
5
|
-
import { isAbsolute, relative } from "node:path";
|
|
6
|
-
import { createRepoBoundary } from "./repo-path.js";
|
|
7
|
-
|
|
8
|
-
function escapeRegex(value) {
|
|
9
|
-
return value.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function globRegex(pattern) {
|
|
13
|
-
let out = "";
|
|
14
|
-
for (let index = 0; index < pattern.length; index += 1) {
|
|
15
|
-
const char = pattern[index];
|
|
16
|
-
if (char === "*" && pattern[index + 1] === "*") {
|
|
17
|
-
const slash = pattern[index + 2] === "/";
|
|
18
|
-
out += slash ? "(?:.*/)?" : ".*";
|
|
19
|
-
index += slash ? 2 : 1;
|
|
20
|
-
} else if (char === "*") out += "[^/]*";
|
|
21
|
-
else if (char === "?") out += "[^/]";
|
|
22
|
-
else out += escapeRegex(char);
|
|
23
|
-
}
|
|
24
|
-
return out;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function parseWeavatrixIgnore(text) {
|
|
28
|
-
const rules = [];
|
|
29
|
-
for (const rawLine of String(text || "").split(/\r?\n/)) {
|
|
30
|
-
let line = rawLine.trim();
|
|
31
|
-
if (!line || line.startsWith("#")) continue;
|
|
32
|
-
const negated = line.startsWith("!");
|
|
33
|
-
if (negated) line = line.slice(1);
|
|
34
|
-
line = line.replace(/\\/g, "/");
|
|
35
|
-
const anchored = line.startsWith("/");
|
|
36
|
-
const directory = line.endsWith("/");
|
|
37
|
-
line = line.replace(/^\/+|\/+$/g, "");
|
|
38
|
-
if (!line || line.split("/").some((part) => part === "..")) continue;
|
|
39
|
-
const prefix = anchored ? "^" : "(?:^|/)";
|
|
40
|
-
const suffix = directory ? "(?:/.*)?$" : "$";
|
|
41
|
-
rules.push({ negated, regex: new RegExp(`${prefix}${globRegex(line)}${suffix}`) });
|
|
42
|
-
}
|
|
43
|
-
return rules;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function loadWeavatrixIgnore(repoRoot) {
|
|
47
|
-
try {
|
|
48
|
-
const resolved = createRepoBoundary(repoRoot).resolve(".weavatrixignore");
|
|
49
|
-
if (!resolved.ok) return [];
|
|
50
|
-
return parseWeavatrixIgnore(readFileSync(resolved.path, "utf8"));
|
|
51
|
-
}
|
|
52
|
-
catch { return []; }
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function isWeavatrixIgnored(path, rules) {
|
|
56
|
-
const normalized = String(path || "").replace(/\\/g, "/").replace(/^\.\//, "");
|
|
57
|
-
let ignored = false;
|
|
58
|
-
for (const rule of rules || []) if (rule.regex.test(normalized)) ignored = !rule.negated;
|
|
59
|
-
return ignored;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function filterWeavatrixIgnored(repoRoot, files) {
|
|
63
|
-
const rules = loadWeavatrixIgnore(repoRoot);
|
|
64
|
-
if (!rules.length) return files;
|
|
65
|
-
return files.filter((file) => {
|
|
66
|
-
const path = isAbsolute(file) ? relative(repoRoot, file) : file;
|
|
67
|
-
return !isWeavatrixIgnored(path, rules);
|
|
68
|
-
});
|
|
69
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_MAX_MESSAGE_BYTES = 8 * 1024 * 1024
|
|
2
|
-
export const DEFAULT_MAX_HEADER_BYTES = 16 * 1024
|
|
3
|
-
export const DEFAULT_REQUEST_TIMEOUT_MS = 10_000
|
|
4
|
-
export const JSON_RPC_VERSION = '2.0'
|
|
5
|
-
|
|
6
|
-
export function positiveInteger(value, fallback, label) {
|
|
7
|
-
const candidate = value == null ? fallback : Number(value)
|
|
8
|
-
if (!Number.isSafeInteger(candidate) || candidate <= 0) {
|
|
9
|
-
throw new TypeError(`${label} must be a positive integer`)
|
|
10
|
-
}
|
|
11
|
-
return candidate
|
|
12
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {delimiter, dirname, join} from 'node:path'
|
|
2
|
-
import {childProcessEnv} from '../../child-env.js'
|
|
3
|
-
|
|
4
|
-
const LSP_ENV_ALLOWLIST = new Set([
|
|
5
|
-
'path', 'pathext', 'systemroot', 'windir', 'comspec',
|
|
6
|
-
'temp', 'tmp', 'tmpdir', 'home', 'userprofile', 'localappdata', 'appdata',
|
|
7
|
-
'lang', 'language', 'lc_all', 'lc_ctype',
|
|
8
|
-
])
|
|
9
|
-
|
|
10
|
-
export function lspChildProcessEnv(overrides = {}) {
|
|
11
|
-
const inherited = childProcessEnv(overrides)
|
|
12
|
-
const clean = Object.fromEntries(
|
|
13
|
-
Object.entries(inherited).filter(([key]) => LSP_ENV_ALLOWLIST.has(key.toLowerCase())),
|
|
14
|
-
)
|
|
15
|
-
const safePath = [dirname(process.execPath)]
|
|
16
|
-
const systemRoot = inherited.SystemRoot || inherited.SYSTEMROOT || inherited.WINDIR
|
|
17
|
-
if (process.platform === 'win32' && systemRoot) safePath.push(join(systemRoot, 'System32'))
|
|
18
|
-
clean.PATH = [...new Set(safePath)].join(delimiter)
|
|
19
|
-
return clean
|
|
20
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export class LspProtocolError extends Error {
|
|
2
|
-
constructor(message, options) {
|
|
3
|
-
super(message, options)
|
|
4
|
-
this.name = 'LspProtocolError'
|
|
5
|
-
}
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export class LspTimeoutError extends Error {
|
|
9
|
-
constructor(method, timeoutMs) {
|
|
10
|
-
super(`LSP request timed out after ${timeoutMs}ms: ${method}`)
|
|
11
|
-
this.name = 'LspTimeoutError'
|
|
12
|
-
this.method = method
|
|
13
|
-
this.timeoutMs = timeoutMs
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import {spawn as spawnChild} from 'node:child_process'
|
|
2
|
-
import {positiveInteger} from './constants.js'
|
|
3
|
-
import {lspChildProcessEnv} from './environment.js'
|
|
4
|
-
import {LspTimeoutError} from './errors.js'
|
|
5
|
-
import {isLspClientActive} from './registry.js'
|
|
6
|
-
import {notifyClient, rejectPendingRequests, requestFromClient} from './protocol.js'
|
|
7
|
-
|
|
8
|
-
export function failClient(client, error) {
|
|
9
|
-
if (client.state === 'closed' || client.state === 'failed') return
|
|
10
|
-
client.state = 'failed'
|
|
11
|
-
rejectPendingRequests(client, error)
|
|
12
|
-
killClient(client, error)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function killClient(client, reason = new Error('LSP client was killed')) {
|
|
16
|
-
if (client.state !== 'closed') client.state = 'closed'
|
|
17
|
-
rejectPendingRequests(client, reason)
|
|
18
|
-
client.openDocuments.clear()
|
|
19
|
-
try { client.child.stdin?.destroy() } catch { /* already closed */ }
|
|
20
|
-
if (process.platform !== 'win32' && client.processGroupPid) {
|
|
21
|
-
try {
|
|
22
|
-
process.kill(-client.processGroupPid, 'SIGKILL')
|
|
23
|
-
return
|
|
24
|
-
} catch { /* fall through to direct child */ }
|
|
25
|
-
}
|
|
26
|
-
if (client.child.exitCode != null || client.child.signalCode != null) return
|
|
27
|
-
if (process.platform === 'win32' && client.child.pid) {
|
|
28
|
-
startWindowsTreeKill(client)
|
|
29
|
-
} else {
|
|
30
|
-
try { client.child.kill('SIGKILL') } catch { /* already exited */ }
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function directChildKill(client) {
|
|
35
|
-
try { client.child.kill('SIGKILL') } catch { /* already exited */ }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function startWindowsTreeKill(client) {
|
|
39
|
-
if (client.windowsTreeKillPromise) return client.windowsTreeKillPromise
|
|
40
|
-
client.windowsTreeKillPromise = new Promise((resolveKill) => {
|
|
41
|
-
try {
|
|
42
|
-
const killer = spawnChild('taskkill', ['/pid', String(client.child.pid), '/T', '/F'], {
|
|
43
|
-
shell: false,
|
|
44
|
-
windowsHide: true,
|
|
45
|
-
env: lspChildProcessEnv(),
|
|
46
|
-
stdio: 'ignore',
|
|
47
|
-
})
|
|
48
|
-
killer.once('error', () => { directChildKill(client); resolveKill() })
|
|
49
|
-
killer.once('exit', (code) => { if (code !== 0) directChildKill(client); resolveKill() })
|
|
50
|
-
} catch {
|
|
51
|
-
directChildKill(client)
|
|
52
|
-
resolveKill()
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
|
-
return client.windowsTreeKillPromise
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function killWindowsTreeAndWait(client, timeoutMs = 3_000) {
|
|
59
|
-
if (process.platform !== 'win32' || !client.child.pid || client.child.exitCode != null) return
|
|
60
|
-
const boundedTimeout = Math.max(250, Math.min(5_000, Number(timeoutMs) || 3_000))
|
|
61
|
-
let timer
|
|
62
|
-
await Promise.race([
|
|
63
|
-
startWindowsTreeKill(client),
|
|
64
|
-
new Promise((resolveKill) => {
|
|
65
|
-
timer = setTimeout(() => { directChildKill(client); resolveKill() }, boundedTimeout)
|
|
66
|
-
}),
|
|
67
|
-
])
|
|
68
|
-
if (timer) clearTimeout(timer)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export async function waitForClientExit(client, timeoutMs = client.requestTimeoutMs) {
|
|
72
|
-
if (!isLspClientActive(client)) return true
|
|
73
|
-
const boundedTimeout = Math.max(100, Math.min(10_000, Number(timeoutMs) || client.requestTimeoutMs))
|
|
74
|
-
let timer
|
|
75
|
-
const outcome = await Promise.race([
|
|
76
|
-
client.exited.then(() => true),
|
|
77
|
-
new Promise((resolveExit) => { timer = setTimeout(() => resolveExit(false), boundedTimeout) }),
|
|
78
|
-
])
|
|
79
|
-
if (timer) clearTimeout(timer)
|
|
80
|
-
return outcome
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function shutdownClient(client, options = {}) {
|
|
84
|
-
if (!client.shutdownPromise) client.shutdownPromise = shutdownClientOnce(client, options)
|
|
85
|
-
return client.shutdownPromise
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export async function shutdownClientOnce(client, {timeoutMs = client.requestTimeoutMs} = {}) {
|
|
89
|
-
const boundedTimeout = positiveInteger(timeoutMs, client.requestTimeoutMs, 'timeoutMs')
|
|
90
|
-
try {
|
|
91
|
-
if (client.state === 'starting') await client.spawned
|
|
92
|
-
if (client.state === 'closed') {
|
|
93
|
-
await waitForClientExit(client, boundedTimeout)
|
|
94
|
-
return
|
|
95
|
-
}
|
|
96
|
-
if (client.state === 'initialized') {
|
|
97
|
-
await requestFromClient(client, 'shutdown', null, {timeoutMs: boundedTimeout})
|
|
98
|
-
}
|
|
99
|
-
client.state = 'stopping'
|
|
100
|
-
await notifyClient(client, 'exit', null)
|
|
101
|
-
await killWindowsTreeAndWait(client, Math.min(3_000, boundedTimeout))
|
|
102
|
-
if (process.platform !== 'win32' && client.processGroupPid) {
|
|
103
|
-
await waitForClientExit(client, Math.min(1_000, boundedTimeout))
|
|
104
|
-
try { process.kill(-client.processGroupPid, 'SIGKILL') } catch { /* group already exited */ }
|
|
105
|
-
}
|
|
106
|
-
if (!await waitForClientExit(client, boundedTimeout)) {
|
|
107
|
-
killClient(client, new LspTimeoutError('exit', boundedTimeout))
|
|
108
|
-
}
|
|
109
|
-
} catch (error) {
|
|
110
|
-
killClient(client, error)
|
|
111
|
-
await killWindowsTreeAndWait(client, Math.min(2_000, boundedTimeout))
|
|
112
|
-
await waitForClientExit(client, Math.min(2_000, boundedTimeout))
|
|
113
|
-
}
|
|
114
|
-
}
|