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.
Files changed (306) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +132 -200
  3. package/bin/native/darwin-arm64/weavatrix +0 -0
  4. package/bin/native/darwin-x64/weavatrix +0 -0
  5. package/bin/native/linux-arm64/weavatrix +0 -0
  6. package/bin/native/linux-x64/weavatrix +0 -0
  7. package/bin/native/win32-arm64/weavatrix.exe +0 -0
  8. package/bin/native/win32-x64/weavatrix.exe +0 -0
  9. package/bin/resolve-binary.mjs +73 -0
  10. package/bin/run-native.mjs +26 -0
  11. package/bin/weavatrix-mcp.mjs +7 -6
  12. package/bin/weavatrix.mjs +5 -0
  13. package/package.json +10 -67
  14. package/server.json +38 -0
  15. package/skill/SKILL.md +62 -374
  16. package/skill/agents/openai.yaml +4 -0
  17. package/skill/references/tool-routing.md +45 -0
  18. package/SECURITY.md +0 -101
  19. package/docs/adr/0001-v0.3-offline-online-split.md +0 -60
  20. package/docs/agent-task-benchmark.md +0 -58
  21. package/docs/releases/v0.2.19.md +0 -33
  22. package/docs/releases/v0.3.0.md +0 -43
  23. package/docs/releases/v0.3.1.md +0 -30
  24. package/docs/releases/v0.3.10.md +0 -76
  25. package/docs/releases/v0.3.11.md +0 -55
  26. package/docs/releases/v0.3.12.md +0 -44
  27. package/docs/releases/v0.3.13.md +0 -34
  28. package/docs/releases/v0.3.14.md +0 -37
  29. package/docs/releases/v0.3.2.md +0 -15
  30. package/docs/releases/v0.3.3.md +0 -25
  31. package/docs/releases/v0.3.4.md +0 -40
  32. package/docs/releases/v0.3.5.md +0 -50
  33. package/docs/releases/v0.3.6.md +0 -44
  34. package/docs/releases/v0.3.7.md +0 -37
  35. package/docs/releases/v0.3.8.md +0 -21
  36. package/docs/releases/v0.3.9.md +0 -36
  37. package/scripts/run-agent-task-benchmark.mjs +0 -118
  38. package/src/analysis/allowed-test-runner.js +0 -70
  39. package/src/analysis/architecture/contract-graph.js +0 -119
  40. package/src/analysis/architecture/contract-schema.js +0 -110
  41. package/src/analysis/architecture/contract-storage.js +0 -35
  42. package/src/analysis/architecture/contract-verification.js +0 -168
  43. package/src/analysis/architecture-contract.js +0 -16
  44. package/src/analysis/audit-debt.js +0 -94
  45. package/src/analysis/audit-extensions.js +0 -60
  46. package/src/analysis/cargo-dependency-evidence.js +0 -108
  47. package/src/analysis/cargo-manifests.js +0 -81
  48. package/src/analysis/change-classification/diff-parser.js +0 -103
  49. package/src/analysis/change-classification/options.js +0 -40
  50. package/src/analysis/change-classification/symbol-classifier.js +0 -177
  51. package/src/analysis/change-classification.js +0 -188
  52. package/src/analysis/coverage-reports-tarpaulin.js +0 -112
  53. package/src/analysis/coverage-reports.js +0 -246
  54. package/src/analysis/cycle-route.js +0 -14
  55. package/src/analysis/data-flow-evidence.js +0 -114
  56. package/src/analysis/dead-check.js +0 -300
  57. package/src/analysis/dead-code-review/candidates.js +0 -194
  58. package/src/analysis/dead-code-review/policy.js +0 -23
  59. package/src/analysis/dead-code-review.js +0 -119
  60. package/src/analysis/dep-check-ecosystems.js +0 -205
  61. package/src/analysis/dep-check.js +0 -290
  62. package/src/analysis/dep-rules.js +0 -10
  63. package/src/analysis/dependency/conventions.js +0 -16
  64. package/src/analysis/dependency/scoped-dependencies.js +0 -51
  65. package/src/analysis/dependency/source-references.js +0 -21
  66. package/src/analysis/duplicate-groups.js +0 -104
  67. package/src/analysis/duplicates-worker.js +0 -11
  68. package/src/analysis/duplicates.compute.js +0 -269
  69. package/src/analysis/duplicates.js +0 -15
  70. package/src/analysis/duplicates.run.js +0 -52
  71. package/src/analysis/duplicates.tokenize.js +0 -199
  72. package/src/analysis/endpoints/common.js +0 -79
  73. package/src/analysis/endpoints/extract.js +0 -120
  74. package/src/analysis/endpoints/inventory.js +0 -84
  75. package/src/analysis/endpoints/mounts.js +0 -129
  76. package/src/analysis/endpoints-java.js +0 -260
  77. package/src/analysis/endpoints-rust.js +0 -124
  78. package/src/analysis/endpoints.js +0 -3
  79. package/src/analysis/findings.js +0 -70
  80. package/src/analysis/git-history/analytics.js +0 -177
  81. package/src/analysis/git-history/collector.js +0 -67
  82. package/src/analysis/git-history/options.js +0 -66
  83. package/src/analysis/git-history.js +0 -143
  84. package/src/analysis/git-ref-graph.js +0 -76
  85. package/src/analysis/go-dependency-evidence.js +0 -62
  86. package/src/analysis/graph-analysis.aggregate.js +0 -296
  87. package/src/analysis/graph-analysis.edges.js +0 -24
  88. package/src/analysis/graph-analysis.js +0 -7
  89. package/src/analysis/graph-analysis.refs.js +0 -202
  90. package/src/analysis/graph-analysis.summaries.js +0 -65
  91. package/src/analysis/health-capabilities.js +0 -83
  92. package/src/analysis/hot-path-review.js +0 -266
  93. package/src/analysis/http-contract-wrappers.js +0 -241
  94. package/src/analysis/http-contracts/analysis.js +0 -179
  95. package/src/analysis/http-contracts/client-call-detection.js +0 -82
  96. package/src/analysis/http-contracts/client-call-parser.js +0 -273
  97. package/src/analysis/http-contracts/graph-context.js +0 -143
  98. package/src/analysis/http-contracts/matching.js +0 -61
  99. package/src/analysis/http-contracts/shared.js +0 -85
  100. package/src/analysis/http-contracts.js +0 -6
  101. package/src/analysis/internal-audit/dependency-health.js +0 -113
  102. package/src/analysis/internal-audit/python-manifests.js +0 -78
  103. package/src/analysis/internal-audit/repo-files.js +0 -50
  104. package/src/analysis/internal-audit/supply-chain.js +0 -149
  105. package/src/analysis/internal-audit.collect.js +0 -213
  106. package/src/analysis/internal-audit.js +0 -9
  107. package/src/analysis/internal-audit.reach.js +0 -201
  108. package/src/analysis/internal-audit.run.js +0 -256
  109. package/src/analysis/java-source.js +0 -36
  110. package/src/analysis/jvm-artifact-index.js +0 -87
  111. package/src/analysis/jvm-dependency-evidence.js +0 -138
  112. package/src/analysis/jvm-manifests.js +0 -114
  113. package/src/analysis/manifests.js +0 -123
  114. package/src/analysis/package-reachability.js +0 -39
  115. package/src/analysis/source-complexity.ast.js +0 -97
  116. package/src/analysis/source-complexity.constants.js +0 -55
  117. package/src/analysis/source-complexity.js +0 -14
  118. package/src/analysis/source-complexity.report.js +0 -81
  119. package/src/analysis/source-complexity.walk.js +0 -228
  120. package/src/analysis/source-correctness/retry-patterns.js +0 -93
  121. package/src/analysis/source-correctness.js +0 -222
  122. package/src/analysis/static-test-reachability.js +0 -150
  123. package/src/analysis/structure/dependency-graph.js +0 -151
  124. package/src/analysis/structure/findings.js +0 -162
  125. package/src/analysis/task-retrieval.js +0 -107
  126. package/src/analysis/transport-contracts.js +0 -224
  127. package/src/analysis/transport-runtime-evidence.js +0 -155
  128. package/src/analysis-kit.mjs +0 -17
  129. package/src/bounds.js +0 -4
  130. package/src/build-graph.js +0 -209
  131. package/src/child-env.js +0 -7
  132. package/src/config.js +0 -5
  133. package/src/extension/local-services.mjs +0 -75
  134. package/src/git-exec.js +0 -67
  135. package/src/graph/build-worker.js +0 -49
  136. package/src/graph/builder/go-receiver-resolution.js +0 -112
  137. package/src/graph/builder/js/dynamic-import.js +0 -26
  138. package/src/graph/builder/js/queries.js +0 -48
  139. package/src/graph/builder/lang-csharp.js +0 -40
  140. package/src/graph/builder/lang-css.js +0 -29
  141. package/src/graph/builder/lang-go.js +0 -140
  142. package/src/graph/builder/lang-html.js +0 -29
  143. package/src/graph/builder/lang-java-calls.js +0 -142
  144. package/src/graph/builder/lang-java.js +0 -215
  145. package/src/graph/builder/lang-js.js +0 -291
  146. package/src/graph/builder/lang-python.js +0 -235
  147. package/src/graph/builder/lang-rust-calls.js +0 -63
  148. package/src/graph/builder/lang-rust.js +0 -299
  149. package/src/graph/builder/lang-solidity.js +0 -126
  150. package/src/graph/builder/lang-sql.js +0 -263
  151. package/src/graph/builder/pass2-resolution.js +0 -81
  152. package/src/graph/builder/spec-pkg.js +0 -87
  153. package/src/graph/community.js +0 -27
  154. package/src/graph/edge-provenance.js +0 -31
  155. package/src/graph/file-lock.js +0 -69
  156. package/src/graph/freshness-probe.js +0 -142
  157. package/src/graph/graph-filter.js +0 -63
  158. package/src/graph/incremental-refresh.js +0 -235
  159. package/src/graph/internal-builder.barrels.js +0 -235
  160. package/src/graph/internal-builder.build.js +0 -300
  161. package/src/graph/internal-builder.java.js +0 -33
  162. package/src/graph/internal-builder.js +0 -12
  163. package/src/graph/internal-builder.langs.js +0 -157
  164. package/src/graph/internal-builder.pass2.js +0 -163
  165. package/src/graph/internal-builder.resolvers.js +0 -286
  166. package/src/graph/layout.js +0 -51
  167. package/src/graph/node-id.js +0 -7
  168. package/src/graph/parser-artifact-boundary.js +0 -55
  169. package/src/graph/relations.js +0 -4
  170. package/src/graph/repo-registry.js +0 -124
  171. package/src/graph/resolvers/rust.js +0 -117
  172. package/src/infra/infra-items.js +0 -255
  173. package/src/infra/infra-registry.js +0 -184
  174. package/src/infra/infra.detect.js +0 -119
  175. package/src/infra/infra.js +0 -38
  176. package/src/infra/infra.match.js +0 -102
  177. package/src/infra/infra.scan.js +0 -93
  178. package/src/mcp/actions/graph-lifecycle.mjs +0 -148
  179. package/src/mcp/architecture-bootstrap.mjs +0 -168
  180. package/src/mcp/architecture-starter.mjs +0 -234
  181. package/src/mcp/catalog.mjs +0 -233
  182. package/src/mcp/company-contract-page.mjs +0 -142
  183. package/src/mcp/company-contract-reconcile.mjs +0 -87
  184. package/src/mcp/company-contract-verdict.mjs +0 -42
  185. package/src/mcp/evidence/bun-lock-graph.mjs +0 -209
  186. package/src/mcp/evidence/duplicate-member-order.mjs +0 -8
  187. package/src/mcp/evidence/package-graph-common.mjs +0 -115
  188. package/src/mcp/evidence/package-lock-graph.mjs +0 -92
  189. package/src/mcp/evidence-snapshot.architecture.mjs +0 -80
  190. package/src/mcp/evidence-snapshot.common.mjs +0 -158
  191. package/src/mcp/evidence-snapshot.duplicates.mjs +0 -213
  192. package/src/mcp/evidence-snapshot.health.mjs +0 -102
  193. package/src/mcp/evidence-snapshot.inventory.mjs +0 -148
  194. package/src/mcp/evidence-snapshot.mjs +0 -50
  195. package/src/mcp/evidence-snapshot.package-graph.mjs +0 -34
  196. package/src/mcp/evidence-snapshot.structure.mjs +0 -84
  197. package/src/mcp/extension-api.mjs +0 -77
  198. package/src/mcp/git-output.mjs +0 -7
  199. package/src/mcp/graph/context-core.mjs +0 -111
  200. package/src/mcp/graph/context-seeds.mjs +0 -207
  201. package/src/mcp/graph/context-state.mjs +0 -85
  202. package/src/mcp/graph/reverse-reach.mjs +0 -42
  203. package/src/mcp/graph/tools-core.mjs +0 -143
  204. package/src/mcp/graph/tools-query.mjs +0 -276
  205. package/src/mcp/graph-context.mjs +0 -5
  206. package/src/mcp/graph-diff.mjs +0 -218
  207. package/src/mcp/health/audit-format.mjs +0 -199
  208. package/src/mcp/health/audit.mjs +0 -199
  209. package/src/mcp/health/dead-code.mjs +0 -110
  210. package/src/mcp/health/duplicates.mjs +0 -121
  211. package/src/mcp/health/endpoints.mjs +0 -71
  212. package/src/mcp/health/structure.mjs +0 -220
  213. package/src/mcp/runtime-version.mjs +0 -31
  214. package/src/mcp/server/auto-refresh.mjs +0 -66
  215. package/src/mcp/server/runtime-config.mjs +0 -43
  216. package/src/mcp/server/shutdown.mjs +0 -40
  217. package/src/mcp/staleness-notice.mjs +0 -20
  218. package/src/mcp/sync/evidence-architecture.mjs +0 -54
  219. package/src/mcp/sync/evidence-common.mjs +0 -88
  220. package/src/mcp/sync/evidence-duplicates.mjs +0 -97
  221. package/src/mcp/sync/evidence-health.mjs +0 -56
  222. package/src/mcp/sync/evidence-packages.mjs +0 -95
  223. package/src/mcp/sync/payload-common.mjs +0 -97
  224. package/src/mcp/sync/payload-v2.mjs +0 -54
  225. package/src/mcp/sync/payload-v3.mjs +0 -79
  226. package/src/mcp/sync-evidence.mjs +0 -23
  227. package/src/mcp/sync-payload.mjs +0 -70
  228. package/src/mcp/tool-result.mjs +0 -54
  229. package/src/mcp/tools-actions.mjs +0 -11
  230. package/src/mcp/tools-architecture.mjs +0 -177
  231. package/src/mcp/tools-company.mjs +0 -251
  232. package/src/mcp/tools-context.mjs +0 -222
  233. package/src/mcp/tools-endpoints.mjs +0 -207
  234. package/src/mcp/tools-graph-hubs.mjs +0 -78
  235. package/src/mcp/tools-graph.mjs +0 -17
  236. package/src/mcp/tools-health.mjs +0 -24
  237. package/src/mcp/tools-history.mjs +0 -28
  238. package/src/mcp/tools-impact-change.mjs +0 -220
  239. package/src/mcp/tools-impact-precision.mjs +0 -89
  240. package/src/mcp/tools-impact.mjs +0 -169
  241. package/src/mcp/tools-source.mjs +0 -238
  242. package/src/mcp/tools-verified-change.mjs +0 -200
  243. package/src/mcp-rg.mjs +0 -50
  244. package/src/mcp-runtime.mjs +0 -2
  245. package/src/mcp-server.mjs +0 -298
  246. package/src/mcp-source-tools.mjs +0 -163
  247. package/src/path-classification.js +0 -226
  248. package/src/path-ignore.js +0 -69
  249. package/src/precision/lsp-client/constants.js +0 -12
  250. package/src/precision/lsp-client/environment.js +0 -20
  251. package/src/precision/lsp-client/errors.js +0 -15
  252. package/src/precision/lsp-client/lifecycle.js +0 -114
  253. package/src/precision/lsp-client/message-parser.js +0 -81
  254. package/src/precision/lsp-client/protocol.js +0 -212
  255. package/src/precision/lsp-client/registry.js +0 -59
  256. package/src/precision/lsp-client/repo-uri.js +0 -60
  257. package/src/precision/lsp-client/stdio-client.js +0 -140
  258. package/src/precision/lsp-client.js +0 -10
  259. package/src/precision/lsp-overlay/budget-policy.js +0 -78
  260. package/src/precision/lsp-overlay/build.js +0 -261
  261. package/src/precision/lsp-overlay/contract.js +0 -61
  262. package/src/precision/lsp-overlay/reference-results.js +0 -108
  263. package/src/precision/lsp-overlay/semantic-inputs.js +0 -62
  264. package/src/precision/lsp-overlay/source-session.js +0 -142
  265. package/src/precision/lsp-overlay/store.js +0 -150
  266. package/src/precision/lsp-overlay/target-index.js +0 -152
  267. package/src/precision/lsp-overlay/target-query.js +0 -55
  268. package/src/precision/lsp-overlay.js +0 -11
  269. package/src/precision/symbol-query.js +0 -224
  270. package/src/precision/typescript-lsp-provider.js +0 -12
  271. package/src/precision/typescript-provider/client.js +0 -89
  272. package/src/precision/typescript-provider/discovery.js +0 -124
  273. package/src/precision/typescript-provider/isolated-runtime.js +0 -39
  274. package/src/precision/typescript-provider/project-host.js +0 -254
  275. package/src/precision/typescript-provider/project-safety.js +0 -166
  276. package/src/precision/typescript-provider/reference-usage.js +0 -53
  277. package/src/process.js +0 -95
  278. package/src/repo-path.js +0 -53
  279. package/src/scan/discover.inventory.js +0 -78
  280. package/src/scan/discover.js +0 -5
  281. package/src/scan/discover.list.js +0 -79
  282. package/src/scan/discover.stack.js +0 -227
  283. package/src/scan/search.core.js +0 -24
  284. package/src/scan/search.git.js +0 -102
  285. package/src/scan/search.js +0 -9
  286. package/src/scan/search.node.js +0 -83
  287. package/src/scan/search.preview.js +0 -49
  288. package/src/scan/search.rg.js +0 -145
  289. package/src/security/advisory-store.js +0 -172
  290. package/src/security/installed-jvm-rust.js +0 -46
  291. package/src/security/installed.js +0 -280
  292. package/src/security/malware-file-heuristics.js +0 -121
  293. package/src/security/malware-heuristics.exclusions.js +0 -134
  294. package/src/security/malware-heuristics.js +0 -15
  295. package/src/security/malware-heuristics.roots.js +0 -145
  296. package/src/security/malware-heuristics.scan.js +0 -89
  297. package/src/security/malware-heuristics.sweep.js +0 -122
  298. package/src/security/malware-scoring.js +0 -105
  299. package/src/security/match.js +0 -85
  300. package/src/security/registry-sig.classify.js +0 -137
  301. package/src/security/registry-sig.js +0 -18
  302. package/src/security/registry-sig.rules.js +0 -192
  303. package/src/security/rust-advisory-report.js +0 -60
  304. package/src/security/typosquat.js +0 -81
  305. package/src/util.js +0 -40
  306. package/src/version.js +0 -7
