weavatrix 0.3.14 → 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.
Files changed (305) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +231 -199
  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 +8 -66
  14. package/skill/SKILL.md +62 -374
  15. package/skill/agents/openai.yaml +4 -0
  16. package/skill/references/tool-routing.md +45 -0
  17. package/SECURITY.md +0 -101
  18. package/docs/adr/0001-v0.3-offline-online-split.md +0 -60
  19. package/docs/agent-task-benchmark.md +0 -58
  20. package/docs/releases/v0.2.19.md +0 -33
  21. package/docs/releases/v0.3.0.md +0 -43
  22. package/docs/releases/v0.3.1.md +0 -30
  23. package/docs/releases/v0.3.10.md +0 -76
  24. package/docs/releases/v0.3.11.md +0 -55
  25. package/docs/releases/v0.3.12.md +0 -44
  26. package/docs/releases/v0.3.13.md +0 -34
  27. package/docs/releases/v0.3.14.md +0 -37
  28. package/docs/releases/v0.3.2.md +0 -15
  29. package/docs/releases/v0.3.3.md +0 -25
  30. package/docs/releases/v0.3.4.md +0 -40
  31. package/docs/releases/v0.3.5.md +0 -50
  32. package/docs/releases/v0.3.6.md +0 -44
  33. package/docs/releases/v0.3.7.md +0 -37
  34. package/docs/releases/v0.3.8.md +0 -21
  35. package/docs/releases/v0.3.9.md +0 -36
  36. package/scripts/run-agent-task-benchmark.mjs +0 -118
  37. package/src/analysis/allowed-test-runner.js +0 -70
  38. package/src/analysis/architecture/contract-graph.js +0 -119
  39. package/src/analysis/architecture/contract-schema.js +0 -110
  40. package/src/analysis/architecture/contract-storage.js +0 -35
  41. package/src/analysis/architecture/contract-verification.js +0 -168
  42. package/src/analysis/architecture-contract.js +0 -16
  43. package/src/analysis/audit-debt.js +0 -94
  44. package/src/analysis/audit-extensions.js +0 -60
  45. package/src/analysis/cargo-dependency-evidence.js +0 -108
  46. package/src/analysis/cargo-manifests.js +0 -81
  47. package/src/analysis/change-classification/diff-parser.js +0 -103
  48. package/src/analysis/change-classification/options.js +0 -40
  49. package/src/analysis/change-classification/symbol-classifier.js +0 -177
  50. package/src/analysis/change-classification.js +0 -188
  51. package/src/analysis/coverage-reports-tarpaulin.js +0 -112
  52. package/src/analysis/coverage-reports.js +0 -246
  53. package/src/analysis/cycle-route.js +0 -14
  54. package/src/analysis/data-flow-evidence.js +0 -114
  55. package/src/analysis/dead-check.js +0 -300
  56. package/src/analysis/dead-code-review/candidates.js +0 -194
  57. package/src/analysis/dead-code-review/policy.js +0 -23
  58. package/src/analysis/dead-code-review.js +0 -119
  59. package/src/analysis/dep-check-ecosystems.js +0 -205
  60. package/src/analysis/dep-check.js +0 -290
  61. package/src/analysis/dep-rules.js +0 -10
  62. package/src/analysis/dependency/conventions.js +0 -16
  63. package/src/analysis/dependency/scoped-dependencies.js +0 -51
  64. package/src/analysis/dependency/source-references.js +0 -21
  65. package/src/analysis/duplicate-groups.js +0 -104
  66. package/src/analysis/duplicates-worker.js +0 -11
  67. package/src/analysis/duplicates.compute.js +0 -269
  68. package/src/analysis/duplicates.js +0 -15
  69. package/src/analysis/duplicates.run.js +0 -52
  70. package/src/analysis/duplicates.tokenize.js +0 -199
  71. package/src/analysis/endpoints/common.js +0 -79
  72. package/src/analysis/endpoints/extract.js +0 -120
  73. package/src/analysis/endpoints/inventory.js +0 -84
  74. package/src/analysis/endpoints/mounts.js +0 -129
  75. package/src/analysis/endpoints-java.js +0 -260
  76. package/src/analysis/endpoints-rust.js +0 -124
  77. package/src/analysis/endpoints.js +0 -3
  78. package/src/analysis/findings.js +0 -70
  79. package/src/analysis/git-history/analytics.js +0 -177
  80. package/src/analysis/git-history/collector.js +0 -67
  81. package/src/analysis/git-history/options.js +0 -66
  82. package/src/analysis/git-history.js +0 -143
  83. package/src/analysis/git-ref-graph.js +0 -76
  84. package/src/analysis/go-dependency-evidence.js +0 -62
  85. package/src/analysis/graph-analysis.aggregate.js +0 -296
  86. package/src/analysis/graph-analysis.edges.js +0 -24
  87. package/src/analysis/graph-analysis.js +0 -7
  88. package/src/analysis/graph-analysis.refs.js +0 -202
  89. package/src/analysis/graph-analysis.summaries.js +0 -65
  90. package/src/analysis/health-capabilities.js +0 -83
  91. package/src/analysis/hot-path-review.js +0 -266
  92. package/src/analysis/http-contract-wrappers.js +0 -241
  93. package/src/analysis/http-contracts/analysis.js +0 -179
  94. package/src/analysis/http-contracts/client-call-detection.js +0 -82
  95. package/src/analysis/http-contracts/client-call-parser.js +0 -273
  96. package/src/analysis/http-contracts/graph-context.js +0 -143
  97. package/src/analysis/http-contracts/matching.js +0 -61
  98. package/src/analysis/http-contracts/shared.js +0 -85
  99. package/src/analysis/http-contracts.js +0 -6
  100. package/src/analysis/internal-audit/dependency-health.js +0 -113
  101. package/src/analysis/internal-audit/python-manifests.js +0 -78
  102. package/src/analysis/internal-audit/repo-files.js +0 -50
  103. package/src/analysis/internal-audit/supply-chain.js +0 -149
  104. package/src/analysis/internal-audit.collect.js +0 -213
  105. package/src/analysis/internal-audit.js +0 -9
  106. package/src/analysis/internal-audit.reach.js +0 -201
  107. package/src/analysis/internal-audit.run.js +0 -256
  108. package/src/analysis/java-source.js +0 -36
  109. package/src/analysis/jvm-artifact-index.js +0 -87
  110. package/src/analysis/jvm-dependency-evidence.js +0 -138
  111. package/src/analysis/jvm-manifests.js +0 -114
  112. package/src/analysis/manifests.js +0 -123
  113. package/src/analysis/package-reachability.js +0 -39
  114. package/src/analysis/source-complexity.ast.js +0 -97
  115. package/src/analysis/source-complexity.constants.js +0 -55
  116. package/src/analysis/source-complexity.js +0 -14
  117. package/src/analysis/source-complexity.report.js +0 -81
  118. package/src/analysis/source-complexity.walk.js +0 -228
  119. package/src/analysis/source-correctness/retry-patterns.js +0 -93
  120. package/src/analysis/source-correctness.js +0 -222
  121. package/src/analysis/static-test-reachability.js +0 -150
  122. package/src/analysis/structure/dependency-graph.js +0 -151
  123. package/src/analysis/structure/findings.js +0 -162
  124. package/src/analysis/task-retrieval.js +0 -107
  125. package/src/analysis/transport-contracts.js +0 -224
  126. package/src/analysis/transport-runtime-evidence.js +0 -155
  127. package/src/analysis-kit.mjs +0 -17
  128. package/src/bounds.js +0 -4
  129. package/src/build-graph.js +0 -209
  130. package/src/child-env.js +0 -7
  131. package/src/config.js +0 -5
  132. package/src/extension/local-services.mjs +0 -75
  133. package/src/git-exec.js +0 -67
  134. package/src/graph/build-worker.js +0 -49
  135. package/src/graph/builder/go-receiver-resolution.js +0 -112
  136. package/src/graph/builder/js/dynamic-import.js +0 -26
  137. package/src/graph/builder/js/queries.js +0 -48
  138. package/src/graph/builder/lang-csharp.js +0 -40
  139. package/src/graph/builder/lang-css.js +0 -29
  140. package/src/graph/builder/lang-go.js +0 -140
  141. package/src/graph/builder/lang-html.js +0 -29
  142. package/src/graph/builder/lang-java-calls.js +0 -142
  143. package/src/graph/builder/lang-java.js +0 -215
  144. package/src/graph/builder/lang-js.js +0 -291
  145. package/src/graph/builder/lang-python.js +0 -235
  146. package/src/graph/builder/lang-rust-calls.js +0 -63
  147. package/src/graph/builder/lang-rust.js +0 -299
  148. package/src/graph/builder/lang-solidity.js +0 -126
  149. package/src/graph/builder/lang-sql.js +0 -263
  150. package/src/graph/builder/pass2-resolution.js +0 -81
  151. package/src/graph/builder/spec-pkg.js +0 -87
  152. package/src/graph/community.js +0 -27
  153. package/src/graph/edge-provenance.js +0 -31
  154. package/src/graph/file-lock.js +0 -69
  155. package/src/graph/freshness-probe.js +0 -142
  156. package/src/graph/graph-filter.js +0 -63
  157. package/src/graph/incremental-refresh.js +0 -235
  158. package/src/graph/internal-builder.barrels.js +0 -235
  159. package/src/graph/internal-builder.build.js +0 -300
  160. package/src/graph/internal-builder.java.js +0 -33
  161. package/src/graph/internal-builder.js +0 -12
  162. package/src/graph/internal-builder.langs.js +0 -157
  163. package/src/graph/internal-builder.pass2.js +0 -163
  164. package/src/graph/internal-builder.resolvers.js +0 -286
  165. package/src/graph/layout.js +0 -51
  166. package/src/graph/node-id.js +0 -7
  167. package/src/graph/parser-artifact-boundary.js +0 -55
  168. package/src/graph/relations.js +0 -4
  169. package/src/graph/repo-registry.js +0 -124
  170. package/src/graph/resolvers/rust.js +0 -117
  171. package/src/infra/infra-items.js +0 -255
  172. package/src/infra/infra-registry.js +0 -184
  173. package/src/infra/infra.detect.js +0 -119
  174. package/src/infra/infra.js +0 -38
  175. package/src/infra/infra.match.js +0 -102
  176. package/src/infra/infra.scan.js +0 -93
  177. package/src/mcp/actions/graph-lifecycle.mjs +0 -148
  178. package/src/mcp/architecture-bootstrap.mjs +0 -168
  179. package/src/mcp/architecture-starter.mjs +0 -234
  180. package/src/mcp/catalog.mjs +0 -233
  181. package/src/mcp/company-contract-page.mjs +0 -142
  182. package/src/mcp/company-contract-reconcile.mjs +0 -87
  183. package/src/mcp/company-contract-verdict.mjs +0 -42
  184. package/src/mcp/evidence/bun-lock-graph.mjs +0 -209
  185. package/src/mcp/evidence/duplicate-member-order.mjs +0 -8
  186. package/src/mcp/evidence/package-graph-common.mjs +0 -115
  187. package/src/mcp/evidence/package-lock-graph.mjs +0 -92
  188. package/src/mcp/evidence-snapshot.architecture.mjs +0 -80
  189. package/src/mcp/evidence-snapshot.common.mjs +0 -158
  190. package/src/mcp/evidence-snapshot.duplicates.mjs +0 -213
  191. package/src/mcp/evidence-snapshot.health.mjs +0 -102
  192. package/src/mcp/evidence-snapshot.inventory.mjs +0 -148
  193. package/src/mcp/evidence-snapshot.mjs +0 -50
  194. package/src/mcp/evidence-snapshot.package-graph.mjs +0 -34
  195. package/src/mcp/evidence-snapshot.structure.mjs +0 -84
  196. package/src/mcp/extension-api.mjs +0 -77
  197. package/src/mcp/git-output.mjs +0 -7
  198. package/src/mcp/graph/context-core.mjs +0 -111
  199. package/src/mcp/graph/context-seeds.mjs +0 -207
  200. package/src/mcp/graph/context-state.mjs +0 -85
  201. package/src/mcp/graph/reverse-reach.mjs +0 -42
  202. package/src/mcp/graph/tools-core.mjs +0 -143
  203. package/src/mcp/graph/tools-query.mjs +0 -276
  204. package/src/mcp/graph-context.mjs +0 -5
  205. package/src/mcp/graph-diff.mjs +0 -218
  206. package/src/mcp/health/audit-format.mjs +0 -199
  207. package/src/mcp/health/audit.mjs +0 -199
  208. package/src/mcp/health/dead-code.mjs +0 -110
  209. package/src/mcp/health/duplicates.mjs +0 -121
  210. package/src/mcp/health/endpoints.mjs +0 -71
  211. package/src/mcp/health/structure.mjs +0 -220
  212. package/src/mcp/runtime-version.mjs +0 -31
  213. package/src/mcp/server/auto-refresh.mjs +0 -66
  214. package/src/mcp/server/runtime-config.mjs +0 -43
  215. package/src/mcp/server/shutdown.mjs +0 -40
  216. package/src/mcp/staleness-notice.mjs +0 -20
  217. package/src/mcp/sync/evidence-architecture.mjs +0 -54
  218. package/src/mcp/sync/evidence-common.mjs +0 -88
  219. package/src/mcp/sync/evidence-duplicates.mjs +0 -97
  220. package/src/mcp/sync/evidence-health.mjs +0 -56
  221. package/src/mcp/sync/evidence-packages.mjs +0 -95
  222. package/src/mcp/sync/payload-common.mjs +0 -97
  223. package/src/mcp/sync/payload-v2.mjs +0 -54
  224. package/src/mcp/sync/payload-v3.mjs +0 -79
  225. package/src/mcp/sync-evidence.mjs +0 -23
  226. package/src/mcp/sync-payload.mjs +0 -70
  227. package/src/mcp/tool-result.mjs +0 -54
  228. package/src/mcp/tools-actions.mjs +0 -11
  229. package/src/mcp/tools-architecture.mjs +0 -177
  230. package/src/mcp/tools-company.mjs +0 -251
  231. package/src/mcp/tools-context.mjs +0 -222
  232. package/src/mcp/tools-endpoints.mjs +0 -207
  233. package/src/mcp/tools-graph-hubs.mjs +0 -78
  234. package/src/mcp/tools-graph.mjs +0 -17
  235. package/src/mcp/tools-health.mjs +0 -24
  236. package/src/mcp/tools-history.mjs +0 -28
  237. package/src/mcp/tools-impact-change.mjs +0 -220
  238. package/src/mcp/tools-impact-precision.mjs +0 -89
  239. package/src/mcp/tools-impact.mjs +0 -169
  240. package/src/mcp/tools-source.mjs +0 -238
  241. package/src/mcp/tools-verified-change.mjs +0 -200
  242. package/src/mcp-rg.mjs +0 -50
  243. package/src/mcp-runtime.mjs +0 -2
  244. package/src/mcp-server.mjs +0 -298
  245. package/src/mcp-source-tools.mjs +0 -163
  246. package/src/path-classification.js +0 -226
  247. package/src/path-ignore.js +0 -69
  248. package/src/precision/lsp-client/constants.js +0 -12
  249. package/src/precision/lsp-client/environment.js +0 -20
  250. package/src/precision/lsp-client/errors.js +0 -15
  251. package/src/precision/lsp-client/lifecycle.js +0 -114
  252. package/src/precision/lsp-client/message-parser.js +0 -81
  253. package/src/precision/lsp-client/protocol.js +0 -212
  254. package/src/precision/lsp-client/registry.js +0 -59
  255. package/src/precision/lsp-client/repo-uri.js +0 -60
  256. package/src/precision/lsp-client/stdio-client.js +0 -140
  257. package/src/precision/lsp-client.js +0 -10
  258. package/src/precision/lsp-overlay/budget-policy.js +0 -78
  259. package/src/precision/lsp-overlay/build.js +0 -261
  260. package/src/precision/lsp-overlay/contract.js +0 -61
  261. package/src/precision/lsp-overlay/reference-results.js +0 -108
  262. package/src/precision/lsp-overlay/semantic-inputs.js +0 -62
  263. package/src/precision/lsp-overlay/source-session.js +0 -142
  264. package/src/precision/lsp-overlay/store.js +0 -150
  265. package/src/precision/lsp-overlay/target-index.js +0 -152
  266. package/src/precision/lsp-overlay/target-query.js +0 -55
  267. package/src/precision/lsp-overlay.js +0 -11
  268. package/src/precision/symbol-query.js +0 -224
  269. package/src/precision/typescript-lsp-provider.js +0 -12
  270. package/src/precision/typescript-provider/client.js +0 -89
  271. package/src/precision/typescript-provider/discovery.js +0 -124
  272. package/src/precision/typescript-provider/isolated-runtime.js +0 -39
  273. package/src/precision/typescript-provider/project-host.js +0 -254
  274. package/src/precision/typescript-provider/project-safety.js +0 -166
  275. package/src/precision/typescript-provider/reference-usage.js +0 -53
  276. package/src/process.js +0 -95
  277. package/src/repo-path.js +0 -53
  278. package/src/scan/discover.inventory.js +0 -78
  279. package/src/scan/discover.js +0 -5
  280. package/src/scan/discover.list.js +0 -79
  281. package/src/scan/discover.stack.js +0 -227
  282. package/src/scan/search.core.js +0 -24
  283. package/src/scan/search.git.js +0 -102
  284. package/src/scan/search.js +0 -9
  285. package/src/scan/search.node.js +0 -83
  286. package/src/scan/search.preview.js +0 -49
  287. package/src/scan/search.rg.js +0 -145
  288. package/src/security/advisory-store.js +0 -172
  289. package/src/security/installed-jvm-rust.js +0 -46
  290. package/src/security/installed.js +0 -280
  291. package/src/security/malware-file-heuristics.js +0 -121
  292. package/src/security/malware-heuristics.exclusions.js +0 -134
  293. package/src/security/malware-heuristics.js +0 -15
  294. package/src/security/malware-heuristics.roots.js +0 -145
  295. package/src/security/malware-heuristics.scan.js +0 -89
  296. package/src/security/malware-heuristics.sweep.js +0 -122
  297. package/src/security/malware-scoring.js +0 -105
  298. package/src/security/match.js +0 -85
  299. package/src/security/registry-sig.classify.js +0 -137
  300. package/src/security/registry-sig.js +0 -18
  301. package/src/security/registry-sig.rules.js +0 -192
  302. package/src/security/rust-advisory-report.js +0 -60
  303. package/src/security/typosquat.js +0 -81
  304. package/src/util.js +0 -40
  305. 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
- }