@@ -1,89 +0,0 @@
1
- import {resolve} from 'node:path'
2
- import {startStdioLspClient} from '../lsp-client.js'
3
- import {WEAVATRIX_VERSION} from '../../version.js'
4
- import {
5
- discoverTypeScriptProvider,
6
- typeScriptLanguageId,
7
- typeScriptLspContract,
8
- } from './discovery.js'
9
- import {isolateTypeScriptRuntime} from './isolated-runtime.js'
10
-
11
- /** Starts Weavatrix's bundled TypeScript server without executing repository configuration. */
12
- export async function createTypeScriptLspClient({repoRoot, timeoutMs = 10_000} = {}) {
13
- const discovered = discoverTypeScriptProvider()
14
- const absoluteRepoRoot = resolve(repoRoot)
15
- const isolated = isolateTypeScriptRuntime(discovered.tsserverPath)
16
- let client
17
- let reportedTypeScript = null
18
- try {
19
- client = await startStdioLspClient({
20
- repoRoot: absoluteRepoRoot,
21
- executablePath: process.execPath,
22
- args: [discovered.cliPath, '--stdio'],
23
- requestTimeoutMs: timeoutMs,
24
- onNotification(method, params) {
25
- if (method === '$/typescriptVersion' && params && typeof params === 'object') {
26
- reportedTypeScript = {
27
- version: typeof params.version === 'string' ? params.version : null,
28
- source: typeof params.source === 'string' ? params.source : null,
29
- }
30
- }
31
- },
32
- })
33
- await client.initialize({
34
- clientInfo: {name: 'weavatrix', version: WEAVATRIX_VERSION},
35
- capabilities: {
36
- workspace: {configuration: true, workspaceFolders: true},
37
- textDocument: {
38
- definition: {linkSupport: true},
39
- references: {},
40
- rename: {},
41
- publishDiagnostics: {relatedInformation: false},
42
- },
43
- },
44
- initializationOptions: {
45
- hostInfo: 'weavatrix',
46
- disableAutomaticTypingAcquisition: true,
47
- tsserver: {path: isolated.tsserverPath},
48
- // An explicit empty list keeps the language-server plugin manager
49
- // from adding --globalPlugins or --pluginProbeLocations. tsserver's
50
- // local-plugin loading remains disabled by default as well.
51
- plugins: [],
52
- },
53
- })
54
- } catch (error) {
55
- client?.kill(error)
56
- isolated.cleanup()
57
- throw error
58
- }
59
- return Object.freeze({
60
- provider: discovered.provider,
61
- version: discovered.version,
62
- providerContract: typeScriptLspContract(),
63
- get typescriptVersion() { return reportedTypeScript?.version || discovered.typescriptVersion },
64
- get typescriptSource() { return reportedTypeScript?.source || 'configured-bundled-path' },
65
- async openDocument(relPath, text, languageId = typeScriptLanguageId(relPath)) {
66
- if (!languageId) throw new TypeError(`Unsupported TypeScript LSP document extension: ${relPath}`)
67
- return client.openDocument({filePath: relPath, text, languageId})
68
- },
69
- references(relPath, position, includeDeclaration = true, referenceTimeoutMs = timeoutMs) {
70
- return client.references({filePath: relPath, position, includeDeclaration, timeoutMs: referenceTimeoutMs})
71
- },
72
- // Generic read-only JSON-RPC passthrough plus URI normalization, so consumers outside
73
- // the core (weavatrix-refactor) can issue their own read-only LSP requests. The client
74
- // still refuses workspace/applyEdit, so nothing here can apply an edit.
75
- request(method, params, options) { return client.request(method, params, options) },
76
- toUri(relPath) { return client.normalizer.toUri(relPath) },
77
- fromUri(uri) { return client.normalizer.fromUri(uri) },
78
- definition(relPath, position) { return client.definition({filePath: relPath, position}) },
79
- closeDocument(relPath) { return client.closeDocument(relPath) },
80
- async close(shutdownTimeoutMs = timeoutMs) {
81
- try { return await client.shutdown({timeoutMs: shutdownTimeoutMs}) }
82
- finally { isolated.cleanup() }
83
- },
84
- kill() {
85
- try { client.kill() }
86
- finally { isolated.cleanup() }
87
- },
88
- })
89
- }
@@ -1,124 +0,0 @@
1
- import {existsSync, readFileSync, realpathSync} from 'node:fs'
2
- import {dirname, extname, isAbsolute, join} from 'node:path'
3
- import {createRequire} from 'node:module'
4
-
5
- const requireFromWeavatrix = createRequire(import.meta.url)
6
- const PROVIDER = 'typescript-language-server'
7
- export const TYPESCRIPT_LSP_CAPABILITY_CONTRACT = 'typescript-references-v4-plugin-suppression'
8
- let discoveredProvider = null
9
-
10
- function resolveOwn(specifier) {
11
- const resolved = requireFromWeavatrix.resolve(specifier)
12
- if (!isAbsolute(resolved)) throw new Error(`Resolved dependency path is not absolute: ${specifier}`)
13
- return realpathSync.native(resolved)
14
- }
15
-
16
- function packageInfoFrom(startPath, expectedName) {
17
- let directory = dirname(startPath)
18
- for (let depth = 0; depth < 10; depth++) {
19
- const packagePath = join(directory, 'package.json')
20
- if (existsSync(packagePath)) {
21
- try {
22
- const manifest = JSON.parse(readFileSync(packagePath, 'utf8'))
23
- if (manifest.name === expectedName) {
24
- return {directory, manifest, packagePath: realpathSync.native(packagePath)}
25
- }
26
- } catch { /* continue upward */ }
27
- }
28
- const parent = dirname(directory)
29
- if (parent === directory) break
30
- directory = parent
31
- }
32
- throw new Error(`Could not locate ${expectedName} package metadata`)
33
- }
34
-
35
- function resolveServerCli() {
36
- const candidates = [
37
- 'typescript-language-server',
38
- 'typescript-language-server/lib/cli.mjs',
39
- 'typescript-language-server/lib/cli.js',
40
- ]
41
- let lastError
42
- for (const candidate of candidates) {
43
- try {
44
- const path = resolveOwn(candidate)
45
- if (existsSync(path)) return path
46
- } catch (error) { lastError = error }
47
- }
48
- throw lastError || new Error('typescript-language-server CLI was not found')
49
- }
50
-
51
- function resolveTypeScriptServer() {
52
- const candidates = ['typescript/lib/tsserver.js', 'typescript/lib/_tsserver.js']
53
- let lastError
54
- for (const candidate of candidates) {
55
- try {
56
- const path = resolveOwn(candidate)
57
- if (existsSync(path)) return path
58
- } catch (error) { lastError = error }
59
- }
60
- try {
61
- const typescriptEntry = resolveOwn('typescript')
62
- for (const name of ['tsserver.js', '_tsserver.js']) {
63
- const candidate = join(dirname(typescriptEntry), name)
64
- if (existsSync(candidate)) return realpathSync.native(candidate)
65
- }
66
- } catch (error) { lastError = error }
67
- throw lastError || new Error('Bundled TypeScript tsserver was not found')
68
- }
69
-
70
- export function discoverTypeScriptProvider() {
71
- if (discoveredProvider) return discoveredProvider
72
- const cliPath = resolveServerCli()
73
- const tsserverPath = resolveTypeScriptServer()
74
- const serverPackage = packageInfoFrom(cliPath, PROVIDER)
75
- const typescriptPackage = packageInfoFrom(tsserverPath, 'typescript')
76
- discoveredProvider = Object.freeze({
77
- available: true,
78
- provider: PROVIDER,
79
- version: String(serverPackage.manifest.version || 'unknown'),
80
- typescriptVersion: String(typescriptPackage.manifest.version || 'unknown'),
81
- cliPath,
82
- tsserverPath,
83
- })
84
- return discoveredProvider
85
- }
86
-
87
- export function typeScriptLspAvailability() {
88
- try {
89
- const result = discoverTypeScriptProvider()
90
- return {
91
- available: true,
92
- provider: result.provider,
93
- version: result.version,
94
- typescriptVersion: result.typescriptVersion,
95
- }
96
- } catch (error) {
97
- return {
98
- available: false,
99
- provider: PROVIDER,
100
- version: null,
101
- typescriptVersion: null,
102
- reason: error?.code === 'MODULE_NOT_FOUND' ? 'DEPENDENCY_NOT_INSTALLED' : 'DISCOVERY_FAILED',
103
- }
104
- }
105
- }
106
-
107
- export function typeScriptLspContract() {
108
- const availability = typeScriptLspAvailability()
109
- return [
110
- TYPESCRIPT_LSP_CAPABILITY_CONTRACT,
111
- `${PROVIDER}@${availability.version || 'unavailable'}`,
112
- `typescript@${availability.typescriptVersion || 'unavailable'}`,
113
- `runtime@${process.platform}-${process.arch}-node${String(process.versions.node || '0').split('.')[0]}`,
114
- ].join('|')
115
- }
116
-
117
- export function typeScriptLanguageId(filePath) {
118
- const extension = extname(String(filePath)).toLowerCase()
119
- if (extension === '.ts' || extension === '.mts' || extension === '.cts') return 'typescript'
120
- if (extension === '.tsx') return 'typescriptreact'
121
- if (extension === '.js' || extension === '.mjs' || extension === '.cjs') return 'javascript'
122
- if (extension === '.jsx') return 'javascriptreact'
123
- return null
124
- }
@@ -1,39 +0,0 @@
1
- import {copyFileSync, linkSync, mkdirSync, mkdtempSync, readdirSync, rmSync} from 'node:fs'
2
- import {tmpdir} from 'node:os'
3
- import {basename, dirname, join} from 'node:path'
4
-
5
- const RUNTIME_FILES = new Set([
6
- 'tsserver.js', '_tsserver.js', 'typescript.js',
7
- 'typingsInstaller.js', '_typingsInstaller.js', 'typesMap.json', 'watchGuard.js',
8
- ])
9
-
10
- function materialize(source, target) {
11
- try { linkSync(source, target) }
12
- catch { copyFileSync(source, target) }
13
- }
14
-
15
- export function isolateTypeScriptRuntime(tsserverPath) {
16
- const sourceLib = dirname(tsserverPath)
17
- const sourceRoot = dirname(sourceLib)
18
- const root = mkdtempSync(join(tmpdir(), 'weavatrix-tsserver-'))
19
- const lib = join(root, 'lib')
20
- mkdirSync(lib, {recursive: true, mode: 0o700})
21
- try {
22
- materialize(join(sourceRoot, 'package.json'), join(root, 'package.json'))
23
- const files = readdirSync(sourceLib).filter((name) =>
24
- RUNTIME_FILES.has(name) || /^lib(?:\..+)?\.d\.ts$/i.test(name))
25
- for (const name of files) materialize(join(sourceLib, name), join(lib, basename(name)))
26
- let cleaned = false
27
- return {
28
- tsserverPath: join(lib, 'tsserver.js'),
29
- cleanup() {
30
- if (cleaned) return
31
- cleaned = true
32
- try { rmSync(root, {recursive: true, force: true}) } catch { /* process cleanup fallback */ }
33
- },
34
- }
35
- } catch (error) {
36
- try { rmSync(root, {recursive: true, force: true}) } catch { /* best effort */ }
37
- throw error
38
- }
39
- }
@@ -1,254 +0,0 @@
1
- import {createHash} from 'node:crypto'
2
- import {existsSync, opendirSync, readFileSync, realpathSync, statSync} from 'node:fs'
3
- import {dirname, isAbsolute, join, relative, resolve} from 'node:path'
4
- import {createRequire} from 'node:module'
5
- import {isPathInside} from '../../repo-path.js'
6
-
7
- const requireFromWeavatrix = createRequire(import.meta.url)
8
- const MAX_PROJECT_FILES = 8_192
9
- const MAX_CONFIG_FILES = 128
10
- const MAX_CONFIG_BYTES = 8 * 1024 * 1024
11
- const MAX_DIRECTORY_ENTRIES = 32_768
12
- const MAX_DIRECTORIES = 4_096
13
- const DEFAULT_SAFETY_TIMEOUT_MS = 5_000
14
- export const norm = (value) => String(value || '').replace(/\\/g, '/').replace(/^\.\//, '')
15
-
16
- export function canonicalKey(path, caseSensitive) {
17
- let canonical
18
- try { canonical = realpathSync.native(path) } catch { return null }
19
- return caseSensitive ? canonical : canonical.toLowerCase()
20
- }
21
-
22
- export function typeScriptRepoContext(repoRoot) {
23
- let root
24
- let ts
25
- try {
26
- root = realpathSync.native(repoRoot)
27
- ts = requireFromWeavatrix('typescript')
28
- } catch { return null }
29
- return {root, ts, caseSensitive: Boolean(ts.sys.useCaseSensitiveFileNames)}
30
- }
31
-
32
- export function guardedExistingPath(root, candidate, state = null) {
33
- const input = String(candidate || '')
34
- const lexical = isAbsolute(input) ? resolve(input) : resolve(root, input)
35
- if (!isPathInside(root, lexical)) {
36
- if (state) state.outsideAccess = true
37
- return null
38
- }
39
- try {
40
- const canonical = realpathSync.native(lexical)
41
- if (!isPathInside(root, canonical)) {
42
- if (state) state.outsideAccess = true
43
- return null
44
- }
45
- return canonical
46
- } catch { return null }
47
- }
48
-
49
- export function nearestTypeScriptConfig(context, absoluteFile) {
50
- const {root} = context
51
- let directory = dirname(absoluteFile)
52
- while (isPathInside(root, directory)) {
53
- for (const name of ['tsconfig.json', 'jsconfig.json']) {
54
- const candidate = join(directory, name)
55
- if (!existsSync(candidate)) continue
56
- const configPath = guardedExistingPath(root, candidate)
57
- return configPath ? {ok: true, configPath} : {ok: false, reason: 'CONFIG_OUTSIDE_REPOSITORY'}
58
- }
59
- if (directory === root) break
60
- const parent = dirname(directory)
61
- if (parent === directory) break
62
- directory = parent
63
- }
64
- return {ok: true, configPath: null}
65
- }
66
-
67
- export function safetyBudget(options = {}) {
68
- const requestedDeadline = Number(options.deadline)
69
- const requestedTimeout = Number(options.timeoutMs)
70
- const timeout = Number.isFinite(requestedTimeout)
71
- ? Math.max(100, Math.min(60_000, Math.floor(requestedTimeout)))
72
- : DEFAULT_SAFETY_TIMEOUT_MS
73
- return {
74
- deadline: Number.isFinite(requestedDeadline) ? requestedDeadline : Date.now() + timeout,
75
- maxEntries: Math.max(1, Math.min(MAX_DIRECTORY_ENTRIES,
76
- Number.isFinite(Number(options.maxDirectoryEntries))
77
- ? Math.floor(Number(options.maxDirectoryEntries)) : MAX_DIRECTORY_ENTRIES)),
78
- maxDirectories: Math.max(1, Math.min(MAX_DIRECTORIES,
79
- Number.isFinite(Number(options.maxDirectories))
80
- ? Math.floor(Number(options.maxDirectories)) : MAX_DIRECTORIES)),
81
- entries: 0,
82
- directories: 0,
83
- configBytes: 0,
84
- configFiles: new Set(),
85
- reason: null,
86
- }
87
- }
88
-
89
- function safetyLimitReached(budget, reason = 'PROJECT_INPUT_LIMIT') {
90
- if (!budget.reason && Date.now() >= budget.deadline) budget.reason = 'SAFETY_DEADLINE'
91
- if (!budget.reason && reason) budget.reason = reason
92
- return Boolean(budget.reason)
93
- }
94
-
95
- function boundedReadDirectory(context, state, budget, candidate, extensions, excludes, includes, depth) {
96
- const {root, ts, caseSensitive} = context
97
- const path = guardedExistingPath(root, candidate, state)
98
- if (!path || safetyLimitReached(budget, null)) return []
99
- const getFileSystemEntries = (directoryPath) => {
100
- if (safetyLimitReached(budget, null)) return {files: [], directories: []}
101
- const directory = guardedExistingPath(root, directoryPath, state)
102
- if (!directory) return {files: [], directories: []}
103
- budget.directories++
104
- if (budget.directories > budget.maxDirectories) {
105
- safetyLimitReached(budget)
106
- return {files: [], directories: []}
107
- }
108
- const files = []
109
- const directories = []
110
- let handle
111
- try {
112
- handle = opendirSync(directory)
113
- while (!safetyLimitReached(budget, null)) {
114
- const entry = handle.readSync()
115
- if (!entry) break
116
- budget.entries++
117
- if (budget.entries > budget.maxEntries) {
118
- safetyLimitReached(budget)
119
- break
120
- }
121
- if (entry.isFile()) files.push(entry.name)
122
- else if (entry.isDirectory()) directories.push(entry.name)
123
- else if (entry.isSymbolicLink()) {
124
- const linked = guardedExistingPath(root, join(directory, entry.name), state)
125
- if (!linked) continue
126
- let stats
127
- try { stats = statSync(linked) } catch { continue }
128
- if (stats.isFile()) files.push(entry.name)
129
- else if (stats.isDirectory()) directories.push(entry.name)
130
- }
131
- }
132
- } catch {
133
- return {files: [], directories: []}
134
- } finally {
135
- try { handle?.closeSync() } catch {}
136
- }
137
- return {files, directories}
138
- }
139
- const guardedRealpath = (entry) => guardedExistingPath(root, entry, state)
140
- || resolve(root, '.weavatrix-invalid-path')
141
- try {
142
- return ts.matchFiles(
143
- path, extensions, excludes, includes, caseSensitive, root, depth,
144
- getFileSystemEntries, guardedRealpath,
145
- )
146
- } catch {
147
- state.limitExceeded = true
148
- return []
149
- }
150
- }
151
-
152
- export function parseRepoConfig(context, configPath, budget) {
153
- const {root, ts, caseSensitive} = context
154
- const state = {
155
- outsideAccess: false,
156
- limitExceeded: false,
157
- configuredPlugins: new Set(),
158
- bytes: 0,
159
- records: new Map(),
160
- }
161
- const diagnostics = []
162
- const host = {
163
- useCaseSensitiveFileNames: caseSensitive,
164
- getCurrentDirectory: () => root,
165
- fileExists(candidate) {
166
- const path = guardedExistingPath(root, candidate, state)
167
- return Boolean(path && ts.sys.fileExists(path))
168
- },
169
- readFile(candidate) {
170
- if (safetyLimitReached(budget, null)) return undefined
171
- const path = guardedExistingPath(root, candidate, state)
172
- if (!path) return undefined
173
- let body
174
- try {
175
- const size = statSync(path).size
176
- if (size > MAX_CONFIG_BYTES || state.bytes + size > MAX_CONFIG_BYTES) {
177
- state.limitExceeded = true
178
- return undefined
179
- }
180
- body = readFileSync(path)
181
- } catch { return undefined }
182
- const rel = norm(relative(root, path))
183
- if (!state.records.has(rel)) {
184
- if (!budget.configFiles.has(rel)
185
- && (budget.configFiles.size >= MAX_CONFIG_FILES
186
- || budget.configBytes + body.byteLength > MAX_CONFIG_BYTES)) {
187
- state.limitExceeded = true
188
- safetyLimitReached(budget, 'CONFIG_INPUT_LIMIT')
189
- return undefined
190
- }
191
- if (!budget.configFiles.has(rel)) {
192
- budget.configFiles.add(rel)
193
- budget.configBytes += body.byteLength
194
- }
195
- state.records.set(rel, createHash('sha256').update(body).digest('hex'))
196
- state.bytes += body.byteLength
197
- }
198
- try {
199
- const raw = ts.parseConfigFileTextToJson(path, body.toString('utf8')).config
200
- for (const plugin of raw?.compilerOptions?.plugins || []) {
201
- const name = typeof plugin?.name === 'string' ? plugin.name.trim() : ''
202
- if (name) state.configuredPlugins.add(name.slice(0, 256))
203
- }
204
- } catch { /* parser diagnostics below decide safety */ }
205
- return body.toString('utf8')
206
- },
207
- readDirectory(candidate, extensions, excludes, includes, depth) {
208
- return boundedReadDirectory(context, state, budget, candidate, extensions, excludes, includes, depth)
209
- },
210
- onUnRecoverableConfigFileDiagnostic(diagnostic) { diagnostics.push(diagnostic) },
211
- trace() {},
212
- }
213
- let parsed
214
- try { parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, host) }
215
- catch { return {complete: false, reason: 'CONFIG_PARSE_FAILED'} }
216
- const errors = [...diagnostics, ...(parsed?.errors || [])]
217
- for (const plugin of parsed?.options?.plugins || []) {
218
- const name = typeof plugin?.name === 'string' ? plugin.name.trim() : ''
219
- if (name) state.configuredPlugins.add(name.slice(0, 256))
220
- }
221
- if (budget.reason) return {complete: false, reason: budget.reason}
222
- if (!parsed || state.outsideAccess || state.limitExceeded
223
- || errors.some((diagnostic) => diagnostic?.category === ts.DiagnosticCategory.Error)) {
224
- return {
225
- complete: false,
226
- reason: state.outsideAccess
227
- ? 'CONFIG_OUTSIDE_REPOSITORY'
228
- : state.limitExceeded ? 'CONFIG_INPUT_LIMIT' : 'CONFIG_PARSE_FAILED',
229
- }
230
- }
231
- const projectFiles = []
232
- const projectKeys = new Set()
233
- for (const file of parsed.fileNames) {
234
- const canonical = guardedExistingPath(root, file, state)
235
- if (!canonical) return {complete: false, reason: 'PROJECT_INPUT_OUTSIDE_REPOSITORY'}
236
- const key = caseSensitive ? canonical : canonical.toLowerCase()
237
- if (projectKeys.has(key)) continue
238
- projectKeys.add(key)
239
- projectFiles.push(norm(relative(root, canonical)))
240
- if (projectFiles.length > MAX_PROJECT_FILES) return {complete: false, reason: 'PROJECT_INPUT_LIMIT'}
241
- }
242
- projectFiles.sort()
243
- return {
244
- complete: true,
245
- parsed,
246
- projectFiles,
247
- projectKeys,
248
- configRecords: state.records,
249
- // The bundled provider runs an isolated tsserver copy whose plugin search
250
- // root contains only TypeScript runtime files. Keep configured plugin names
251
- // as evidence, but never execute them from the inspected repository/bundle.
252
- plugins: [...state.configuredPlugins].sort(),
253
- }
254
- }
@@ -1,166 +0,0 @@
1
- import {createHash} from 'node:crypto'
2
- import {readFileSync, statSync} from 'node:fs'
3
- import {relative} from 'node:path'
4
- import {
5
- canonicalKey,
6
- guardedExistingPath,
7
- nearestTypeScriptConfig,
8
- norm,
9
- parseRepoConfig,
10
- safetyBudget,
11
- typeScriptRepoContext,
12
- } from './project-host.js'
13
-
14
- const MAX_PROJECT_FILES = 8_192
15
- const MAX_CONFIG_FILES = 128
16
- const MAX_EXTRA_INPUT_BYTES = 64 * 1024 * 1024
17
- const MAX_PROJECT_INPUT_BYTES = 128 * 1024 * 1024
18
- const MAX_SINGLE_INPUT_BYTES = 4 * 1024 * 1024
19
-
20
- function referenceConfigPath(context, reference) {
21
- const {root, ts} = context
22
- let candidate
23
- try {
24
- candidate = typeof ts.resolveProjectReferencePath === 'function'
25
- ? ts.resolveProjectReferencePath(reference)
26
- : reference?.path
27
- } catch { return null }
28
- if (!candidate) return null
29
- return guardedExistingPath(root, candidate)
30
- }
31
-
32
- export function typeScriptProjectSafety(repoRoot, relFiles = [], options = {}) {
33
- const context = typeScriptRepoContext(repoRoot)
34
- if (!context) return {safe: false, reason: 'TYPESCRIPT_UNAVAILABLE', fingerprint: null}
35
- const {root} = context
36
- const budget = safetyBudget(options)
37
- const files = [...new Set((relFiles || []).map(norm).filter(Boolean))].sort()
38
- if (files.length > MAX_PROJECT_FILES) return {safe: false, reason: 'PROJECT_INPUT_LIMIT', fingerprint: null}
39
- const graphFiles = new Set(files)
40
- const mappings = []
41
- const fileConfigs = {}
42
- const queue = []
43
- const queued = new Set()
44
- for (const file of files) {
45
- const absolute = guardedExistingPath(root, file)
46
- if (!absolute) return {safe: false, reason: 'UNREADABLE_PROJECT_INPUT', fingerprint: null}
47
- const nearest = nearestTypeScriptConfig(context, absolute)
48
- if (!nearest.ok) return {safe: false, reason: nearest.reason, fingerprint: null}
49
- const configRel = nearest.configPath ? norm(relative(root, nearest.configPath)) : '<inferred>'
50
- mappings.push(`${file}=>${configRel}`)
51
- fileConfigs[file] = nearest.configPath ? configRel : null
52
- if (nearest.configPath && !queued.has(nearest.configPath)) {
53
- queued.add(nearest.configPath)
54
- queue.push(nearest.configPath)
55
- }
56
- }
57
-
58
- const configRecords = new Map()
59
- const projectFiles = new Set()
60
- const configuredPlugins = new Set()
61
- const projects = {}
62
- for (let cursor = 0; cursor < queue.length; cursor++) {
63
- if (Date.now() >= budget.deadline) return {safe: false, reason: 'SAFETY_DEADLINE', fingerprint: null}
64
- if (queue.length > MAX_CONFIG_FILES) return {safe: false, reason: 'CONFIG_INPUT_LIMIT', fingerprint: null}
65
- const configPath = queue[cursor]
66
- const parsed = parseRepoConfig(context, configPath, budget)
67
- if (!parsed.complete) return {safe: false, reason: parsed.reason, fingerprint: null}
68
- const configRel = norm(relative(root, configPath))
69
- projects[configRel] = {
70
- projectFiles: parsed.projectFiles,
71
- configFiles: [...parsed.configRecords.keys()].sort(),
72
- configuredPlugins: parsed.plugins,
73
- }
74
- for (const plugin of parsed.plugins || []) configuredPlugins.add(plugin)
75
- for (const [file, digest] of parsed.configRecords) {
76
- configRecords.set(file, digest)
77
- if (configRecords.size > MAX_CONFIG_FILES) {
78
- return {safe: false, reason: 'CONFIG_INPUT_LIMIT', fingerprint: null}
79
- }
80
- }
81
- for (const file of parsed.projectFiles) {
82
- projectFiles.add(file)
83
- if (projectFiles.size > MAX_PROJECT_FILES) {
84
- return {safe: false, reason: 'PROJECT_INPUT_LIMIT', fingerprint: null}
85
- }
86
- }
87
- for (const reference of parsed.parsed.projectReferences || []) {
88
- const referenced = referenceConfigPath(context, reference)
89
- if (!referenced) return {safe: false, reason: 'PROJECT_REFERENCE_UNRESOLVED', fingerprint: null}
90
- if (!queued.has(referenced)) {
91
- queued.add(referenced)
92
- queue.push(referenced)
93
- }
94
- }
95
- }
96
-
97
- const extraRecords = []
98
- let projectBytes = 0
99
- let extraBytes = 0
100
- for (const file of [...projectFiles].sort()) {
101
- if (Date.now() >= budget.deadline) return {safe: false, reason: 'SAFETY_DEADLINE', fingerprint: null}
102
- const absolute = guardedExistingPath(root, file)
103
- if (!absolute) return {safe: false, reason: 'UNREADABLE_PROJECT_INPUT', fingerprint: null}
104
- let size
105
- try {
106
- const stats = statSync(absolute)
107
- if (!stats.isFile()) return {safe: false, reason: 'UNREADABLE_PROJECT_INPUT', fingerprint: null}
108
- size = stats.size
109
- if (size > MAX_SINGLE_INPUT_BYTES || projectBytes + size > MAX_PROJECT_INPUT_BYTES) {
110
- return {safe: false, reason: 'PROJECT_INPUT_LIMIT', fingerprint: null}
111
- }
112
- } catch { return {safe: false, reason: 'UNREADABLE_PROJECT_INPUT', fingerprint: null} }
113
- projectBytes += size
114
- if (graphFiles.has(file)) continue
115
- if (extraBytes + size > MAX_EXTRA_INPUT_BYTES) {
116
- return {safe: false, reason: 'PROJECT_INPUT_LIMIT', fingerprint: null}
117
- }
118
- let body
119
- try { body = readFileSync(absolute) }
120
- catch { return {safe: false, reason: 'UNREADABLE_PROJECT_INPUT', fingerprint: null} }
121
- extraBytes += body.byteLength
122
- extraRecords.push(`${file}:${createHash('sha256').update(body).digest('hex')}`)
123
- }
124
- const fingerprint = createHash('sha256').update([
125
- ...mappings.map((item) => `map:${item}`),
126
- ...[...configRecords.entries()].sort(([a], [b]) => a.localeCompare(b))
127
- .map(([file, digest]) => `config:${file}:${digest}`),
128
- ...[...projectFiles].sort().map((file) => `project:${file}`),
129
- ...extraRecords.map((item) => `extra:${item}`),
130
- ].join('\n')).digest('hex')
131
- return {
132
- safe: true,
133
- reason: null,
134
- fingerprint,
135
- configFiles: [...configRecords.keys()].sort(),
136
- projectFiles: [...projectFiles].sort(),
137
- fileConfigs,
138
- projects,
139
- configuredPlugins: [...configuredPlugins].sort(),
140
- pluginsSuppressed: configuredPlugins.size,
141
- }
142
- }
143
-
144
- export function typeScriptConfiguredProjectMembership(repoRoot, relFile) {
145
- const context = typeScriptRepoContext(repoRoot)
146
- if (!context) return {complete: false, member: false, reason: 'TYPESCRIPT_UNAVAILABLE'}
147
- const {root, caseSensitive} = context
148
- const target = guardedExistingPath(root, relFile)
149
- if (!target) return {complete: false, member: false, reason: 'UNREADABLE_PATH'}
150
- const nearest = nearestTypeScriptConfig(context, target)
151
- if (!nearest.ok) return {complete: false, member: false, reason: nearest.reason}
152
- const configPath = nearest.configPath
153
- if (!configPath) return {complete: false, member: false, reason: 'NO_CONFIGURED_PROJECT'}
154
- const parsed = parseRepoConfig(context, configPath, safetyBudget())
155
- if (!parsed.complete) return {complete: false, member: false, reason: parsed.reason}
156
- const targetKey = canonicalKey(target, caseSensitive)
157
- const member = parsed.projectKeys.has(targetKey)
158
- return {
159
- complete: true,
160
- member,
161
- projectFiles: parsed.projectFiles,
162
- configFiles: [...parsed.configRecords.keys()].sort(),
163
- configFile: norm(relative(root, configPath)),
164
- reason: member ? null : 'NOT_IN_CONFIGURED_PROJECT',
165
- }
166
- }