pi-lens 4.4.3 → 4.4.4

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 (143) hide show
  1. package/CHANGELOG.md +281 -0
  2. package/README.md +13 -1
  3. package/dist/{chunk-SS36ZX4J.js → chunk-24AKHFGP.js} +6 -6
  4. package/dist/{chunk-JSQFTSC6.js → chunk-2FN63LPV.js} +11 -11
  5. package/dist/{chunk-5UCAH366.js → chunk-2KXTHF3M.js} +20 -20
  6. package/dist/{chunk-EU63SDCG.js → chunk-3DA2JDOL.js} +367 -281
  7. package/dist/{chunk-RWWYJ623.js → chunk-3KAZU3VR.js} +1 -1
  8. package/dist/chunk-3PTHXJXR.js +28 -0
  9. package/dist/chunk-3QED2P4V.js +31 -0
  10. package/dist/{chunk-N2H3ZPZY.js → chunk-3RKP3FMS.js} +1 -0
  11. package/dist/{chunk-XVF2VVXP.js → chunk-5HSRT26B.js} +2 -2
  12. package/dist/{chunk-MXGY6KPT.js → chunk-5IUSNGOC.js} +15 -15
  13. package/dist/{chunk-EJIUXPBX.js → chunk-5OFV52AM.js} +1 -1
  14. package/dist/{chunk-AW7R2GWG.js → chunk-5VSF4UUQ.js} +12 -7
  15. package/dist/{chunk-6STA6Z7I.js → chunk-5WRPDP2A.js} +18 -18
  16. package/dist/{chunk-YIUAVF7E.js → chunk-6DLWKFEX.js} +35 -34
  17. package/dist/{chunk-WOA4LN2R.js → chunk-7D6L4MTX.js} +2 -2
  18. package/dist/{chunk-GAEGEPOC.js → chunk-7HXE67BN.js} +2 -2
  19. package/dist/chunk-AGSS7277.js +84 -0
  20. package/dist/{chunk-R3PCY3YS.js → chunk-AMERCH6V.js} +13 -13
  21. package/dist/{chunk-WU567RSC.js → chunk-ATKJFDQN.js} +14 -10
  22. package/dist/{chunk-VSSBEUFT.js → chunk-AWSCPCN2.js} +16 -16
  23. package/dist/{chunk-VCHXCQFG.js → chunk-BWD5EPKO.js} +3 -3
  24. package/dist/{chunk-H6MF6EVN.js → chunk-CT4MN5IU.js} +1200 -230
  25. package/dist/{chunk-FCSOVF55.js → chunk-CUHSHSB6.js} +6 -6
  26. package/dist/{chunk-CHOW74WE.js → chunk-CYRKQRPM.js} +7 -10
  27. package/dist/{chunk-Q7DUK4LX.js → chunk-DEL2U6CM.js} +5 -5
  28. package/dist/{chunk-6C7JWPCP.js → chunk-DGUKIYSI.js} +26 -23
  29. package/dist/{chunk-PHDMFAKM.js → chunk-DUO2TU77.js} +2 -2
  30. package/dist/{chunk-MRXKYPCS.js → chunk-EBC36LNN.js} +2 -2
  31. package/dist/{chunk-CSHFH5IN.js → chunk-ED36RF6Y.js} +1 -1
  32. package/dist/{chunk-76MWC2K6.js → chunk-EUXZEFTV.js} +90 -97
  33. package/dist/{chunk-QOZXJ4NA.js → chunk-FM4RDMKT.js} +11 -8
  34. package/dist/{chunk-ACXEQZOU.js → chunk-GAQ7OQG6.js} +31 -28
  35. package/dist/{chunk-XAGLWMJ4.js → chunk-GL5LUMZE.js} +23 -23
  36. package/dist/{chunk-BKE7E7TC.js → chunk-GMX5OS7B.js} +9 -9
  37. package/dist/{chunk-ZHFZWTZV.js → chunk-GSLNCYGG.js} +11 -11
  38. package/dist/{chunk-3W7IEVVL.js → chunk-GSM5UU7A.js} +26 -34
  39. package/dist/{chunk-724UL2TJ.js → chunk-GXFV6MYU.js} +3 -3
  40. package/dist/chunk-HAD7QRQI.js +30 -0
  41. package/dist/{chunk-QOBUHWHX.js → chunk-HRNHZA3N.js} +6 -6
  42. package/dist/{chunk-VWCXW2E7.js → chunk-HWCCCQNX.js} +1 -1
  43. package/dist/{chunk-7DYBLXYA.js → chunk-HYGZEMX6.js} +3 -3
  44. package/dist/chunk-IJYGIDF4.js +186 -0
  45. package/dist/{chunk-4W4VLAPJ.js → chunk-IQWN5JCP.js} +11 -11
  46. package/dist/{chunk-SQ4TVJBR.js → chunk-IUE7SSKV.js} +7 -7
  47. package/dist/{chunk-IBGIOBEE.js → chunk-IWDLU6K3.js} +68 -5
  48. package/dist/{chunk-XCJN5IJU.js → chunk-IYNJ6YPZ.js} +4 -4
  49. package/dist/{chunk-JAU72XNY.js → chunk-J64T55EI.js} +3 -3
  50. package/dist/{chunk-2BBLFX75.js → chunk-J6MPGQTS.js} +5 -5
  51. package/dist/chunk-JORED7EX.js +44 -0
  52. package/dist/{chunk-6SDEP3AJ.js → chunk-JUKMK5Y4.js} +8 -8
  53. package/dist/{chunk-FMSO6RDZ.js → chunk-JW3YWBVB.js} +15 -15
  54. package/dist/{chunk-L3BLXZG7.js → chunk-KAPJINKN.js} +30 -69
  55. package/dist/{chunk-2B73ZPOK.js → chunk-KN35K52N.js} +3 -3
  56. package/dist/{chunk-C34ZXZ7O.js → chunk-KUA4OJ5Y.js} +24 -11
  57. package/dist/chunk-KYUB6QDC.js +72 -0
  58. package/dist/{chunk-67WMNWEE.js → chunk-LLJNQKQW.js} +12 -10
  59. package/dist/chunk-LZSEQW6A.js +57 -0
  60. package/dist/{chunk-ZANLILYD.js → chunk-M2IFVFWU.js} +2 -2
  61. package/dist/chunk-M2UD3LPU.js +88 -0
  62. package/dist/{chunk-XIJZVPD5.js → chunk-MBKQHGA7.js} +15 -15
  63. package/dist/{chunk-P7266HLM.js → chunk-MPNJLASA.js} +2 -2
  64. package/dist/{chunk-6GFRNVYR.js → chunk-MZGQNL3Y.js} +10 -10
  65. package/dist/{chunk-ZWFS3OEF.js → chunk-N5FLEO7W.js} +33 -32
  66. package/dist/{chunk-3JNKMP3B.js → chunk-O4NWQT6I.js} +4 -4
  67. package/dist/{chunk-T6CPIZNT.js → chunk-OHKJ7PQF.js} +33 -32
  68. package/dist/{chunk-VRZP76YM.js → chunk-P63GXC37.js} +4 -1
  69. package/dist/{chunk-XTMJ7XDT.js → chunk-POMLXPMY.js} +1 -1
  70. package/dist/{chunk-QQNQ7SJW.js → chunk-QBFBRV2B.js} +10 -4
  71. package/dist/{chunk-CMMHNUZ2.js → chunk-R3T7CLQ2.js} +60 -38
  72. package/dist/{chunk-23WOO7TX.js → chunk-R7YOCSNV.js} +10 -10
  73. package/dist/{chunk-2CD5RS6M.js → chunk-RKBXZN3A.js} +23 -2
  74. package/dist/{chunk-OCAGUNBC.js → chunk-RKKOYPRA.js} +12 -12
  75. package/dist/{chunk-52FUZI6K.js → chunk-RMLC67LF.js} +1 -1
  76. package/dist/{chunk-XMSXXAHW.js → chunk-ROYSX2S5.js} +2 -2
  77. package/dist/{chunk-M6HSLBWY.js → chunk-RVK72CZX.js} +6507 -7514
  78. package/dist/{chunk-FLKWCIZH.js → chunk-S2HEXIWI.js} +5 -5
  79. package/dist/{chunk-UZ3TUTRV.js → chunk-S67KYG37.js} +1 -1
  80. package/dist/{chunk-VCO24T4L.js → chunk-SD4I7ZO4.js} +14 -14
  81. package/dist/{chunk-Z527N6YQ.js → chunk-SKAMFC7A.js} +12 -12
  82. package/dist/{chunk-MQLUAEOC.js → chunk-SRYEJNES.js} +2 -2
  83. package/dist/{chunk-BRGDSBJE.js → chunk-SYE6T2EB.js} +12 -12
  84. package/dist/{chunk-GXW6K4YJ.js → chunk-T57LYLZT.js} +1 -1
  85. package/dist/{chunk-ZXYDQ3TZ.js → chunk-T7FEBKXN.js} +48 -15
  86. package/dist/{chunk-3EFKIGZN.js → chunk-TIQ2PKDW.js} +3 -3
  87. package/dist/{chunk-QILIYSJF.js → chunk-TLX72JTG.js} +1 -1
  88. package/dist/{chunk-TYM6OUIT.js → chunk-TOY6QKOH.js} +12 -12
  89. package/dist/{chunk-IZSHAIJ7.js → chunk-TRETHTQO.js} +12 -12
  90. package/dist/{chunk-3VPVG5SU.js → chunk-U44DJXQP.js} +10 -6
  91. package/dist/{chunk-PNQDPDRC.js → chunk-UA7ZXYDR.js} +1 -1
  92. package/dist/{chunk-OXXKQCSV.js → chunk-UBOQLVT2.js} +4 -4
  93. package/dist/{chunk-QGQ5CCCG.js → chunk-UMS2MFPZ.js} +2 -2
  94. package/dist/{chunk-QHJ6DQ2R.js → chunk-US7ECLD7.js} +55 -13
  95. package/dist/{chunk-AB3YYG6N.js → chunk-UZ2XCLYS.js} +8 -8
  96. package/dist/chunk-V35JG4JB.js +2667 -0
  97. package/dist/{chunk-N3X6Q4YT.js → chunk-VOOAPROD.js} +3 -3
  98. package/dist/{chunk-SAEFPSB6.js → chunk-W66O5E25.js} +21 -11
  99. package/dist/{chunk-KBG47GHP.js → chunk-WMYE35PX.js} +15 -11
  100. package/dist/{chunk-5NGR67U5.js → chunk-XBJVAEEO.js} +32 -5
  101. package/dist/{chunk-YYOIQFRS.js → chunk-XHJBS436.js} +122 -44
  102. package/dist/{chunk-GBPP5P7X.js → chunk-XTBDCR27.js} +4 -4
  103. package/dist/{chunk-4UNXBNE2.js → chunk-XTFDJI3T.js} +11 -11
  104. package/dist/{chunk-UXGETRP4.js → chunk-XYURS6EN.js} +13 -12
  105. package/dist/{chunk-RPLINGVB.js → chunk-Y6GZHWSJ.js} +6 -6
  106. package/dist/{chunk-2VUQMUMT.js → chunk-YCWYO6TQ.js} +41 -38
  107. package/dist/{chunk-77M37OKR.js → chunk-YH5MVY2U.js} +7 -7
  108. package/dist/{chunk-AOHZU2BM.js → chunk-YNE3RP52.js} +1 -1
  109. package/dist/{chunk-YSZNHOBY.js → chunk-ZHH37KZL.js} +12 -12
  110. package/dist/{chunk-ZGDGW2V3.js → chunk-ZIVJBOO3.js} +2 -2
  111. package/dist/{chunk-RXCOMYPY.js → chunk-ZVUL7QYY.js} +29 -28
  112. package/dist/index.js +11052 -5852
  113. package/dist/mcp/analyze-cli.js +10 -9
  114. package/dist/mcp/cli.js +41 -38
  115. package/dist/mcp/server.js +1120 -474
  116. package/dist/mcp/worker.js +61 -58
  117. package/dist/probes/installer.js +19 -19
  118. package/dist/workers/project-snapshot-persist-worker.js +28 -9
  119. package/dist/workers/review-graph-persist-worker.js +1 -1
  120. package/docs/configuration.md +11 -0
  121. package/docs/engineering-principles.md +32 -0
  122. package/docs/lsp-capability-matrix.md +1 -1
  123. package/docs/lsp-idle-eviction.md +15 -26
  124. package/docs/pi-lens-fixer.md +117 -13
  125. package/docs/pi-lens-merge-policy.md +97 -7
  126. package/docs/pi-lens-monitor.md +8 -2
  127. package/docs/pi-lens-retro.md +4 -1
  128. package/docs/pi-lens-reviewer.md +112 -4
  129. package/docs/pi-lens-subagent.md +22 -14
  130. package/docs/release-qa-baseline.md +3 -1
  131. package/docs/servercapabilities.md +9 -19
  132. package/package.json +7 -5
  133. package/scripts/download-grammars.js +40 -7
  134. package/scripts/lib/latency-log-phases.mjs +95 -0
  135. package/scripts/rpc-load-check.mjs +570 -81
  136. package/dist/chunk-3YN6V5TV.js +0 -85
  137. package/dist/chunk-466Z26MG.js +0 -30
  138. package/dist/chunk-EC47DWFA.js +0 -56
  139. package/dist/chunk-KDERNTCQ.js +0 -28
  140. package/dist/chunk-KSX3TXSZ.js +0 -69
  141. package/dist/chunk-LOP6GRPH.js +0 -31
  142. package/dist/chunk-UOPVJQ4N.js +0 -103
  143. /package/dist/{chunk-FC4RP4I2.js → chunk-6HWVI6MI.js} +0 -0
@@ -0,0 +1,2667 @@
1
+ import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
+ import {
3
+ buildModuleGraph
4
+ } from "./chunk-XTBDCR27.js";
5
+ import {
6
+ getSharedTreeSitterClient,
7
+ loadWebTreeSitter,
8
+ logTreeSitterDiagnostic,
9
+ resolveTreeSitterLanguage,
10
+ wasmQueryInput
11
+ } from "./chunk-US7ECLD7.js";
12
+ import {
13
+ EXTENSION_TO_GRAMMAR,
14
+ KIND_TO_GRAMMAR
15
+ } from "./chunk-GXFV6MYU.js";
16
+ import {
17
+ HOOK_WALL_BUDGET_MS,
18
+ bounded
19
+ } from "./chunk-RMLC67LF.js";
20
+ import {
21
+ freshnessFromMtime,
22
+ hashContentBytes,
23
+ toRunnerDisplayPath
24
+ } from "./chunk-AGSS7277.js";
25
+ import {
26
+ loadPiLensGlobalConfig
27
+ } from "./chunk-KUA4OJ5Y.js";
28
+ import {
29
+ isFileKind
30
+ } from "./chunk-DUO2TU77.js";
31
+ import {
32
+ isDeclarationFile
33
+ } from "./chunk-J6MPGQTS.js";
34
+ import {
35
+ findGoverningTsconfigDir,
36
+ getDirectoryMarkers,
37
+ registerWorkspaceTopologyReset
38
+ } from "./chunk-5HSRT26B.js";
39
+ import {
40
+ incrementDegradationCount,
41
+ recordDegradationOnce
42
+ } from "./chunk-J64T55EI.js";
43
+ import {
44
+ toPositiveFinite
45
+ } from "./chunk-YNE3RP52.js";
46
+ import {
47
+ BoundedFifoMap,
48
+ BoundedLruCache
49
+ } from "./chunk-ABBOA7UD.js";
50
+ import {
51
+ isUnderDir,
52
+ normalizeEphemeralMapKey,
53
+ normalizeMapKey
54
+ } from "./chunk-3RKP3FMS.js";
55
+
56
+ // dist/clients/blocker-freshness.js
57
+ import * as fs5 from "node:fs";
58
+ import * as nodePath from "node:path";
59
+
60
+ // dist/clients/lsp/implicit-scope.js
61
+ import * as fs from "node:fs";
62
+ import * as path from "node:path";
63
+
64
+ // dist/clients/deps/ast-grep-napi.js
65
+ import { createRequire } from "node:module";
66
+ import { pathToFileURL } from "node:url";
67
+ var _require = createRequire(import.meta.url);
68
+ function loadAstGrepNapi() {
69
+ try {
70
+ const entry = _require.resolve("@ast-grep/napi");
71
+ return import(pathToFileURL(entry).href);
72
+ } catch {
73
+ return import("@ast-grep/napi");
74
+ }
75
+ }
76
+ function loadAstGrepNapiSync() {
77
+ try {
78
+ return _require("@ast-grep/napi");
79
+ } catch {
80
+ return void 0;
81
+ }
82
+ }
83
+
84
+ // dist/clients/runtime-config.js
85
+ var _runnerTimeoutFloorCache;
86
+ function getRunnerTimeoutFloorMs() {
87
+ if (_runnerTimeoutFloorCache !== void 0)
88
+ return _runnerTimeoutFloorCache;
89
+ const config = loadPiLensGlobalConfig();
90
+ const configFloor = toPositiveFinite(config?.dispatch?.runnerTimeoutFloorMs);
91
+ const envFloor = toPositiveFinite(process.env.PI_LENS_RUNNER_TIMEOUT_FLOOR_MS);
92
+ _runnerTimeoutFloorCache = Math.max(configFloor, envFloor, 0);
93
+ return _runnerTimeoutFloorCache;
94
+ }
95
+ var RUNTIME_CONFIG = {
96
+ pipeline: {
97
+ lspMaxFileBytes: 2 * 1024 * 1024,
98
+ lspMaxFileLines: 5e3,
99
+ cascadeMaxFiles: 5,
100
+ cascadeMaxDiagnosticsPerFile: 20,
101
+ // Hard cap on how long the pipeline will wait for an LSP client to spawn.
102
+ // Keeps tool_result from blocking the TUI during cold LSP start (e.g.
103
+ // pyright workspace indexing). The LSP server continues spawning in the
104
+ // background; subsequent edits get full diagnostics once it is ready.
105
+ lspSpawnBudgetMs: 5e3
106
+ },
107
+ dispatch: {
108
+ runnerTimeoutMs: 3e4
109
+ },
110
+ crashNotice: {
111
+ alwaysShowFirstN: 2,
112
+ showEveryNth: 5
113
+ },
114
+ reviewGraph: {
115
+ // Deprecated (#776): no longer read by `review-graph/builder.ts`, which
116
+ // now derives its fallback from `project-scale.ts`'s `maxProjectFiles`
117
+ // knob (reproducing this same 1,000 default at the default base). Left
118
+ // in place only in case an external caller still reads the constant.
119
+ maxFiles: 1e3,
120
+ maxFileBytes: 1 * 1024 * 1024
121
+ },
122
+ turnEnd: {
123
+ maxLines: 20,
124
+ maxChars: 1e3
125
+ }
126
+ };
127
+
128
+ // dist/clients/lsp/content-limits.js
129
+ var LSP_MAX_FILE_BYTES = RUNTIME_CONFIG.pipeline.lspMaxFileBytes;
130
+ var LSP_MAX_FILE_LINES = RUNTIME_CONFIG.pipeline.lspMaxFileLines;
131
+ function exceedsLspSyncLimits(content) {
132
+ const sizeBytes = Buffer.byteLength(content, "utf-8");
133
+ if (sizeBytes > LSP_MAX_FILE_BYTES) {
134
+ return {
135
+ tooLarge: true,
136
+ reason: `${sizeBytes} bytes > ${LSP_MAX_FILE_BYTES} limit`
137
+ };
138
+ }
139
+ const lineCount = content.split("\n").length;
140
+ if (lineCount > LSP_MAX_FILE_LINES) {
141
+ return {
142
+ tooLarge: true,
143
+ reason: `${lineCount} lines > ${LSP_MAX_FILE_LINES} limit`
144
+ };
145
+ }
146
+ return { tooLarge: false, reason: "" };
147
+ }
148
+
149
+ // dist/clients/lsp/implicit-scope.js
150
+ function isGoSourceFile(filePath) {
151
+ return path.extname(filePath).toLowerCase() === ".go";
152
+ }
153
+ function seesJsTsGlobalScope(filePath) {
154
+ return isFileKind(filePath, "jsts");
155
+ }
156
+ function inOneGoPackage(a, b) {
157
+ return isGoSourceFile(a) && isGoSourceFile(b) && path.posix.dirname(a) === path.posix.dirname(b);
158
+ }
159
+ function recordUnknownRole(filePath, cause, err) {
160
+ incrementDegradationCount({
161
+ kind: "lsp-implicit-scope-unknown",
162
+ subject: cause,
163
+ reason: `${cause}: ${filePath}${err === void 0 ? "" : ` (${err instanceof Error ? err.message : String(err)})`} \u2014 unresolved, so the diagnostics cache refuses its entry and the blocker sweep and the cascade see no role for it`,
164
+ metadata: {
165
+ file: filePath,
166
+ cause,
167
+ ...err === void 0 ? {} : { err: err instanceof Error ? err.message : String(err) }
168
+ }
169
+ });
170
+ }
171
+ function goPackageFiles(dir) {
172
+ let entries;
173
+ try {
174
+ entries = fs.readdirSync(dir, { withFileTypes: true });
175
+ } catch (err) {
176
+ recordUnknownRole(dir, "unreadable-go-dir", err);
177
+ return void 0;
178
+ }
179
+ return entries.flatMap((entry) => entry.isFile() && isGoSourceFile(entry.name) ? [path.join(dir, entry.name)] : []);
180
+ }
181
+ function implicitScopeOf(filePath, feeds = feedsJsTsGlobalScope) {
182
+ if (isGoSourceFile(filePath)) {
183
+ return (other) => other !== filePath && inOneGoPackage(filePath, other);
184
+ }
185
+ if (seesJsTsGlobalScope(filePath)) {
186
+ return (other) => other !== filePath && feeds(other);
187
+ }
188
+ return void 0;
189
+ }
190
+ function goPackageChangedAt(dir) {
191
+ try {
192
+ let changedAt = fs.statSync(dir).mtimeMs;
193
+ for (const peer of goPackageFiles(dir) ?? []) {
194
+ changedAt = Math.max(changedAt, fs.statSync(peer).mtimeMs);
195
+ }
196
+ return changedAt;
197
+ } catch (err) {
198
+ recordUnknownRole(dir, "unreadable-mtime", err);
199
+ return void 0;
200
+ }
201
+ }
202
+ var GO_HEADER_BYTES = 8 * 1024;
203
+ var KNOWN_GOOS = new Set("aix android darwin dragonfly freebsd hurd illumos ios js linux netbsd openbsd plan9 solaris wasip1 windows zos".split(" "));
204
+ var KNOWN_GOARCH = new Set("386 amd64 arm arm64 loong64 mips mips64 mips64le mipsle ppc64 ppc64le riscv64 s390x wasm".split(" "));
205
+ var GOOS_BY_PLATFORM = {
206
+ win32: "windows",
207
+ sunos: "solaris"
208
+ };
209
+ var GOARCH_BY_ARCH = { x64: "amd64", ia32: "386" };
210
+ var UNIX_GOOS = new Set("aix android darwin dragonfly freebsd hurd illumos ios linux netbsd openbsd solaris".split(" "));
211
+ function hostSatisfiesGoTag(tag) {
212
+ const goos = process.env.GOOS || GOOS_BY_PLATFORM[process.platform] || process.platform;
213
+ const goarch = process.env.GOARCH || GOARCH_BY_ARCH[process.arch] || process.arch;
214
+ return tag === goos || tag === goarch || tag === "unix" && UNIX_GOOS.has(goos);
215
+ }
216
+ function goBuildExcludesHost(filePath, header) {
217
+ const parts = path.basename(filePath, ".go").replace(/_test$/, "").split("_");
218
+ const last = parts.at(-1) ?? "";
219
+ const prev = parts.length > 2 ? parts.at(-2) ?? "" : "";
220
+ if (parts.length > 1 && KNOWN_GOARCH.has(last)) {
221
+ if (!hostSatisfiesGoTag(last))
222
+ return true;
223
+ if (KNOWN_GOOS.has(prev) && !hostSatisfiesGoTag(prev))
224
+ return true;
225
+ } else if (parts.length > 1 && KNOWN_GOOS.has(last) && !hostSatisfiesGoTag(last)) {
226
+ return true;
227
+ }
228
+ const line = /^\/\/go:build[ \t](.*)$/m.exec(header);
229
+ if (!line)
230
+ return /^\/\/[ \t]*\+build[ \t]/m.test(header);
231
+ const term = /^\s*(!?)([\w.]+)\s*$/.exec(line[1]);
232
+ const tag = term?.[2] ?? "";
233
+ if (!(KNOWN_GOOS.has(tag) || KNOWN_GOARCH.has(tag) || tag === "unix"))
234
+ return true;
235
+ return hostSatisfiesGoTag(tag) === (term?.[1] === "!");
236
+ }
237
+ function goHeaderOf(filePath) {
238
+ let fd;
239
+ try {
240
+ fd = fs.openSync(filePath, "r");
241
+ const buffer = Buffer.alloc(GO_HEADER_BYTES);
242
+ const read = fs.readSync(fd, buffer, 0, GO_HEADER_BYTES, 0);
243
+ return buffer.toString("utf8", 0, read);
244
+ } catch (err) {
245
+ recordUnknownRole(filePath, "unreadable-go-peer", err);
246
+ return void 0;
247
+ } finally {
248
+ if (fd !== void 0)
249
+ fs.closeSync(fd);
250
+ }
251
+ }
252
+ function goPackageClauseOf(header) {
253
+ const code = header.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, "");
254
+ return /^\s*package\s+([\p{L}_][\p{L}\p{N}_]*)/mu.exec(code)?.[1];
255
+ }
256
+ function goPackagePeersOf(filePath) {
257
+ const inScope = isGoSourceFile(filePath) ? implicitScopeOf(filePath) : void 0;
258
+ const ownHeader = inScope && goHeaderOf(filePath);
259
+ const clause = ownHeader ? goPackageClauseOf(ownHeader) : void 0;
260
+ if (!inScope || !clause)
261
+ return void 0;
262
+ const testFile = isGoTestFile(filePath);
263
+ return (peer) => {
264
+ if (!inScope(peer) || isGoTestFile(peer) && !testFile)
265
+ return false;
266
+ const header = goHeaderOf(peer);
267
+ return header !== void 0 && goPackageClauseOf(header) === clause && !goBuildExcludesHost(peer, header);
268
+ };
269
+ }
270
+ function isGoTestFile(filePath) {
271
+ return filePath.toLowerCase().endsWith("_test.go");
272
+ }
273
+ function probeFor(filePath) {
274
+ switch (path.extname(filePath).toLowerCase()) {
275
+ case ".ts":
276
+ case ".tsx":
277
+ return "ts-script-or-ambient";
278
+ // ES and CommonJS module extensions: never a script, but a TS one can
279
+ // still hold `declare global`.
280
+ case ".mts":
281
+ case ".cts":
282
+ return "ambient";
283
+ case ".js":
284
+ case ".jsx":
285
+ return "js-script";
286
+ default:
287
+ return void 0;
288
+ }
289
+ }
290
+ function globalScopeRoleByName(filePath) {
291
+ if (isDeclarationFile(filePath))
292
+ return "feeds";
293
+ return probeFor(filePath) ? void 0 : "none";
294
+ }
295
+ var MODULE_LINE = /^[ \t]*(?:import|export)\b/m;
296
+ var AMBIENT_DECLARATION = /\bdeclare\s+(?:global|module)\b/;
297
+ function globalScopeRoleByText(content) {
298
+ return MODULE_LINE.test(content) && !AMBIENT_DECLARATION.test(content) ? "none" : void 0;
299
+ }
300
+ function classifyJsTsGlobalScope(filePath) {
301
+ const byName = globalScopeRoleByName(filePath);
302
+ if (byName)
303
+ return byName;
304
+ const probe = probeFor(filePath);
305
+ let content;
306
+ try {
307
+ content = fs.readFileSync(filePath, "utf8");
308
+ } catch (err) {
309
+ recordUnknownRole(filePath, "unreadable-file", err);
310
+ return "unknown";
311
+ }
312
+ if (exceedsLspSyncLimits(content).tooLarge)
313
+ return "unknown";
314
+ const napi = loadAstGrepNapiSync();
315
+ if (!napi) {
316
+ recordDegradationOnce({
317
+ kind: "ast-grep-napi-unavailable",
318
+ subject: "implicit-scope",
319
+ reason: "native addon failed to load; every created JS/TS file counts as feeding the global scope in the diagnostics cache, and its role is unknown to the blocker sweep and the cascade"
320
+ });
321
+ return "unknown";
322
+ }
323
+ const lang = probe === "js-script" ? napi.Lang.JavaScript : path.extname(filePath).toLowerCase() === ".tsx" ? napi.Lang.Tsx : napi.Lang.TypeScript;
324
+ let root;
325
+ try {
326
+ root = napi.parse(lang, content).root();
327
+ } catch (err) {
328
+ recordUnknownRole(filePath, "parse-failed", err);
329
+ return "unknown";
330
+ }
331
+ let isModule = false;
332
+ for (const child of root.children()) {
333
+ const kind = child.kind();
334
+ if (kind === "import_statement" || kind === "export_statement") {
335
+ isModule = true;
336
+ } else if (kind === "ambient_declaration" && child.children().some((part) => part.kind() === "global" || part.kind() === "module")) {
337
+ return "feeds";
338
+ }
339
+ }
340
+ if (probe === "ambient" || isModule)
341
+ return "none";
342
+ if (probe === "js-script") {
343
+ return ["require($$$ARGS)", "module.exports", "exports.$NAME"].some((pattern) => root.find(pattern) !== null) ? "none" : "feeds";
344
+ }
345
+ return "feeds";
346
+ }
347
+ function feedsJsTsGlobalScope(filePath) {
348
+ return classifyJsTsGlobalScope(filePath) !== "none";
349
+ }
350
+
351
+ // dist/clients/path-birth.js
352
+ import * as fs2 from "node:fs";
353
+ var replacedPathBirths = new BoundedFifoMap(1024);
354
+ var inodeKey = (stat) => `${stat.dev}:${stat.ino}:${stat.birthtimeMs}`;
355
+ var appearedPaths = new BoundedFifoMap(1024);
356
+ function notePathAppeared(filePath, atMs = Date.now()) {
357
+ appearedPaths.set(normalizeMapKey(filePath), atMs);
358
+ }
359
+ var birthIsCtimeAlias;
360
+ function birthTimeIsCtimeAlias() {
361
+ if (birthIsCtimeAlias !== void 0)
362
+ return birthIsCtimeAlias;
363
+ birthIsCtimeAlias = false;
364
+ if (process.platform === "linux") {
365
+ try {
366
+ const proc = fs2.statSync("/proc");
367
+ birthIsCtimeAlias = proc.birthtimeMs !== 0 && proc.birthtimeMs === proc.ctimeMs;
368
+ } catch {
369
+ }
370
+ }
371
+ return birthIsCtimeAlias;
372
+ }
373
+ function pathBirthtimeMs(stat, filePath) {
374
+ const appeared = filePath === void 0 ? void 0 : appearedPaths.get(normalizeMapKey(filePath));
375
+ const inode = inodeBirthtimeMs(stat);
376
+ if (appeared === void 0)
377
+ return inode;
378
+ return inode === void 0 ? appeared : Math.max(inode, appeared);
379
+ }
380
+ function inodeBirthtimeMs(stat) {
381
+ if (!(stat.birthtimeMs > 0)) {
382
+ recordDegradationOnce({
383
+ kind: "lsp-file-birth-unknown",
384
+ subject: "no-birth-time",
385
+ reason: "the filesystem reports no birth time; a file created while a language server runs is announced as Changed (#4320)"
386
+ });
387
+ return void 0;
388
+ }
389
+ if (birthTimeIsCtimeAlias()) {
390
+ recordDegradationOnce({
391
+ kind: "lsp-file-birth-unknown",
392
+ subject: "ctime-alias",
393
+ reason: "Node reports ctime as the birth time (no statx); a file created while a language server runs is announced as Changed (#4320)"
394
+ });
395
+ return void 0;
396
+ }
397
+ return replacedPathBirths.get(inodeKey(stat)) ?? stat.birthtimeMs;
398
+ }
399
+
400
+ // dist/clients/review-graph/import-resolvers.js
401
+ import * as fs4 from "node:fs";
402
+ import * as path3 from "node:path";
403
+
404
+ // dist/clients/review-graph/tsconfig-paths.js
405
+ import * as fs3 from "node:fs";
406
+ import * as os from "node:os";
407
+ import * as path2 from "node:path";
408
+ var cache = new BoundedLruCache(64);
409
+ var referencesCache = new BoundedLruCache(64);
410
+ registerWorkspaceTopologyReset(() => {
411
+ cache.clear();
412
+ referencesCache.clear();
413
+ });
414
+ function parseJsonc(content) {
415
+ let output = "";
416
+ let inString = false;
417
+ let escaped = false;
418
+ for (let i = 0; i < content.length; i++) {
419
+ const char = content[i];
420
+ const next = content[i + 1];
421
+ if (inString) {
422
+ output += char;
423
+ if (escaped)
424
+ escaped = false;
425
+ else if (char === "\\")
426
+ escaped = true;
427
+ else if (char === '"')
428
+ inString = false;
429
+ continue;
430
+ }
431
+ if (char === '"') {
432
+ inString = true;
433
+ output += char;
434
+ } else if (char === "/" && next === "/") {
435
+ while (i < content.length && content[i] !== "\n")
436
+ i++;
437
+ output += "\n";
438
+ } else if (char === "/" && next === "*") {
439
+ i += 2;
440
+ while (i < content.length && !(content[i] === "*" && content[i + 1] === "/"))
441
+ i++;
442
+ i++;
443
+ } else {
444
+ output += char;
445
+ }
446
+ }
447
+ return JSON.parse(output.replace(/,\s*([}\]])/g, "$1"));
448
+ }
449
+ function configSignature(configPath) {
450
+ try {
451
+ const stat = fs3.statSync(configPath);
452
+ return `${stat.mtimeMs}:${stat.size}`;
453
+ } catch {
454
+ return "missing";
455
+ }
456
+ }
457
+ function configDependencyPaths(configPath) {
458
+ const paths = /* @__PURE__ */ new Set();
459
+ const visit = (currentPath) => {
460
+ const normalized = path2.resolve(currentPath);
461
+ if (paths.has(normalized))
462
+ return;
463
+ paths.add(normalized);
464
+ let json;
465
+ try {
466
+ json = parseJsonc(fs3.readFileSync(normalized, "utf8"));
467
+ } catch {
468
+ return;
469
+ }
470
+ if (typeof json.extends === "string") {
471
+ const parent = resolveExtends(normalized, json.extends);
472
+ if (parent)
473
+ visit(parent);
474
+ }
475
+ for (const reference of json.references ?? []) {
476
+ if (typeof reference?.path !== "string")
477
+ continue;
478
+ const referenced = resolveReferenceConfig(normalized, reference.path);
479
+ if (referenced)
480
+ visit(referenced);
481
+ }
482
+ };
483
+ visit(configPath);
484
+ return [...paths].sort((a, b) => a.localeCompare(b));
485
+ }
486
+ function dependencySignature(configPath) {
487
+ return configDependencyPaths(configPath).map((dependency) => `${dependency}:${configSignature(dependency)}`).join("|");
488
+ }
489
+ function resolveExtends(configPath, value) {
490
+ if (!value.startsWith("."))
491
+ return void 0;
492
+ const resolved = path2.resolve(path2.dirname(configPath), value);
493
+ return resolved.toLowerCase().endsWith(".json") ? resolved : `${resolved}.json`;
494
+ }
495
+ function readConfig(configPath, seen) {
496
+ const normalized = path2.resolve(configPath);
497
+ if (seen.has(normalized))
498
+ return void 0;
499
+ seen.add(normalized);
500
+ let json;
501
+ try {
502
+ json = parseJsonc(fs3.readFileSync(normalized, "utf8"));
503
+ } catch {
504
+ return void 0;
505
+ }
506
+ let inherited;
507
+ if (typeof json.extends === "string") {
508
+ const parentPath = resolveExtends(normalized, json.extends);
509
+ if (parentPath)
510
+ inherited = readConfig(parentPath, seen);
511
+ }
512
+ const options = json.compilerOptions;
513
+ const baseUrl = typeof options?.baseUrl === "string" ? path2.resolve(path2.dirname(normalized), options.baseUrl) : inherited?.baseUrl ?? path2.dirname(normalized);
514
+ const paths = options?.paths && typeof options.paths === "object" ? Object.fromEntries(Object.entries(options.paths).map(([pattern, targets]) => [
515
+ pattern,
516
+ Array.isArray(targets) ? targets.map((target) => path2.resolve(baseUrl, target)) : targets
517
+ ])) : inherited?.paths;
518
+ const rootDir = typeof options?.rootDir === "string" ? path2.resolve(path2.dirname(normalized), options.rootDir) : inherited?.rootDir;
519
+ const include = Array.isArray(json.include) ? json.include.filter((value) => typeof value === "string") : inherited?.include;
520
+ const references = Array.isArray(json.references) ? json.references.map((reference) => reference?.path).filter((value) => typeof value === "string" && value.startsWith(".")) : [];
521
+ return { baseUrl, paths, rootDir, include, references };
522
+ }
523
+ function resolveReferenceConfig(configPath, value) {
524
+ const target = path2.resolve(path2.dirname(configPath), value);
525
+ try {
526
+ if (fs3.statSync(target).isDirectory()) {
527
+ return getDirectoryMarkers(target).tsconfigPath;
528
+ }
529
+ if (fs3.statSync(target).isFile())
530
+ return target;
531
+ } catch {
532
+ return void 0;
533
+ }
534
+ return void 0;
535
+ }
536
+ function includeRoot(configDir, pattern) {
537
+ const wildcard = pattern.search(/[*?]/);
538
+ const prefix = wildcard === -1 ? pattern : pattern.slice(0, wildcard);
539
+ const trimmed = prefix.replace(/[\\/]+$/, "");
540
+ if (!trimmed)
541
+ return void 0;
542
+ const resolved = path2.resolve(configDir, trimmed);
543
+ return path2.extname(resolved) ? path2.dirname(resolved) : resolved;
544
+ }
545
+ function firstSourceEntry(configPath, parsed) {
546
+ const configDir = path2.dirname(configPath);
547
+ const roots = [
548
+ ...parsed.rootDir ? [parsed.rootDir] : [],
549
+ ...(parsed.include ?? []).map((pattern) => includeRoot(configDir, pattern)).filter((value) => value !== void 0)
550
+ ];
551
+ const candidates = [
552
+ ...roots.flatMap((root) => [
553
+ path2.join(root, "index.ts"),
554
+ path2.join(root, "index.tsx")
555
+ ]),
556
+ path2.join(configDir, "src", "index.ts"),
557
+ path2.join(configDir, "src", "index.tsx"),
558
+ path2.join(configDir, "index.ts"),
559
+ path2.join(configDir, "index.tsx")
560
+ ];
561
+ for (const candidate of candidates) {
562
+ try {
563
+ if (fs3.statSync(candidate).isFile())
564
+ return candidate;
565
+ } catch {
566
+ }
567
+ }
568
+ return void 0;
569
+ }
570
+ function collectReferencedProjects(configPath, result, visited) {
571
+ const normalized = path2.resolve(configPath);
572
+ if (visited.has(normalized))
573
+ return;
574
+ visited.add(normalized);
575
+ const parsed = readConfig(normalized, /* @__PURE__ */ new Set());
576
+ if (!parsed)
577
+ return;
578
+ for (const reference of parsed.references) {
579
+ const referencedConfig = resolveReferenceConfig(normalized, reference);
580
+ if (!referencedConfig)
581
+ continue;
582
+ const referenced = readConfig(referencedConfig, /* @__PURE__ */ new Set());
583
+ if (referenced) {
584
+ const packageJsonPath = getDirectoryMarkers(path2.dirname(referencedConfig)).packageJsonPath;
585
+ const entry = firstSourceEntry(referencedConfig, referenced);
586
+ if (packageJsonPath && entry) {
587
+ try {
588
+ const pkg = JSON.parse(fs3.readFileSync(packageJsonPath, "utf8"));
589
+ if (typeof pkg.name === "string" && !result.has(pkg.name)) {
590
+ result.set(pkg.name, entry);
591
+ }
592
+ } catch {
593
+ }
594
+ }
595
+ }
596
+ collectReferencedProjects(referencedConfig, result, visited);
597
+ }
598
+ }
599
+ function parseTsconfigPaths(cwd, homeDir = os.homedir()) {
600
+ const normalizedCwd = path2.resolve(cwd);
601
+ const configDir = findGoverningTsconfigDir(normalizedCwd, homeDir);
602
+ const configPath = configDir ? path2.join(configDir, "tsconfig.json") : "";
603
+ const signature = configPath ? dependencySignature(configPath) : "missing";
604
+ const key = `${normalizedCwd}|${configPath}|${signature}`;
605
+ const cached = cache.get(key);
606
+ if (cached)
607
+ return cached;
608
+ if (!configDir) {
609
+ cache.set(key, []);
610
+ return [];
611
+ }
612
+ const parsed = readConfig(path2.join(configDir, "tsconfig.json"), /* @__PURE__ */ new Set());
613
+ const matchers = Object.entries(parsed?.paths ?? {}).filter((entry) => Array.isArray(entry[1]) && entry[1].every((target) => typeof target === "string")).map(([pattern, targets]) => {
614
+ const star = pattern.indexOf("*");
615
+ return {
616
+ pattern,
617
+ prefix: star === -1 ? pattern : pattern.slice(0, star),
618
+ suffix: star === -1 ? "" : pattern.slice(star + 1),
619
+ targets
620
+ };
621
+ }).sort((a, b) => b.prefix.length - a.prefix.length);
622
+ cache.set(key, matchers);
623
+ return matchers;
624
+ }
625
+ function aliasedImportTargets(specifier, importerDir) {
626
+ for (const matcher of parseTsconfigPaths(importerDir)) {
627
+ if (!specifier.startsWith(matcher.prefix) || !specifier.endsWith(matcher.suffix))
628
+ continue;
629
+ const wildcard = specifier.slice(matcher.prefix.length, specifier.length - matcher.suffix.length);
630
+ if (!matcher.pattern.includes("*") && wildcard)
631
+ continue;
632
+ return matcher.targets.map((target) => target.replaceAll("*", wildcard));
633
+ }
634
+ return [];
635
+ }
636
+ function referencedProjectImportTarget(specifier, importerDir) {
637
+ const normalizedImporterDir = path2.resolve(importerDir);
638
+ const governingDir = findGoverningTsconfigDir(normalizedImporterDir);
639
+ const governingPath = governingDir ? path2.join(governingDir, "tsconfig.json") : "";
640
+ const key = `${normalizedImporterDir}|${governingPath}|${governingPath ? dependencySignature(governingPath) : "missing"}`;
641
+ let projects = referencesCache.get(key);
642
+ if (!projects) {
643
+ projects = /* @__PURE__ */ new Map();
644
+ const configDir = governingDir;
645
+ if (configDir) {
646
+ collectReferencedProjects(path2.join(configDir, "tsconfig.json"), projects, /* @__PURE__ */ new Set());
647
+ }
648
+ referencesCache.set(key, projects);
649
+ }
650
+ return projects.get(specifier);
651
+ }
652
+
653
+ // dist/clients/review-graph/import-resolvers.js
654
+ function isWithin(cwd, p) {
655
+ return isUnderDir(path3.resolve(p), path3.resolve(cwd));
656
+ }
657
+ function isFile(p) {
658
+ try {
659
+ return fs4.statSync(p).isFile();
660
+ } catch {
661
+ return false;
662
+ }
663
+ }
664
+ function isDir(p) {
665
+ try {
666
+ return fs4.statSync(p).isDirectory();
667
+ } catch {
668
+ return false;
669
+ }
670
+ }
671
+ function firstExistingFile(cwd, candidates) {
672
+ for (const c of candidates) {
673
+ if (isFile(c) && isWithin(cwd, c))
674
+ return [normalizeMapKey(c)];
675
+ }
676
+ return [];
677
+ }
678
+ function sourceFilesIn(cwd, dir, ext) {
679
+ if (!isDir(dir) || !isWithin(cwd, dir))
680
+ return [];
681
+ try {
682
+ return fs4.readdirSync(dir).filter((n) => n.endsWith(ext)).map((n) => normalizeMapKey(path3.join(dir, n))).sort();
683
+ } catch {
684
+ return [];
685
+ }
686
+ }
687
+ function resolveRelative(cwd, filePath, source, exts) {
688
+ const base = path3.resolve(path3.dirname(filePath), source);
689
+ return firstExistingFile(cwd, [base, ...exts.map((e) => base + e)]);
690
+ }
691
+ function resolveDart(cwd, filePath, source) {
692
+ if (source.startsWith("package:") || source.startsWith("dart:"))
693
+ return [];
694
+ return resolveRelative(cwd, filePath, source, [".dart"]);
695
+ }
696
+ var JS_TS_EXT_RE = /\.(mjs|cjs|mts|cts|jsx?|tsx?)$/i;
697
+ function jsTsExtensionCandidates(base, strippedBase, ext) {
698
+ const candidates = [];
699
+ if (ext === ".mjs")
700
+ candidates.push(`${strippedBase}.mts`);
701
+ if (ext === ".cjs")
702
+ candidates.push(`${strippedBase}.cts`);
703
+ if (ext === ".mts")
704
+ candidates.push(`${strippedBase}.mts`);
705
+ if (ext === ".cts")
706
+ candidates.push(`${strippedBase}.cts`);
707
+ candidates.push(`${strippedBase}.ts`, `${strippedBase}.tsx`, `${strippedBase}.mts`, `${strippedBase}.cts`);
708
+ candidates.push(base, `${strippedBase}.js`, `${strippedBase}.jsx`, `${strippedBase}.mjs`, `${strippedBase}.cjs`);
709
+ for (const index of [
710
+ "index.ts",
711
+ "index.tsx",
712
+ "index.mts",
713
+ "index.cts",
714
+ "index.js",
715
+ "index.jsx",
716
+ "index.mjs",
717
+ "index.cjs"
718
+ ]) {
719
+ candidates.push(path3.join(base, index));
720
+ }
721
+ return [...new Set(candidates)];
722
+ }
723
+ function jsTsCandidatePaths(filePath, source) {
724
+ const base = path3.resolve(path3.dirname(filePath), source);
725
+ const strippedSource = source.replace(JS_TS_EXT_RE, "");
726
+ const strippedBase = strippedSource === source ? base : path3.resolve(path3.dirname(filePath), strippedSource);
727
+ const ext = path3.extname(source).toLowerCase();
728
+ return jsTsExtensionCandidates(base, strippedBase, ext);
729
+ }
730
+ function findWorkspaceModuleForSpecifier(modules, source) {
731
+ let best;
732
+ for (const mod of modules) {
733
+ let subpath;
734
+ if (source === mod.name)
735
+ subpath = "";
736
+ else if (source.startsWith(`${mod.name}/`))
737
+ subpath = source.slice(mod.name.length + 1);
738
+ if (subpath === void 0)
739
+ continue;
740
+ if (!best || mod.name.length > best.mod.name.length)
741
+ best = { mod, subpath };
742
+ }
743
+ return best;
744
+ }
745
+ function pickExportsMain(exportsField) {
746
+ if (typeof exportsField === "string")
747
+ return exportsField;
748
+ if (!exportsField || typeof exportsField !== "object")
749
+ return void 0;
750
+ const obj = exportsField;
751
+ const dot = obj["."] ?? obj;
752
+ if (typeof dot === "string")
753
+ return dot;
754
+ if (dot && typeof dot === "object") {
755
+ for (const key of ["import", "require", "default"]) {
756
+ const v = dot[key];
757
+ if (typeof v === "string")
758
+ return v;
759
+ }
760
+ }
761
+ return void 0;
762
+ }
763
+ function workspaceEntryCandidates(pkgRoot) {
764
+ let main;
765
+ try {
766
+ const pkg = JSON.parse(fs4.readFileSync(path3.join(pkgRoot, "package.json"), "utf-8"));
767
+ main = pickExportsMain(pkg.exports) ?? pkg.module ?? pkg.main;
768
+ } catch {
769
+ }
770
+ const candidates = [];
771
+ if (main) {
772
+ const mainPath = path3.resolve(pkgRoot, main);
773
+ const ext = path3.extname(mainPath).toLowerCase();
774
+ const stripped = mainPath.replace(JS_TS_EXT_RE, "");
775
+ candidates.push(...jsTsExtensionCandidates(mainPath, stripped, ext));
776
+ }
777
+ for (const base of [pkgRoot, path3.join(pkgRoot, "src")]) {
778
+ candidates.push(path3.join(base, "index.ts"), path3.join(base, "index.tsx"), path3.join(base, "index.mts"), path3.join(base, "index.cts"), path3.join(base, "index.js"), path3.join(base, "index.jsx"), path3.join(base, "index.mjs"), path3.join(base, "index.cjs"));
779
+ }
780
+ return [...new Set(candidates)];
781
+ }
782
+ function workspaceSubpathCandidates(pkgRoot, subpath) {
783
+ const base = path3.join(pkgRoot, subpath);
784
+ const strippedSubpath = subpath.replace(JS_TS_EXT_RE, "");
785
+ const strippedBase = strippedSubpath === subpath ? base : path3.join(pkgRoot, strippedSubpath);
786
+ const ext = path3.extname(subpath).toLowerCase();
787
+ return jsTsExtensionCandidates(base, strippedBase, ext);
788
+ }
789
+ function resolveAliasedImport(cwd, specifier, importerDir) {
790
+ for (const target of aliasedImportTargets(specifier, importerDir)) {
791
+ const ext = path3.extname(target).toLowerCase();
792
+ const stripped = target.replace(JS_TS_EXT_RE, "");
793
+ const found = firstExistingFile(cwd, jsTsExtensionCandidates(target, stripped, ext));
794
+ if (found.length)
795
+ return found;
796
+ }
797
+ return [];
798
+ }
799
+ function resolveProjectReferenceImport(cwd, specifier, importerDir) {
800
+ const target = referencedProjectImportTarget(specifier, importerDir);
801
+ return target ? firstExistingFile(cwd, [target]) : [];
802
+ }
803
+ function resolveWorkspacePackageImport(cwd, source) {
804
+ if (source.startsWith("."))
805
+ return [];
806
+ const graph = buildModuleGraph(cwd);
807
+ if (!graph)
808
+ return [];
809
+ const match = findWorkspaceModuleForSpecifier(graph.modules.values(), source);
810
+ if (!match)
811
+ return [];
812
+ const candidates = match.subpath ? workspaceSubpathCandidates(match.mod.root, match.subpath) : workspaceEntryCandidates(match.mod.root);
813
+ return firstExistingFile(cwd, candidates);
814
+ }
815
+ function resolveJsTs(cwd, filePath, source) {
816
+ if (!source.startsWith(".")) {
817
+ const aliased = resolveAliasedImport(cwd, source, path3.dirname(filePath));
818
+ if (aliased.length)
819
+ return aliased;
820
+ const referenced = resolveProjectReferenceImport(cwd, source, path3.dirname(filePath));
821
+ return referenced.length ? referenced : resolveWorkspacePackageImport(cwd, source);
822
+ }
823
+ return firstExistingFile(cwd, jsTsCandidatePaths(filePath, source));
824
+ }
825
+ function resolveCxx(cwd, filePath, source) {
826
+ if (source.startsWith("<"))
827
+ return [];
828
+ const dir = path3.dirname(path3.resolve(filePath));
829
+ return firstExistingFile(cwd, [
830
+ path3.resolve(dir, source),
831
+ path3.resolve(cwd, source),
832
+ path3.resolve(cwd, "include", source),
833
+ path3.resolve(cwd, "src", source)
834
+ ]);
835
+ }
836
+ function pythonRoots(cwd, fileDir) {
837
+ let p = fileDir;
838
+ const root = path3.resolve(cwd);
839
+ while (isFile(path3.join(p, "__init__.py")) && isWithin(cwd, p)) {
840
+ const parent = path3.dirname(p);
841
+ if (parent === p)
842
+ break;
843
+ p = parent;
844
+ }
845
+ const roots = /* @__PURE__ */ new Set([p, root, path3.join(root, "src")]);
846
+ return [...roots].filter((r) => isDir(r));
847
+ }
848
+ function resolvePython(cwd, filePath, source) {
849
+ const fileDir = path3.dirname(path3.resolve(filePath));
850
+ if (source.startsWith(".")) {
851
+ const m = source.match(/^(\.+)(.*)$/);
852
+ if (!m)
853
+ return [];
854
+ const dots = m[1].length;
855
+ let baseDir = fileDir;
856
+ for (let i = 1; i < dots; i++)
857
+ baseDir = path3.dirname(baseDir);
858
+ const rest = m[2] ? m[2].split(".") : [];
859
+ const target = path3.join(baseDir, ...rest);
860
+ return firstExistingFile(cwd, [
861
+ `${target}.py`,
862
+ path3.join(target, "__init__.py")
863
+ ]);
864
+ }
865
+ const parts = source.split(".");
866
+ for (const root of pythonRoots(cwd, fileDir)) {
867
+ const target = path3.join(root, ...parts);
868
+ const found = firstExistingFile(cwd, [
869
+ `${target}.py`,
870
+ path3.join(target, "__init__.py")
871
+ ]);
872
+ if (found.length)
873
+ return found;
874
+ }
875
+ return [];
876
+ }
877
+ function findGoModule(cwd, filePath) {
878
+ let dir = path3.dirname(path3.resolve(filePath));
879
+ const root = path3.resolve(cwd);
880
+ while (true) {
881
+ try {
882
+ const content = fs4.readFileSync(path3.join(dir, "go.mod"), "utf-8");
883
+ const m = content.match(/^[^\S\n\r\u2028\u2029]*module\s+(\S+)/m);
884
+ if (m)
885
+ return { moduleDir: dir, modulePath: m[1] };
886
+ } catch {
887
+ }
888
+ if (dir === root)
889
+ break;
890
+ const parent = path3.dirname(dir);
891
+ if (parent === dir || !isWithin(cwd, parent))
892
+ break;
893
+ dir = parent;
894
+ }
895
+ return null;
896
+ }
897
+ function resolveGo(cwd, filePath, source) {
898
+ const mod = findGoModule(cwd, filePath);
899
+ if (!mod)
900
+ return [];
901
+ if (source !== mod.modulePath && !source.startsWith(`${mod.modulePath}/`)) {
902
+ return [];
903
+ }
904
+ const rel = source === mod.modulePath ? "" : source.slice(mod.modulePath.length + 1);
905
+ return sourceFilesIn(cwd, path3.join(mod.moduleDir, rel), ".go");
906
+ }
907
+ function javaSourceRoots(cwd, filePath) {
908
+ const root = path3.resolve(cwd);
909
+ const roots = /* @__PURE__ */ new Set();
910
+ for (const c of ["src/main/java", "src/test/java", "src", ""]) {
911
+ roots.add(path3.join(root, c));
912
+ }
913
+ let p = path3.dirname(path3.resolve(filePath));
914
+ while (true) {
915
+ roots.add(p);
916
+ if (p === root)
917
+ break;
918
+ const parent = path3.dirname(p);
919
+ if (parent === p || !isWithin(cwd, parent))
920
+ break;
921
+ p = parent;
922
+ }
923
+ return [...roots].filter((r) => isDir(r));
924
+ }
925
+ function resolveJava(cwd, filePath, source) {
926
+ const parts = source.split(".");
927
+ for (const root of javaSourceRoots(cwd, filePath)) {
928
+ const asFile = firstExistingFile(cwd, [
929
+ `${path3.join(root, ...parts)}.java`
930
+ ]);
931
+ if (asFile.length)
932
+ return asFile;
933
+ const asPkg = sourceFilesIn(cwd, path3.join(root, ...parts), ".java");
934
+ if (asPkg.length)
935
+ return asPkg;
936
+ if (parts.length > 1) {
937
+ const dropLast = firstExistingFile(cwd, [
938
+ `${path3.join(root, ...parts.slice(0, -1))}.java`
939
+ ]);
940
+ if (dropLast.length)
941
+ return dropLast;
942
+ }
943
+ }
944
+ return [];
945
+ }
946
+ function importSourceNameParts(source) {
947
+ return source.match(/[\w$-]+/g) ?? [];
948
+ }
949
+ function fileAnswerNames(file) {
950
+ return [
951
+ path3.basename(file).split(".")[0] ?? "",
952
+ path3.basename(path3.dirname(file))
953
+ ].flatMap((name) => name.length > 0 ? [name] : []);
954
+ }
955
+ function resolveImportToFiles(cwd, filePath, languageId, source) {
956
+ switch (languageId) {
957
+ case "typescript":
958
+ case "tsx":
959
+ case "javascript":
960
+ case "jsts":
961
+ return resolveJsTs(cwd, filePath, source);
962
+ case "c":
963
+ case "cpp":
964
+ return resolveCxx(cwd, filePath, source);
965
+ case "ruby":
966
+ return resolveRelative(cwd, filePath, source, [".rb"]);
967
+ case "zig":
968
+ return resolveRelative(cwd, filePath, source, [".zig"]);
969
+ case "bash":
970
+ return resolveRelative(cwd, filePath, source, [".sh", ".bash"]);
971
+ case "dart":
972
+ return resolveDart(cwd, filePath, source);
973
+ case "python":
974
+ return resolvePython(cwd, filePath, source);
975
+ case "go":
976
+ return resolveGo(cwd, filePath, source);
977
+ case "java":
978
+ return resolveJava(cwd, filePath, source);
979
+ default:
980
+ return [];
981
+ }
982
+ }
983
+
984
+ // dist/clients/tree-sitter-symbol-extractor.js
985
+ import * as path4 from "node:path";
986
+ var SYMBOL_QUERIES = {
987
+ typescript: {
988
+ defs: `
989
+ ;; Function declarations: function foo(params) { }
990
+ (function_declaration
991
+ name: (identifier) @funcName
992
+ parameters: (formal_parameters) @funcParams
993
+ body: (statement_block) @funcBody) @funcDef
994
+
995
+ ;; Arrow functions: const foo = (params) => { }
996
+ (variable_declarator
997
+ name: (identifier) @arrowName
998
+ value: (arrow_function
999
+ parameters: (formal_parameters) @arrowParams
1000
+ body: (_) @arrowBody)) @arrowDef
1001
+
1002
+ ;; Class declarations: class Foo { }
1003
+ (class_declaration
1004
+ name: (type_identifier) @className) @classDef
1005
+
1006
+ ;; Method definitions: class Foo { bar() { } }
1007
+ (method_definition
1008
+ name: (property_identifier) @methodName
1009
+ parameters: (formal_parameters) @methodParams) @methodDef
1010
+
1011
+ ;; Interface declarations: interface Foo { }
1012
+ (interface_declaration
1013
+ name: (type_identifier) @interfaceName) @interfaceDef
1014
+
1015
+ ;; Type alias: type Foo = ...
1016
+ (type_alias_declaration
1017
+ name: (type_identifier) @typeName) @typeDef
1018
+ `,
1019
+ refs: `
1020
+ ;; Function/method calls: foo() or obj.bar()
1021
+ (call_expression
1022
+ function: (identifier) @callIdent) @callRef
1023
+
1024
+ (call_expression
1025
+ function: (member_expression
1026
+ object: (_)
1027
+ property: (property_identifier) @callMethod)) @callMethodRef
1028
+
1029
+ ;; New expressions: new Foo()
1030
+ (new_expression
1031
+ constructor: (identifier) @newIdent) @newRef
1032
+
1033
+ ;; Type references: type T = Foo
1034
+ (type_identifier) @typeIdent
1035
+ `
1036
+ },
1037
+ python: {
1038
+ defs: `
1039
+ ;; Function definitions: def foo(params):
1040
+ (function_definition
1041
+ name: (identifier) @funcName
1042
+ parameters: (parameters) @funcParams) @funcDef
1043
+
1044
+ ;; Class definitions: class Foo:
1045
+ (class_definition
1046
+ name: (identifier) @className) @classDef
1047
+
1048
+ ;; Method definitions (within class)
1049
+ (class_definition
1050
+ body: (block
1051
+ (function_definition
1052
+ name: (identifier) @methodName
1053
+ parameters: (parameters) @methodParams) @methodDef))
1054
+ `,
1055
+ refs: `
1056
+ ;; Function calls: foo() or obj.bar()
1057
+ (call
1058
+ function: (identifier) @callIdent) @callRef
1059
+
1060
+ (call
1061
+ function: (attribute
1062
+ object: (_)
1063
+ attribute: (identifier) @callMethod)) @callMethodRef
1064
+ `
1065
+ },
1066
+ rust: {
1067
+ defs: `
1068
+ ;; Function definitions: fn foo(params) { }
1069
+ (function_item
1070
+ name: (identifier) @funcName
1071
+ parameters: (parameters) @funcParams) @funcDef
1072
+
1073
+ ;; Struct definitions: struct Foo { }
1074
+ (struct_item
1075
+ name: (type_identifier) @structName) @structDef
1076
+
1077
+ ;; Impl blocks: impl Foo { fn bar() { } }
1078
+ (impl_item
1079
+ type: (type_identifier) @implType
1080
+ body: (declaration_list
1081
+ (function_item
1082
+ name: (identifier) @implMethodName) @implMethodDef))
1083
+ `,
1084
+ refs: `
1085
+ ;; Function calls: foo() or obj.bar()
1086
+ (call_expression
1087
+ function: (identifier) @callIdent) @callRef
1088
+
1089
+ (call_expression
1090
+ function: (field_expression
1091
+ value: (_)
1092
+ field: (field_identifier) @callField)) @callFieldRef
1093
+ `
1094
+ },
1095
+ go: {
1096
+ defs: `
1097
+ (function_declaration
1098
+ name: (identifier) @funcName
1099
+ parameters: (parameter_list) @funcParams) @funcDef
1100
+
1101
+ (method_declaration
1102
+ name: (field_identifier) @methodName
1103
+ parameters: (parameter_list) @methodParams) @methodDef
1104
+
1105
+ (type_spec
1106
+ name: (type_identifier) @typeName) @typeDef
1107
+ `,
1108
+ refs: `
1109
+ (call_expression
1110
+ function: (identifier) @callIdent) @callRef
1111
+
1112
+ (call_expression
1113
+ function: (selector_expression
1114
+ field: (field_identifier) @callMethod)) @callMethodRef
1115
+ `
1116
+ },
1117
+ ruby: {
1118
+ defs: `
1119
+ (method
1120
+ name: (identifier) @methodName) @methodDef
1121
+
1122
+ (singleton_method
1123
+ name: (identifier) @methodName) @methodDef
1124
+
1125
+ (class
1126
+ name: (constant) @className) @classDef
1127
+
1128
+ (module
1129
+ name: (constant) @moduleName) @moduleDef
1130
+ `,
1131
+ refs: `
1132
+ (call
1133
+ method: (identifier) @callIdent) @callRef
1134
+
1135
+ (call
1136
+ method: (constant) @typeIdent) @typeRef
1137
+ `
1138
+ },
1139
+ c: {
1140
+ defs: `
1141
+ (function_definition
1142
+ declarator: (function_declarator
1143
+ declarator: (identifier) @funcName
1144
+ parameters: (parameter_list) @funcParams)) @funcDef
1145
+
1146
+ (type_definition
1147
+ declarator: (type_identifier) @typeName) @typeDef
1148
+
1149
+ (struct_specifier
1150
+ name: (type_identifier) @typeName) @typeDef
1151
+
1152
+ (enum_specifier
1153
+ name: (type_identifier) @typeName) @typeDef
1154
+ `,
1155
+ refs: `
1156
+ (call_expression
1157
+ function: (identifier) @callIdent) @callRef
1158
+
1159
+ (call_expression
1160
+ function: (field_expression
1161
+ field: (field_identifier) @callField)) @callFieldRef
1162
+
1163
+ (type_identifier) @typeIdent
1164
+ `
1165
+ },
1166
+ cpp: {
1167
+ defs: `
1168
+ (function_definition
1169
+ declarator: (function_declarator
1170
+ declarator: (identifier) @funcName
1171
+ parameters: (parameter_list) @funcParams)) @funcDef
1172
+
1173
+ (class_specifier
1174
+ name: (type_identifier) @className) @classDef
1175
+
1176
+ (struct_specifier
1177
+ name: (type_identifier) @className) @classDef
1178
+
1179
+ (type_definition
1180
+ declarator: (type_identifier) @typeName) @typeDef
1181
+ `,
1182
+ refs: `
1183
+ (call_expression
1184
+ function: (identifier) @callIdent) @callRef
1185
+
1186
+ (call_expression
1187
+ function: (field_expression
1188
+ field: (field_identifier) @callField)) @callFieldRef
1189
+
1190
+ (type_identifier) @typeIdent
1191
+ `
1192
+ },
1193
+ java: {
1194
+ defs: `
1195
+ (method_declaration
1196
+ name: (identifier) @methodName
1197
+ parameters: (formal_parameters) @methodParams) @methodDef
1198
+
1199
+ (class_declaration
1200
+ name: (identifier) @className) @classDef
1201
+
1202
+ (interface_declaration
1203
+ name: (identifier) @interfaceName) @interfaceDef
1204
+
1205
+ (constructor_declaration
1206
+ name: (identifier) @funcName
1207
+ parameters: (formal_parameters) @funcParams) @funcDef
1208
+
1209
+ (enum_declaration
1210
+ name: (identifier) @className) @classDef
1211
+ `,
1212
+ refs: `
1213
+ (method_invocation
1214
+ name: (identifier) @callMethod) @callRef
1215
+
1216
+ (object_creation_expression
1217
+ type: (type_identifier) @newIdent) @newRef
1218
+
1219
+ (type_identifier) @typeIdent
1220
+ `
1221
+ },
1222
+ kotlin: {
1223
+ // #251: class_declaration's name is type_identifier; the old extra
1224
+ // `(class_declaration (simple_identifier) @className)` was a bad pattern that
1225
+ // failed query compilation (taking the whole language down). Call refs use
1226
+ // no field name in the shipped grammar (the old `calleeExpression:` field
1227
+ // also failed to compile).
1228
+ defs: `
1229
+ (function_declaration
1230
+ (simple_identifier) @funcName) @funcDef
1231
+
1232
+ (class_declaration
1233
+ (type_identifier) @className) @classDef
1234
+
1235
+ (object_declaration
1236
+ (type_identifier) @className) @classDef
1237
+ `,
1238
+ refs: `
1239
+ (call_expression
1240
+ (simple_identifier) @callIdent) @callRef
1241
+
1242
+ (user_type (type_identifier) @typeIdent)
1243
+ `
1244
+ },
1245
+ dart: {
1246
+ // #251: function_signature/class_definition take their name/params as direct
1247
+ // children in the shipped grammar — the old `name:`/`parameters:` fields did
1248
+ // not exist and failed query compilation. method bodies' inner
1249
+ // function_signature also matches @funcDef (methods surface as functions).
1250
+ defs: `
1251
+ (function_signature
1252
+ (identifier) @funcName
1253
+ (formal_parameter_list) @funcParams) @funcDef
1254
+
1255
+ (class_definition
1256
+ (identifier) @className) @classDef
1257
+ `,
1258
+ refs: `
1259
+ (type_identifier) @typeIdent
1260
+ `
1261
+ },
1262
+ elixir: {
1263
+ defs: `
1264
+ (call
1265
+ target: (identifier) @_kw
1266
+ (arguments
1267
+ (call
1268
+ target: (identifier) @funcName
1269
+ (arguments) @funcParams) @funcDef)
1270
+ (#match? @_kw "^def[pm]?$"))
1271
+
1272
+ (call
1273
+ target: (identifier) @_kw
1274
+ (arguments (alias) @moduleName) @moduleDef
1275
+ (#match? @_kw "^defmodule$"))
1276
+ `,
1277
+ refs: `
1278
+ (call
1279
+ target: (identifier) @callIdent) @callRef
1280
+
1281
+ (alias) @typeIdent
1282
+ `
1283
+ },
1284
+ csharp: {
1285
+ defs: `
1286
+ (method_declaration
1287
+ name: (identifier) @methodName
1288
+ parameters: (parameter_list) @methodParams) @methodDef
1289
+
1290
+ (class_declaration
1291
+ name: (identifier) @className) @classDef
1292
+
1293
+ (struct_declaration
1294
+ name: (identifier) @className) @classDef
1295
+
1296
+ (interface_declaration
1297
+ name: (identifier) @interfaceName) @interfaceDef
1298
+
1299
+ (constructor_declaration
1300
+ name: (identifier) @funcName
1301
+ parameters: (parameter_list) @funcParams) @funcDef
1302
+
1303
+ (enum_declaration
1304
+ name: (identifier) @className) @classDef
1305
+ `,
1306
+ refs: `
1307
+ (invocation_expression
1308
+ function: (identifier) @callIdent) @callRef
1309
+
1310
+ (invocation_expression
1311
+ function: (member_access_expression
1312
+ name: (identifier) @callMethod)) @callMethodRef
1313
+
1314
+ (object_creation_expression
1315
+ type: (identifier) @newIdent) @newRef
1316
+ `
1317
+ },
1318
+ php: {
1319
+ defs: `
1320
+ (function_definition
1321
+ name: (name) @funcName
1322
+ parameters: (formal_parameters) @funcParams) @funcDef
1323
+
1324
+ (method_declaration
1325
+ name: (name) @methodName
1326
+ parameters: (formal_parameters) @methodParams) @methodDef
1327
+
1328
+ (class_declaration
1329
+ name: (name) @className) @classDef
1330
+
1331
+ (interface_declaration
1332
+ name: (name) @interfaceName) @interfaceDef
1333
+
1334
+ (trait_declaration
1335
+ name: (name) @className) @classDef
1336
+ `,
1337
+ refs: `
1338
+ (function_call_expression
1339
+ function: (name) @callIdent) @callRef
1340
+
1341
+ (member_call_expression
1342
+ name: (name) @callMethod) @callMethodRef
1343
+
1344
+ (object_creation_expression
1345
+ (name) @newIdent) @newRef
1346
+ `
1347
+ },
1348
+ swift: {
1349
+ defs: `
1350
+ (function_declaration
1351
+ (simple_identifier) @funcName) @funcDef
1352
+
1353
+ (class_declaration
1354
+ (type_identifier) @className) @classDef
1355
+
1356
+ (protocol_declaration
1357
+ (type_identifier) @interfaceName) @interfaceDef
1358
+ `,
1359
+ refs: `
1360
+ (call_expression
1361
+ (simple_identifier) @callIdent) @callRef
1362
+
1363
+ (navigation_expression
1364
+ (simple_identifier) @callMethod) @callMethodRef
1365
+
1366
+ (type_identifier) @typeIdent
1367
+ `
1368
+ },
1369
+ lua: {
1370
+ // #255: pulled from @tree-sitter-grammars/tree-sitter-lua (the aggregator's
1371
+ // build corrupts once a 2nd grammar loads). That grammar names function defs
1372
+ // `function_declaration` — the name is either a direct (identifier) (global /
1373
+ // `local function`) or a (dot_index_expression) for `function M.run`. Calls
1374
+ // are `function_call` with an (identifier) or (dot_index_expression) callee.
1375
+ defs: `
1376
+ (function_declaration
1377
+ (identifier) @funcName) @funcDef
1378
+
1379
+ (function_declaration
1380
+ (dot_index_expression) @funcName) @funcDef
1381
+ `,
1382
+ refs: `
1383
+ (function_call (identifier) @callIdent) @callRef
1384
+
1385
+ (function_call (dot_index_expression) @callIdent) @callRef
1386
+ `
1387
+ },
1388
+ ocaml: {
1389
+ // #251: let_binding holds value_name as a direct child (no `pattern:` /
1390
+ // value_pattern wrapper), and module_binding's module_name is a direct child
1391
+ // (no `name:` field) — the old patterns failed query compilation.
1392
+ defs: `
1393
+ (value_definition
1394
+ (let_binding (value_name) @funcName)) @funcDef
1395
+
1396
+ (module_definition
1397
+ (module_binding (module_name) @moduleName)) @moduleDef
1398
+ `,
1399
+ refs: `
1400
+ (application_expression
1401
+ (value_path (value_name) @callIdent)) @callRef
1402
+
1403
+ (value_path (value_name) @callIdent) @callRef
1404
+ `
1405
+ },
1406
+ zig: {
1407
+ defs: `
1408
+ (function_declaration
1409
+ (identifier) @funcName) @funcDef
1410
+ `,
1411
+ // #251: the old `field_access` node name doesn't exist in this grammar and
1412
+ // failed to compile; call_expression refs are valid.
1413
+ refs: `
1414
+ (call_expression
1415
+ (identifier) @callIdent) @callRef
1416
+ `
1417
+ },
1418
+ bash: {
1419
+ defs: `
1420
+ (function_definition
1421
+ name: (word) @funcName) @funcDef
1422
+ `,
1423
+ refs: `
1424
+ (command
1425
+ name: (command_name (word) @callIdent)) @callRef
1426
+ `
1427
+ },
1428
+ // #1522: tree-sitter-cue (eonpatapon/tree-sitter-cue, vendored per #1520) has
1429
+ // no `label:`/`value:` field names on `field` — verified directly against the
1430
+ // committed wasm (`value:` fails query compilation with "Bad field name");
1431
+ // only `let_clause`'s `left:`/`right:` and `call_expression`'s `function:`
1432
+ // are real fields. A `#Definition` is not a distinct node kind: `#Service`
1433
+ // parses as a plain (identifier) whose text happens to start with "#", so
1434
+ // definitions are told apart from ordinary struct fields with a `#match?`/
1435
+ // `#not-match?` text predicate rather than a grammar-level distinction —
1436
+ // both predicates must sit INSIDE the pattern's own parens (this repo's
1437
+ // tree-sitter gotcha). `#Definition`s map to `type` (the closest existing
1438
+ // SymbolKind to a reusable CUE schema); plain fields map to `property`;
1439
+ // `let` bindings map to `variable`.
1440
+ //
1441
+ // Two known upstream grammar rough edges (#1522 review round 1, F4;
1442
+ // docs/language-coverage.md's CUE row has the same note, kept in sync):
1443
+ // (1) an aliased field label (`X=name: value`) exposes BOTH identifiers
1444
+ // as untagged siblings under one `label` node — `(label alias:
1445
+ // (identifier) (identifier))` — with no field/structural way to tell the
1446
+ // alias from the real name, so `fieldDef` spuriously captures the alias
1447
+ // too, alongside the correct symbol for `name`. (2) a multi-hash raw
1448
+ // string (`##"..."##`, 2+ `#` delimiters) mis-parses regardless of
1449
+ // content — the field's value becomes a bare `identifier` instead of a
1450
+ // `string`, which can surface as a spurious `#`-prefixed ref via the
1451
+ // `typeIdent` pattern below. Both are grammar bugs, not query bugs — no
1452
+ // query change here can distinguish what the parse tree doesn't.
1453
+ cue: {
1454
+ defs: `
1455
+ (field
1456
+ (label (identifier) @defName)
1457
+ (#match? @defName "^#")) @defDef
1458
+
1459
+ (field
1460
+ (label (identifier) @fieldName)
1461
+ (value (_)) @fieldValue
1462
+ (#not-match? @fieldName "^#")) @fieldDef
1463
+
1464
+ (let_clause
1465
+ left: (identifier) @letName
1466
+ right: (_) @letValue) @letDef
1467
+ `,
1468
+ // `#Service` used as a value (e.g. `#Other: #Service & {...}`) is the same
1469
+ // (identifier) node kind as its own definition label — there is no
1470
+ // separate reference/definition node kind in this grammar, so the
1471
+ // definition's own label also matches this pattern, mirroring the same
1472
+ // self-match already accepted for swift/java's broad `type_identifier`
1473
+ // ref patterns above.
1474
+ refs: `
1475
+ ((identifier) @typeIdent
1476
+ (#match? @typeIdent "^#"))
1477
+
1478
+ (call_expression
1479
+ function: (identifier) @callIdent) @callRef
1480
+
1481
+ (call_expression
1482
+ function: (selector_expression (identifier) (identifier) @callMethod)) @callMethodRef
1483
+ `
1484
+ }
1485
+ };
1486
+ SYMBOL_QUERIES.tsx = SYMBOL_QUERIES.typescript;
1487
+ SYMBOL_QUERIES.javascript = {
1488
+ defs: `
1489
+ ;; Function declarations: function foo(params) { }
1490
+ (function_declaration
1491
+ name: (identifier) @funcName
1492
+ parameters: (formal_parameters) @funcParams
1493
+ body: (statement_block) @funcBody) @funcDef
1494
+
1495
+ ;; Arrow functions: const foo = (params) => { }
1496
+ (variable_declarator
1497
+ name: (identifier) @arrowName
1498
+ value: (arrow_function
1499
+ parameters: (formal_parameters) @arrowParams
1500
+ body: (_) @arrowBody)) @arrowDef
1501
+
1502
+ ;; Class declarations: class Foo { } \u2014 (identifier) name on this grammar.
1503
+ (class_declaration
1504
+ name: (identifier) @className) @classDef
1505
+
1506
+ ;; Method definitions: class Foo { bar() { } }
1507
+ (method_definition
1508
+ name: (property_identifier) @methodName
1509
+ parameters: (formal_parameters) @methodParams) @methodDef
1510
+ `,
1511
+ refs: `
1512
+ ;; Function/method calls: foo() or obj.bar()
1513
+ (call_expression
1514
+ function: (identifier) @callIdent) @callRef
1515
+
1516
+ (call_expression
1517
+ function: (member_expression
1518
+ object: (_)
1519
+ property: (property_identifier) @callMethod)) @callMethodRef
1520
+
1521
+ ;; New expressions: new Foo()
1522
+ (new_expression
1523
+ constructor: (identifier) @newIdent) @newRef
1524
+ `
1525
+ };
1526
+ function symbolExtractionGrammar(kind, filePath) {
1527
+ if (!kind)
1528
+ return void 0;
1529
+ const kindGrammar = KIND_TO_GRAMMAR[kind];
1530
+ if (!kindGrammar)
1531
+ return void 0;
1532
+ const byExtension = filePath ? EXTENSION_TO_GRAMMAR[path4.extname(filePath).toLowerCase()] : void 0;
1533
+ const grammar = byExtension ?? kindGrammar;
1534
+ return grammar in SYMBOL_QUERIES ? grammar : void 0;
1535
+ }
1536
+ var JS_TS_IMPORT_QUERY = `
1537
+ (comment) @importComment
1538
+ (import_statement source: (string) @importSource)
1539
+ (export_statement source: (string) @importSource)
1540
+ (call_expression
1541
+ function: (import)
1542
+ arguments: (arguments . (string) @importSource))
1543
+ (call_expression
1544
+ function: (identifier) @_m
1545
+ arguments: (arguments . (string) @importSource)
1546
+ (#eq? @_m "require"))
1547
+ `;
1548
+ var IMPORT_QUERIES = {
1549
+ // ESM import + re-export source strings; `source:` is a (string) on both the
1550
+ // typescript and tsx grammars (validated). parseImportMatch strips the quotes.
1551
+ // #4438: a dynamic `import("x")` and a `require("x")` call with a string
1552
+ // argument count too, as the review graph's import facts already count them
1553
+ // (`dispatch/facts/import-facts.ts`): the inline-blocker sweep and the
1554
+ // created-module producer read this query, and a dependency it missed
1555
+ // stayed stale there. Node names probed on the shipped typescript, tsx and
1556
+ // javascript grammars; a pattern one grammar lacks would fail the whole
1557
+ // query. The ambient-module extension is TS/TSX-only; JavaScript uses
1558
+ // the common query. `import x = require()` remains outside this query.
1559
+ typescript: `${JS_TS_IMPORT_QUERY}
1560
+ (ambient_declaration (module name: (string) @importSource))`,
1561
+ tsx: `${JS_TS_IMPORT_QUERY}
1562
+ (ambient_declaration (module name: (string) @importSource))`,
1563
+ // #include "foo.h" (string_literal) and #include <stdio.h> (system_lib_string)
1564
+ // on both the c and cpp grammars (validated). parseImportMatch strips the quotes
1565
+ // from the local form; the system form keeps its <> so the resolver/bucketer can
1566
+ // tell a system header from a local include.
1567
+ c: `
1568
+ (preproc_include path: (string_literal) @importSource)
1569
+ (preproc_include path: (system_lib_string) @importSource)
1570
+ `,
1571
+ cpp: `
1572
+ (preproc_include path: (string_literal) @importSource)
1573
+ (preproc_include path: (system_lib_string) @importSource)
1574
+ `,
1575
+ python: `
1576
+ (import_statement name: (dotted_name) @importSource)
1577
+ (import_statement name: (aliased_import name: (dotted_name) @importSource))
1578
+ (import_from_statement module_name: (dotted_name) @importSource)
1579
+ (import_from_statement module_name: (relative_import) @importSource)
1580
+ `,
1581
+ go: `(import_spec path: (interpreted_string_literal) @importSource)`,
1582
+ rust: `(use_declaration argument: (_) @importSource)`,
1583
+ java: `(import_declaration (scoped_identifier) @importSource)`,
1584
+ kotlin: `(import_header (identifier) @importSource)`,
1585
+ csharp: `
1586
+ (using_directive (identifier) @importSource)
1587
+ (using_directive (qualified_name) @importSource)
1588
+ `,
1589
+ swift: `(import_declaration (identifier) @importSource)`,
1590
+ php: `(namespace_use_clause (name) @importSource)`,
1591
+ ocaml: `(open_module (module_path) @importSource)`,
1592
+ dart: `(import_specification (configurable_uri (uri (string_literal) @importSource)))`,
1593
+ // local x = require("mod.a") — a plain function call, not a statement. The
1594
+ // pulled @tree-sitter-grammars grammar (#255) names it function_call.
1595
+ lua: `
1596
+ (function_call
1597
+ (identifier) @_m
1598
+ (arguments (string) @importSource)
1599
+ (#match? @_m "^require$"))
1600
+ `,
1601
+ // require "x" / require_relative "x" — covers both via the "^require" prefix.
1602
+ ruby: `
1603
+ (call
1604
+ method: (identifier) @_m
1605
+ (argument_list (string (string_content) @importSource))
1606
+ (#match? @_m "^require"))
1607
+ `,
1608
+ // @import("std") — a builtin call, not a statement.
1609
+ zig: `
1610
+ (builtin_function (builtin_identifier) @_m
1611
+ (arguments (string (string_content) @importSource))
1612
+ (#match? @_m "^@import$"))
1613
+ `,
1614
+ // import/alias/require/use Foo — macro calls; excludes defmodule and friends.
1615
+ elixir: `
1616
+ (call target: (identifier) @_m
1617
+ (arguments (alias) @importSource)
1618
+ (#match? @_m "^(import|alias|require|use)$"))
1619
+ `,
1620
+ // source ./x.sh and . ./x.sh — the two POSIX file-include builtins.
1621
+ bash: `
1622
+ (command (command_name (word) @_m)
1623
+ (word) @importSource
1624
+ (#match? @_m "^(source|\\.)$"))
1625
+ `,
1626
+ // import "strings" / import x "encoding/json" — `import_spec`'s `path:`
1627
+ // field is verified against the committed wasm. Block-form
1628
+ // (`import (\n\t"strings"\n)`) nests each `import_spec` one level deeper,
1629
+ // under `import_spec_list`, rather than as a direct child of
1630
+ // `import_declaration` — a second pattern is needed, not a reshaped first
1631
+ // one (#1522 review round 1, F2: the single-line-only pattern extracted
1632
+ // zero imports from the block form).
1633
+ cue: `
1634
+ (import_declaration (import_spec path: (string) @importSource))
1635
+ (import_declaration (import_spec_list (import_spec path: (string) @importSource)))
1636
+ `
1637
+ };
1638
+ IMPORT_QUERIES.javascript = JS_TS_IMPORT_QUERY;
1639
+ function parseImportMatch(match) {
1640
+ const imports = [];
1641
+ for (const capture of match.captures) {
1642
+ if (capture.name === "importComment") {
1643
+ for (const tag of capture.node.text.matchAll(/@import\b[^@]*?\bfrom\s*(["'])([^"'\r\n]+)\1/g)) {
1644
+ imports.push({
1645
+ source: tag[2],
1646
+ line: capture.node.startPosition.row + 1
1647
+ });
1648
+ }
1649
+ continue;
1650
+ }
1651
+ if (capture.name !== "importSource")
1652
+ continue;
1653
+ const raw = String(capture.node.text ?? "").trim();
1654
+ const source = raw.replace(/^["'`]+|["'`]+$/g, "");
1655
+ if (!source)
1656
+ continue;
1657
+ imports.push({ source, line: capture.node.startPosition.row + 1 });
1658
+ }
1659
+ return imports;
1660
+ }
1661
+ var TreeSitterSymbolExtractor = class _TreeSitterSymbolExtractor {
1662
+ languageId;
1663
+ client;
1664
+ // biome-ignore lint/suspicious/noExplicitAny: Query type from web-tree-sitter
1665
+ defQuery = null;
1666
+ // biome-ignore lint/suspicious/noExplicitAny: Query type from web-tree-sitter
1667
+ refQuery = null;
1668
+ // biome-ignore lint/suspicious/noExplicitAny: Query type from web-tree-sitter
1669
+ importQuery = null;
1670
+ constructor(languageId, client) {
1671
+ this.languageId = languageId;
1672
+ this.client = client;
1673
+ }
1674
+ async init() {
1675
+ try {
1676
+ if (!await this.client.isLanguageSupported(this.languageId))
1677
+ return false;
1678
+ const language = this.client.getLanguage(this.languageId);
1679
+ if (!language)
1680
+ return false;
1681
+ const { Query } = await loadWebTreeSitter();
1682
+ const queries = SYMBOL_QUERIES[this.languageId];
1683
+ if (queries) {
1684
+ this.defQuery = this.compileQuery(Query, language, queries.defs, "defs");
1685
+ this.refQuery = this.compileQuery(Query, language, queries.refs, "refs");
1686
+ }
1687
+ const importQuerySrc = IMPORT_QUERIES[this.languageId];
1688
+ if (importQuerySrc) {
1689
+ this.importQuery = this.compileQuery(Query, language, importQuerySrc, "imports");
1690
+ }
1691
+ return Boolean(this.defQuery || this.importQuery);
1692
+ } catch (err) {
1693
+ this.client.reportWasmAbort(err);
1694
+ logTreeSitterDiagnostic({
1695
+ subsystem: "symbol-extractor",
1696
+ languageId: this.languageId,
1697
+ message: `Failed to init ${this.languageId}: ${err instanceof Error ? err.message : String(err)}`
1698
+ });
1699
+ return false;
1700
+ }
1701
+ }
1702
+ // biome-ignore lint/suspicious/noExplicitAny: web-tree-sitter Query/Language types
1703
+ compileQuery(Query, language, src, label) {
1704
+ const input = wasmQueryInput(`${this.languageId}:${label}:${src}`);
1705
+ try {
1706
+ return new Query(language, src);
1707
+ } catch (err) {
1708
+ if (this.client.reportWasmAbort(err, input))
1709
+ throw err;
1710
+ logTreeSitterDiagnostic({
1711
+ subsystem: "symbol-extractor",
1712
+ languageId: this.languageId,
1713
+ message: `${this.languageId} ${label} query failed: ${err.message}`,
1714
+ metadata: { query: label }
1715
+ });
1716
+ return null;
1717
+ }
1718
+ }
1719
+ /**
1720
+ * Extract symbols from a parsed tree-sitter tree
1721
+ */
1722
+ extract(tree, filePath, content) {
1723
+ const symbols = [];
1724
+ const refs = [];
1725
+ const relativePath = path4.relative(process.cwd(), filePath);
1726
+ if (this.defQuery) {
1727
+ let lines;
1728
+ const lineAt = (row) => (lines ??= content.split("\n"))[row] ?? "";
1729
+ for (const match of this.queryMatches(this.defQuery, tree.rootNode)) {
1730
+ const symbol = this.parseDefMatch(match, relativePath, lineAt);
1731
+ if (symbol)
1732
+ symbols.push(symbol);
1733
+ }
1734
+ }
1735
+ if (this.refQuery) {
1736
+ for (const match of this.queryMatches(this.refQuery, tree.rootNode)) {
1737
+ const ref = this.parseRefMatch(match, relativePath);
1738
+ if (ref)
1739
+ refs.push(ref);
1740
+ }
1741
+ }
1742
+ return {
1743
+ symbols,
1744
+ refs,
1745
+ imports: this.extractImports(tree),
1746
+ coverage: {
1747
+ definitions: this.defQuery ? "complete" : "unavailable",
1748
+ references: this.refQuery ? "complete" : "unavailable",
1749
+ imports: this.importQuery ? "complete" : "unavailable"
1750
+ }
1751
+ };
1752
+ }
1753
+ /**
1754
+ * The import references alone (optional: only for languages with an
1755
+ * IMPORT_QUERIES entry). #4368 r1 F2: a caller that needs only imports
1756
+ * (the forward-import resolver) runs this instead of {@link extract}, whose
1757
+ * definitions pass is the cost of a large file.
1758
+ */
1759
+ // biome-ignore lint/suspicious/noExplicitAny: Tree type
1760
+ extractImports(tree) {
1761
+ const imports = [];
1762
+ if (!this.importQuery)
1763
+ return imports;
1764
+ for (const match of this.queryMatches(this.importQuery, tree.rootNode)) {
1765
+ imports.push(...parseImportMatch(match));
1766
+ }
1767
+ return imports;
1768
+ }
1769
+ queryMatches(query, rootNode) {
1770
+ try {
1771
+ return query.matches(rootNode);
1772
+ } catch (error) {
1773
+ this.client.reportWasmAbort(error);
1774
+ throw error;
1775
+ }
1776
+ }
1777
+ // biome-ignore lint/suspicious/noExplicitAny: Match type
1778
+ parseDefMatch(match, filePath, lineAt) {
1779
+ const captures = {};
1780
+ for (const capture of match.captures) {
1781
+ captures[capture.name] = {
1782
+ text: capture.node.text,
1783
+ // biome-ignore lint/suspicious/noExplicitAny: Node type
1784
+ node: capture.node
1785
+ };
1786
+ }
1787
+ let name;
1788
+ let kind;
1789
+ let params;
1790
+ let defNode;
1791
+ if (captures.funcName) {
1792
+ name = captures.funcName.text;
1793
+ kind = "function";
1794
+ params = captures.funcParams?.text;
1795
+ defNode = captures.funcDef?.node;
1796
+ } else if (captures.arrowName) {
1797
+ name = captures.arrowName.text;
1798
+ kind = "function";
1799
+ params = captures.arrowParams?.text;
1800
+ defNode = captures.arrowDef?.node;
1801
+ } else if (captures.className) {
1802
+ name = captures.className.text;
1803
+ kind = "class";
1804
+ defNode = captures.classDef?.node;
1805
+ } else if (captures.methodName) {
1806
+ name = captures.methodName.text;
1807
+ kind = "method";
1808
+ params = captures.methodParams?.text;
1809
+ defNode = captures.methodDef?.node;
1810
+ } else if (captures.interfaceName) {
1811
+ name = captures.interfaceName.text;
1812
+ kind = "interface";
1813
+ defNode = captures.interfaceDef?.node;
1814
+ } else if (captures.typeName) {
1815
+ name = captures.typeName.text;
1816
+ kind = "type";
1817
+ defNode = captures.typeDef?.node;
1818
+ } else if (captures.moduleName) {
1819
+ name = captures.moduleName.text;
1820
+ kind = "class";
1821
+ defNode = captures.moduleDef?.node;
1822
+ } else if (captures.defName) {
1823
+ name = captures.defName.text;
1824
+ kind = "type";
1825
+ defNode = captures.defDef?.node;
1826
+ } else if (captures.fieldName) {
1827
+ name = captures.fieldName.text;
1828
+ kind = "property";
1829
+ defNode = captures.fieldDef?.node;
1830
+ } else if (captures.letName) {
1831
+ name = captures.letName.text;
1832
+ kind = "variable";
1833
+ defNode = captures.letDef?.node;
1834
+ }
1835
+ if (!name || !kind || !defNode)
1836
+ return null;
1837
+ const isExported = this.isExported(defNode, lineAt);
1838
+ const signature = params ? this.extractSignature(params, kind) : void 0;
1839
+ const local = this.isFunctionLocal(defNode);
1840
+ const visibility = kind === "method" ? this.detectVisibility(defNode, name) : void 0;
1841
+ const decorators = this.extractDecorators(defNode);
1842
+ const isAsync = (kind === "function" || kind === "method") && this.isAsyncDecl(defNode);
1843
+ const docInfo = this.extractDocCommentInfo(defNode);
1844
+ return {
1845
+ id: `${filePath}:${name}`,
1846
+ name,
1847
+ kind,
1848
+ filePath,
1849
+ line: defNode.startPosition.row + 1,
1850
+ endLine: defNode.endPosition.row + 1,
1851
+ column: defNode.startPosition.column + 1,
1852
+ signature,
1853
+ isExported,
1854
+ ...local ? { local: true } : {},
1855
+ ...visibility ? { visibility } : {},
1856
+ ...decorators.length > 0 ? { decorators } : {},
1857
+ ...isAsync ? { isAsync: true } : {},
1858
+ ...docInfo ? { doc: docInfo.text, docStartLine: docInfo.startLine } : {}
1859
+ };
1860
+ }
1861
+ // Comment node kinds across grammars that use tree-sitter's conventional
1862
+ // name. Line/block comments (`//`, `/* */`, `/** */`, `#`) all parse to a
1863
+ // single "comment" node type in every grammar checked (JS/TS, Python, Go,
1864
+ // Rust, Java, Kotlin, C#, C/C++, Ruby, PHP, Swift, Lua) — no per-grammar
1865
+ // query needed, unlike decorators/annotations which vary by node shape.
1866
+ static COMMENT_NODE_KIND = "comment";
1867
+ /**
1868
+ * First sentence/line of the doc comment immediately preceding a declaration,
1869
+ * whitespace-collapsed and capped ~120 chars — the highest-signal token for an
1870
+ * agent deciding which symbol to read (#512). Structural, not per-language:
1871
+ * walks contiguous preceding-sibling `comment` nodes (same traversal shape as
1872
+ * `extractDecorators`), stopping at the nearest non-comment/non-decorator named
1873
+ * sibling, and takes the LAST contiguous comment block (the one directly above
1874
+ * the declaration — an earlier unrelated comment separated by blank lines still
1875
+ * parses as a preceding sibling, but a real gap makes it not "attached").
1876
+ * JSDoc/TSDoc block comments strip the leading/trailing block markers and
1877
+ * per-line `*` gutters; plain `//`/`#` line comments strip the marker only.
1878
+ * Returns undefined when no comment is directly attached. Returns both the
1879
+ * summarized text (`doc`) and the 1-based start line of the attached comment
1880
+ * block (`docStartLine`, #523) — readSymbol extends its returned range to that
1881
+ * line so an agent reading a symbol sees its contract, not just its body.
1882
+ */
1883
+ // biome-ignore lint/suspicious/noExplicitAny: web-tree-sitter node
1884
+ extractDocCommentInfo(declNode) {
1885
+ const isComment = (n) => !!n && n.type === _TreeSitterSymbolExtractor.COMMENT_NODE_KIND;
1886
+ const isDeco = (n) => !!n && _TreeSitterSymbolExtractor.DECORATOR_NODE_KINDS.has(n.type);
1887
+ const sameStartRow = (a, b) => a?.startPosition?.row === b?.startPosition?.row;
1888
+ let node = declNode;
1889
+ for (let hops = 0; hops < 4; hops++) {
1890
+ const parent = node.parent;
1891
+ if (!parent || !sameStartRow(parent, node))
1892
+ break;
1893
+ node = parent;
1894
+ }
1895
+ let cursor = node.previousSibling;
1896
+ while (cursor && isDeco(cursor))
1897
+ cursor = cursor.previousSibling;
1898
+ let boundaryRow = node.startPosition?.row;
1899
+ const run = [];
1900
+ while (cursor && isComment(cursor)) {
1901
+ const gapRows = boundaryRow !== void 0 && typeof cursor.endPosition?.row === "number" ? boundaryRow - cursor.endPosition.row : 0;
1902
+ if (gapRows > 1)
1903
+ break;
1904
+ run.push(cursor);
1905
+ boundaryRow = cursor.startPosition?.row;
1906
+ cursor = cursor.previousSibling;
1907
+ }
1908
+ if (run.length === 0)
1909
+ return void 0;
1910
+ run.reverse();
1911
+ const raw = run.map((n) => String(n.text ?? "")).join("\n");
1912
+ const text = this.summarizeDocComment(raw);
1913
+ if (!text)
1914
+ return void 0;
1915
+ const startLine = (run[0].startPosition?.row ?? 0) + 1;
1916
+ return { text, startLine };
1917
+ }
1918
+ static DOC_MAX_CHARS = 120;
1919
+ /** Strip comment markers, take the first sentence (or first line if no
1920
+ * sentence terminator), collapse whitespace, cap at ~120 chars. */
1921
+ summarizeDocComment(raw) {
1922
+ const cleaned = raw.replace(/^\s*\/\*\*?/, "").replace(/\*\/\s*$/, "").split(/\r?\n/).map((line) => line.replace(/^\s*(\*|\/\/\/?|#)\s?/, "").trimEnd()).filter((line) => line.trim().length > 0).join(" ").trim();
1923
+ if (!cleaned)
1924
+ return void 0;
1925
+ const sentenceMatch = cleaned.match(/^.*?[.!?](?:\s|$)/);
1926
+ let first = (sentenceMatch ? sentenceMatch[0] : cleaned).trim();
1927
+ first = first.replace(/\s+/g, " ");
1928
+ if (first.length > _TreeSitterSymbolExtractor.DOC_MAX_CHARS) {
1929
+ first = `${first.slice(0, _TreeSitterSymbolExtractor.DOC_MAX_CHARS - 1).trimEnd()}\u2026`;
1930
+ }
1931
+ return first || void 0;
1932
+ }
1933
+ /**
1934
+ * Structural async/suspend detection: an `async` keyword node (Python/JS/TS
1935
+ * have it as a direct child of the declaration) or `async`/`suspend` inside a
1936
+ * `*modifiers*` container (Rust `function_modifiers`, Kotlin/Java/C#
1937
+ * `modifiers`). Conservative — a grammar that spells it differently just
1938
+ * yields false (no false positives).
1939
+ */
1940
+ // biome-ignore lint/suspicious/noExplicitAny: web-tree-sitter node
1941
+ isAsyncDecl(node) {
1942
+ const isAsyncTok = (n) => !!n && (n.type === "async" || n.type === "suspend" || /modifier/.test(String(n.type)) && /^(?:async|suspend)$/.test(String(n.text ?? "").trim()));
1943
+ for (const child of node.children ?? []) {
1944
+ if (isAsyncTok(child))
1945
+ return true;
1946
+ if (/modifiers/.test(String(child?.type))) {
1947
+ for (const gc of child.children ?? []) {
1948
+ if (isAsyncTok(gc))
1949
+ return true;
1950
+ }
1951
+ }
1952
+ }
1953
+ return false;
1954
+ }
1955
+ // Decorator/attribute/annotation node kinds across grammars. Python/TS/JS use
1956
+ // `decorator`; Rust `attribute_item`; Java/Kotlin/C# `marker_annotation` /
1957
+ // `annotation` (often nested inside a `modifiers` container).
1958
+ static DECORATOR_NODE_KINDS = /* @__PURE__ */ new Set([
1959
+ "decorator",
1960
+ "attribute_item",
1961
+ "marker_annotation",
1962
+ "annotation"
1963
+ ]);
1964
+ // Containers that hold annotations as children (Java/Kotlin `modifiers`).
1965
+ static MODIFIERS_CONTAINER_KINDS = /* @__PURE__ */ new Set(["modifiers"]);
1966
+ /**
1967
+ * Decorators/attributes/annotations attached to a declaration node, in source
1968
+ * order. Structural (tree-sitter), not text heuristics, so it handles the three
1969
+ * placement shapes seen across grammars:
1970
+ * - preceding siblings: Python (`decorated_definition` children), Rust
1971
+ * (`attribute_item`), TS methods (`decorator`);
1972
+ * - own children: TS class decorators;
1973
+ * - nested in a `modifiers` container: Java/Kotlin/C# annotations.
1974
+ * Languages without these node kinds simply yield none.
1975
+ */
1976
+ // biome-ignore lint/suspicious/noExplicitAny: web-tree-sitter node
1977
+ extractDecorators(node) {
1978
+ const isDeco = (n) => !!n && _TreeSitterSymbolExtractor.DECORATOR_NODE_KINDS.has(n.type);
1979
+ const text = (n) => {
1980
+ const first = String(n?.text ?? "").split(/\r?\n/, 1)[0]?.trim() ?? "";
1981
+ return first.length > 120 ? `${first.slice(0, 117)}\u2026` : first;
1982
+ };
1983
+ const out = [];
1984
+ const above = [];
1985
+ for (let sib = node.previousSibling; sib; sib = sib.previousSibling) {
1986
+ if (isDeco(sib))
1987
+ above.push(text(sib));
1988
+ else if (sib.isNamed)
1989
+ break;
1990
+ }
1991
+ out.push(...above.reverse());
1992
+ for (const child of node.children ?? []) {
1993
+ if (isDeco(child))
1994
+ out.push(text(child));
1995
+ else if (_TreeSitterSymbolExtractor.MODIFIERS_CONTAINER_KINDS.has(child?.type)) {
1996
+ for (const gc of child.children ?? []) {
1997
+ if (isDeco(gc))
1998
+ out.push(text(gc));
1999
+ }
2000
+ }
2001
+ }
2002
+ return [...new Set(out.filter(Boolean))].slice(0, 8);
2003
+ }
2004
+ // biome-ignore lint/suspicious/noExplicitAny: Match type
2005
+ parseRefMatch(match, filePath) {
2006
+ let name;
2007
+ let refNode;
2008
+ let referenceKind = "unknown";
2009
+ for (const capture of match.captures) {
2010
+ const captureName = String(capture.name);
2011
+ if (captureName.endsWith("Ident") || captureName.endsWith("Method") || captureName.endsWith("Field")) {
2012
+ name = capture.node.text;
2013
+ refNode = capture.node;
2014
+ if (captureName.startsWith("type"))
2015
+ referenceKind = "type";
2016
+ else if (captureName.startsWith("call") || captureName.startsWith("new")) {
2017
+ referenceKind = "call";
2018
+ }
2019
+ }
2020
+ if (captureName.endsWith("Ref") && !refNode) {
2021
+ refNode = capture.node;
2022
+ if (captureName.startsWith("type"))
2023
+ referenceKind = "type";
2024
+ else if (captureName.startsWith("call") || captureName.startsWith("new")) {
2025
+ referenceKind = "call";
2026
+ }
2027
+ }
2028
+ }
2029
+ if (!name || !refNode)
2030
+ return null;
2031
+ return {
2032
+ symbolId: `${filePath}:${name}`,
2033
+ // Will be resolved later
2034
+ symbolName: name,
2035
+ filePath,
2036
+ line: refNode.startPosition.row + 1,
2037
+ column: refNode.startPosition.column + 1,
2038
+ referenceKind
2039
+ };
2040
+ }
2041
+ // biome-ignore lint/suspicious/noExplicitAny: Node type
2042
+ isExported(node, lineAt) {
2043
+ const line = lineAt(node.startPosition.row);
2044
+ return /^\s*export\b/.test(line) || this.hasExportModifier(node);
2045
+ }
2046
+ // biome-ignore lint/suspicious/noExplicitAny: Node type
2047
+ hasExportModifier(node) {
2048
+ let current = node.parent;
2049
+ while (current) {
2050
+ if (current.type === "statement_block")
2051
+ return false;
2052
+ if (current.type === "export_statement" || current.type === "export_declaration") {
2053
+ return true;
2054
+ }
2055
+ current = current.parent;
2056
+ }
2057
+ return false;
2058
+ }
2059
+ // biome-ignore lint/suspicious/noExplicitAny: Node type
2060
+ isFunctionLocal(node) {
2061
+ let current = node.parent;
2062
+ while (current) {
2063
+ if (current.type === "statement_block")
2064
+ return true;
2065
+ current = current.parent;
2066
+ }
2067
+ return false;
2068
+ }
2069
+ // biome-ignore lint/suspicious/noExplicitAny: Node type
2070
+ detectVisibility(node, nameText) {
2071
+ if (nameText.startsWith("#"))
2072
+ return "private";
2073
+ for (const child of node?.children ?? []) {
2074
+ if (child?.type === "accessibility_modifier") {
2075
+ if (child.text === "private")
2076
+ return "private";
2077
+ if (child.text === "protected")
2078
+ return "protected";
2079
+ return void 0;
2080
+ }
2081
+ }
2082
+ return void 0;
2083
+ }
2084
+ extractSignature(paramsText, kind) {
2085
+ if (kind === "function" || kind === "method") {
2086
+ return paramsText.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").replace(/\s+/g, " ").trim();
2087
+ }
2088
+ return void 0;
2089
+ }
2090
+ };
2091
+
2092
+ // dist/clients/blocker-freshness.js
2093
+ var DEPENDENCY_DRIFT_MAX_DELIVERIES = 3;
2094
+ var SELF_DRIFT_HASH_BUDGET_BYTES = 8 * 1024 * 1024;
2095
+ var MAX_IMPLICIT_SCOPE_PARSES = 32;
2096
+ var GLOBAL_SCOPE_ROLE_MEMO_ENTRIES = 1024;
2097
+ var globalScopeRoleMemo = new BoundedLruCache(GLOBAL_SCOPE_ROLE_MEMO_ENTRIES);
2098
+ function roleByTextOf(filePath) {
2099
+ try {
2100
+ return globalScopeRoleByText(fs5.readFileSync(filePath, "utf8"));
2101
+ } catch {
2102
+ return void 0;
2103
+ }
2104
+ }
2105
+ function createImplicitScopeCheck(runtime, cwd) {
2106
+ let receipts;
2107
+ let receiptsDropped = false;
2108
+ const roles = /* @__PURE__ */ new Map();
2109
+ let parses = 0;
2110
+ const roleOf = (other) => {
2111
+ const known = roles.get(other);
2112
+ if (known !== void 0)
2113
+ return known;
2114
+ let stat;
2115
+ try {
2116
+ stat = fs5.statSync(other);
2117
+ } catch {
2118
+ roles.set(other, "none");
2119
+ return "none";
2120
+ }
2121
+ const memo = globalScopeRoleMemo.get(other);
2122
+ let role;
2123
+ if (memo?.mtimeMs === stat.mtimeMs && memo.ctimeMs === stat.ctimeMs && memo.size === stat.size) {
2124
+ role = memo.role;
2125
+ } else {
2126
+ const byName = globalScopeRoleByName(other);
2127
+ const byText = byName ? void 0 : roleByTextOf(other);
2128
+ if (byName ?? byText) {
2129
+ role = byName ?? byText;
2130
+ } else if (parses >= MAX_IMPLICIT_SCOPE_PARSES) {
2131
+ return "unknown";
2132
+ } else {
2133
+ parses += 1;
2134
+ role = classifyJsTsGlobalScope(other);
2135
+ }
2136
+ globalScopeRoleMemo.set(other, {
2137
+ mtimeMs: stat.mtimeMs,
2138
+ ctimeMs: stat.ctimeMs,
2139
+ size: stat.size,
2140
+ role
2141
+ });
2142
+ }
2143
+ roles.set(other, role);
2144
+ return role;
2145
+ };
2146
+ const rootKey = normalizeMapKey(nodePath.resolve(cwd));
2147
+ const rootPrefix = rootKey.endsWith("/") ? rootKey : `${rootKey}/`;
2148
+ const inRoot = (file) => file === rootKey || file.startsWith(rootPrefix);
2149
+ let scanned;
2150
+ const feeding = [];
2151
+ let newestUnknownTs = Number.NEGATIVE_INFINITY;
2152
+ const scanBackTo = (all, recordedAtMs) => {
2153
+ scanned ??= all.length;
2154
+ while (scanned > 0 && all[scanned - 1].ts > recordedAtMs) {
2155
+ const receipt = all[--scanned];
2156
+ if (!inRoot(receipt.filePath))
2157
+ continue;
2158
+ const role = roleOf(receipt.filePath);
2159
+ if (role === "feeds")
2160
+ feeding.push(receipt);
2161
+ else if (role === "unknown") {
2162
+ newestUnknownTs = Math.max(newestUnknownTs, receipt.ts);
2163
+ }
2164
+ }
2165
+ };
2166
+ const feedsGlobalScope = (other) => roles.get(other) === "feeds";
2167
+ return (filePath, recordedAtMs) => {
2168
+ const key = normalizeMapKey(filePath);
2169
+ const goFile = isGoSourceFile(key);
2170
+ const inScope = goFile ? goPackagePeersOf(key) : implicitScopeOf(key, feedsGlobalScope);
2171
+ if (!inScope)
2172
+ return { drifted: [], truncated: false };
2173
+ if (receipts === void 0) {
2174
+ const coordinator = runtime;
2175
+ receipts = coordinator.getMutationsSince?.(0) ?? [];
2176
+ receiptsDropped = (coordinator.droppedMutationReceiptCount ?? 0) > 0;
2177
+ }
2178
+ let drifted;
2179
+ let unknownAfter = false;
2180
+ if (goFile) {
2181
+ for (let i = receipts.length - 1; i >= 0; i--) {
2182
+ const receipt = receipts[i];
2183
+ if (receipt.ts <= recordedAtMs)
2184
+ break;
2185
+ if (inScope(receipt.filePath)) {
2186
+ drifted = receipt.filePath;
2187
+ break;
2188
+ }
2189
+ }
2190
+ } else {
2191
+ scanBackTo(receipts, recordedAtMs);
2192
+ drifted = feeding.find((receipt) => receipt.ts > recordedAtMs && inScope(receipt.filePath))?.filePath;
2193
+ unknownAfter = newestUnknownTs > recordedAtMs;
2194
+ }
2195
+ if (drifted)
2196
+ return { drifted: [drifted], truncated: false };
2197
+ const evictedAfter = receiptsDropped && (receipts.length === 0 || receipts[0].ts > recordedAtMs);
2198
+ return { drifted: [], truncated: unknownAfter || evictedAfter };
2199
+ };
2200
+ }
2201
+ var MAX_DRIFT_CHECK_IMPORTS = 128;
2202
+ var extractorCache = /* @__PURE__ */ new Map();
2203
+ function getExtractor(languageId) {
2204
+ let cached = extractorCache.get(languageId);
2205
+ if (!cached) {
2206
+ cached = (async () => {
2207
+ const client = getSharedTreeSitterClient();
2208
+ if (!client)
2209
+ return null;
2210
+ const extractor = new TreeSitterSymbolExtractor(languageId, client);
2211
+ const ok = await extractor.init();
2212
+ return ok ? extractor : null;
2213
+ })().catch(() => null);
2214
+ extractorCache.set(languageId, cached);
2215
+ }
2216
+ return cached;
2217
+ }
2218
+ var importResolutionMemo = /* @__PURE__ */ new Map();
2219
+ var importResolutionMemoTurnIndex;
2220
+ function noteImportResolutionMemoTurn(turnIndex) {
2221
+ if (turnIndex === void 0)
2222
+ return;
2223
+ if (importResolutionMemoTurnIndex !== turnIndex) {
2224
+ importResolutionMemo.clear();
2225
+ importResolutionMemoTurnIndex = turnIndex;
2226
+ }
2227
+ }
2228
+ async function resolveForwardImportsMemoized(cwd, filePath, resolveForwardImports) {
2229
+ const sig = await statSignature(filePath);
2230
+ if (sig === void 0) {
2231
+ try {
2232
+ return await resolveForwardImports(cwd, filePath);
2233
+ } catch {
2234
+ return [];
2235
+ }
2236
+ }
2237
+ const cached = importResolutionMemo.get(filePath);
2238
+ if (cached && cached.mtimeMs === sig.mtimeMs && cached.size === sig.size)
2239
+ return cached.imports;
2240
+ let imports;
2241
+ try {
2242
+ imports = await resolveForwardImports(cwd, filePath);
2243
+ } catch {
2244
+ imports = [];
2245
+ }
2246
+ importResolutionMemo.set(filePath, {
2247
+ mtimeMs: sig.mtimeMs,
2248
+ size: sig.size,
2249
+ imports
2250
+ });
2251
+ return imports;
2252
+ }
2253
+ var FORWARD_IMPORT_SOURCE_MEMO_ENTRIES = 1024;
2254
+ var JS_TS_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript"]);
2255
+ var RELATIVE_SPECIFIER = /(["'])(\.{1,2}\/(?:(?!\1)[^\n])*)\1/g;
2256
+ var DEPENDENCY_CALL = /\bimport\s*\(|\brequire\s*\(|@import\b|\bdeclare\s+module\s*["']|\/\/\/\s*<reference\s+(?:path|types)/;
2257
+ function holdsUncapturedDependency(content, sources) {
2258
+ if (DEPENDENCY_CALL.test(content))
2259
+ return true;
2260
+ const captured = new Set(sources);
2261
+ for (const match of content.matchAll(RELATIVE_SPECIFIER)) {
2262
+ if (!captured.has(match[2]))
2263
+ return true;
2264
+ }
2265
+ return false;
2266
+ }
2267
+ var forwardImportSourceMemo = new BoundedLruCache(FORWARD_IMPORT_SOURCE_MEMO_ENTRIES);
2268
+ var forwardImportParses = 0;
2269
+ function _getForwardImportParsesForTests() {
2270
+ return forwardImportParses;
2271
+ }
2272
+ async function forwardImportSources(filePath, languageId) {
2273
+ let stat;
2274
+ try {
2275
+ stat = fs5.statSync(filePath);
2276
+ } catch {
2277
+ return void 0;
2278
+ }
2279
+ const key = normalizeMapKey(filePath);
2280
+ const memo = forwardImportSourceMemo.get(key);
2281
+ if (memo?.mtimeMs === stat.mtimeMs && memo.ctimeMs === stat.ctimeMs && memo.size === stat.size) {
2282
+ return memo;
2283
+ }
2284
+ const client = getSharedTreeSitterClient();
2285
+ if (!client)
2286
+ return void 0;
2287
+ const extractor = await getExtractor(languageId);
2288
+ if (!extractor)
2289
+ return void 0;
2290
+ let content;
2291
+ try {
2292
+ content = fs5.readFileSync(filePath, "utf-8");
2293
+ } catch {
2294
+ return void 0;
2295
+ }
2296
+ const outcome = await client.withParsedTree(
2297
+ filePath,
2298
+ languageId,
2299
+ content,
2300
+ // Imports only (#4368 r1 F2): the definitions pass of a full extract is
2301
+ // what made a large file cost seconds.
2302
+ (tree) => extractor.extractImports(tree),
2303
+ "blocker-freshness"
2304
+ );
2305
+ if (!outcome.parsed)
2306
+ return void 0;
2307
+ forwardImportParses++;
2308
+ const sources = [...new Set(outcome.value.map((ref) => ref.source))];
2309
+ const entry = {
2310
+ mtimeMs: stat.mtimeMs,
2311
+ ctimeMs: stat.ctimeMs,
2312
+ size: stat.size,
2313
+ sources,
2314
+ unresolved: memo?.unresolved ?? /* @__PURE__ */ new Set(),
2315
+ uncaptured: JS_TS_LANGUAGES.has(languageId) && holdsUncapturedDependency(content, sources)
2316
+ };
2317
+ forwardImportSourceMemo.set(key, entry);
2318
+ return entry;
2319
+ }
2320
+ async function extractForwardImports(cwd, filePath) {
2321
+ const languageId = resolveTreeSitterLanguage(filePath);
2322
+ if (!languageId)
2323
+ return { targets: [], unresolved: [], complete: false };
2324
+ const entry = await forwardImportSources(filePath, languageId);
2325
+ if (!entry)
2326
+ return { targets: [], unresolved: [], complete: false };
2327
+ const resolved = /* @__PURE__ */ new Set();
2328
+ const unresolved = /* @__PURE__ */ new Set();
2329
+ for (const source of entry.sources) {
2330
+ const targets = resolveImportToFiles(cwd, filePath, languageId, source);
2331
+ if (targets.length === 0)
2332
+ unresolved.add(source);
2333
+ for (const target of targets) {
2334
+ if (entry.unresolved.has(source))
2335
+ notePathAppeared(target);
2336
+ resolved.add(target);
2337
+ }
2338
+ }
2339
+ entry.unresolved = unresolved;
2340
+ return {
2341
+ targets: [...resolved],
2342
+ unresolved: [...unresolved],
2343
+ complete: !entry.uncaptured
2344
+ };
2345
+ }
2346
+ function extractForwardImportPaths(cwd, filePath) {
2347
+ return extractForwardImports(cwd, filePath).then(({ targets }) => targets);
2348
+ }
2349
+ async function statSignature(filePath) {
2350
+ try {
2351
+ const stat = await fs5.promises.stat(filePath);
2352
+ return { mtimeMs: stat.mtimeMs, size: stat.size };
2353
+ } catch {
2354
+ return void 0;
2355
+ }
2356
+ }
2357
+ async function statMtimeMs(filePath) {
2358
+ try {
2359
+ const stat = await fs5.promises.stat(filePath);
2360
+ return stat.mtimeMs;
2361
+ } catch {
2362
+ return void 0;
2363
+ }
2364
+ }
2365
+ async function detectSelfDrift(args) {
2366
+ const boundOptions = (label) => ({
2367
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
2368
+ signal: args.signal,
2369
+ hook: "turn_end",
2370
+ label
2371
+ });
2372
+ const stat = await bounded(fs5.promises.stat(args.filePath), boundOptions("selfDriftStat"));
2373
+ if (stat === void 0)
2374
+ return { verdict: "unverifiable", unverifiableReason: "stat-unavailable" };
2375
+ if (args.recordedSize === void 0)
2376
+ return { verdict: "unverifiable", unverifiableReason: "missing-baseline" };
2377
+ if (stat.size !== args.recordedSize)
2378
+ return { verdict: "drift" };
2379
+ if (args.recordedHash === void 0)
2380
+ return { verdict: "unverifiable", unverifiableReason: "missing-baseline" };
2381
+ if (stat.size > args.budget.bytesLeft) {
2382
+ args.budget.exhausted = true;
2383
+ return {
2384
+ verdict: "unverifiable",
2385
+ unverifiableReason: "hash-budget-exhausted"
2386
+ };
2387
+ }
2388
+ const content = await bounded(fs5.promises.readFile(args.filePath), boundOptions("selfDriftHash"));
2389
+ if (content === void 0)
2390
+ return { verdict: "unverifiable", unverifiableReason: "hash-unavailable" };
2391
+ args.budget.bytesLeft -= content.byteLength;
2392
+ const hash = hashContentBytes(content);
2393
+ return { verdict: hash === args.recordedHash ? "unchanged" : "drift" };
2394
+ }
2395
+ async function collectForwardImportMtimes(cwd, filePath, resolveForwardImports = extractForwardImportPaths, turnIndex) {
2396
+ noteImportResolutionMemoTurn(turnIndex);
2397
+ let imports = [];
2398
+ try {
2399
+ imports = await resolveForwardImportsMemoized(cwd, filePath, resolveForwardImports);
2400
+ } catch {
2401
+ imports = [];
2402
+ }
2403
+ const truncated = imports.length > MAX_DRIFT_CHECK_IMPORTS;
2404
+ const out = [];
2405
+ for (const dep of imports.slice(0, MAX_DRIFT_CHECK_IMPORTS)) {
2406
+ const mtimeMs = await statMtimeMs(dep);
2407
+ if (mtimeMs !== void 0)
2408
+ out.push({ path: dep, mtimeMs });
2409
+ }
2410
+ return { mtimes: out, truncated };
2411
+ }
2412
+ async function detectDrift(cwd, filePath, recordedAtMs, resolveForwardImports, turnIndex, implicitScope, skipOwnFile = false) {
2413
+ const drifted = [];
2414
+ if (!skipOwnFile) {
2415
+ const ownFreshness = freshnessFromMtime({
2416
+ mtimeMs: await statMtimeMs(filePath),
2417
+ referenceMs: recordedAtMs
2418
+ });
2419
+ if (ownFreshness.verdict === "stale")
2420
+ drifted.push(filePath);
2421
+ }
2422
+ const { mtimes, truncated } = await collectForwardImportMtimes(cwd, filePath, resolveForwardImports, turnIndex);
2423
+ for (const { path: depPath, mtimeMs } of mtimes) {
2424
+ const verdict = freshnessFromMtime({
2425
+ mtimeMs,
2426
+ referenceMs: recordedAtMs
2427
+ });
2428
+ if (verdict.verdict === "stale")
2429
+ drifted.push(depPath);
2430
+ }
2431
+ const implicit = implicitScope(filePath, recordedAtMs);
2432
+ drifted.push(...implicit.drifted);
2433
+ return { drifted, truncated: truncated || implicit.truncated };
2434
+ }
2435
+ function isEligibleForDriftCheck(entry) {
2436
+ if (entry.stale)
2437
+ return false;
2438
+ if (entry.recordedAtMs === void 0)
2439
+ return false;
2440
+ return entry.sources !== void 0 && entry.sources.length > 0;
2441
+ }
2442
+ function isAllLspSourced(sources) {
2443
+ return sources !== void 0 && sources.length > 0 && sources.every((source) => source === "lsp");
2444
+ }
2445
+ function canSubsumeLspWidgetRow(entry) {
2446
+ return isEligibleForDriftCheck(entry) && isAllLspSourced(entry.sources);
2447
+ }
2448
+ async function sweepInlineBlockerFreshness(runtime, cwd, options) {
2449
+ const counts = {
2450
+ total: 0,
2451
+ kept: 0,
2452
+ revalidated: 0,
2453
+ alreadyStale: 0,
2454
+ truncatedImports: 0,
2455
+ selfHealed: 0,
2456
+ selfUnverifiable: 0,
2457
+ hashBudgetExhausted: 0
2458
+ };
2459
+ const resolveForwardImports = options?.resolveForwardImports ?? extractForwardImportPaths;
2460
+ const implicitScope = createImplicitScopeCheck(runtime, cwd);
2461
+ let turnIndex;
2462
+ try {
2463
+ turnIndex = runtime.turnIndex;
2464
+ } catch {
2465
+ turnIndex = void 0;
2466
+ }
2467
+ let inlineEntries;
2468
+ try {
2469
+ inlineEntries = runtime.getInlineBlockersSnapshot();
2470
+ } catch {
2471
+ return counts;
2472
+ }
2473
+ const population = inlineEntries.map((entry) => ({
2474
+ filePath: entry.filePath,
2475
+ stale: entry.stale ?? false,
2476
+ recordedAtMs: entry.recordedAtMs,
2477
+ sources: entry.sources,
2478
+ staleReason: entry.staleReason,
2479
+ recordedSize: entry.recordedSize,
2480
+ recordedHash: entry.recordedHash,
2481
+ demote: () => runtime.markInlineBlockerStale(entry.filePath, "dependency-drift"),
2482
+ setSelfDrift: (isSelfDrift) => runtime.setInlineBlockerSelfDriftStale(entry.filePath, isSelfDrift)
2483
+ }));
2484
+ const inlineByKey = /* @__PURE__ */ new Map();
2485
+ for (const entry of population) {
2486
+ inlineByKey.set(normalizeEphemeralMapKey(entry.filePath), entry);
2487
+ }
2488
+ for (const extra of options?.additionalEntries ?? []) {
2489
+ const key = normalizeEphemeralMapKey(extra.filePath);
2490
+ const inlineEntry = inlineByKey.get(key);
2491
+ if (inlineEntry && canSubsumeLspWidgetRow(inlineEntry)) {
2492
+ const demoteInline = inlineEntry.demote;
2493
+ const demoteWidget = extra.demote;
2494
+ inlineEntry.demote = () => {
2495
+ const inlineChanged = demoteInline();
2496
+ const widgetChanged = demoteWidget();
2497
+ return inlineChanged || widgetChanged;
2498
+ };
2499
+ continue;
2500
+ }
2501
+ population.push({
2502
+ filePath: extra.filePath,
2503
+ stale: false,
2504
+ recordedAtMs: extra.recordedAtMs,
2505
+ sources: ["lsp"],
2506
+ demote: extra.demote,
2507
+ // A widget row is pure-LSP by construction, so it takes the import
2508
+ // axis and never reaches the self axis. No `setSelfDrift` (#2982):
2509
+ // the widget store has no re-arming self-drift setter, and an entry
2510
+ // without one is not eligible for that axis.
2511
+ staleReason: void 0,
2512
+ recordedSize: void 0,
2513
+ recordedHash: void 0
2514
+ });
2515
+ }
2516
+ counts.total = population.length;
2517
+ const hashBudget = {
2518
+ bytesLeft: SELF_DRIFT_HASH_BUDGET_BYTES,
2519
+ exhausted: false
2520
+ };
2521
+ for (const entry of population) {
2522
+ try {
2523
+ const selfDriftDemoted = entry.stale && entry.staleReason === "self-drift";
2524
+ if (entry.stale && !selfDriftDemoted) {
2525
+ counts.alreadyStale += 1;
2526
+ continue;
2527
+ }
2528
+ if (entry.recordedAtMs === void 0) {
2529
+ counts.kept += 1;
2530
+ continue;
2531
+ }
2532
+ const recordedSources = entry.sources !== void 0 && entry.sources.length > 0 ? entry.sources : void 0;
2533
+ if (recordedSources === void 0) {
2534
+ counts.kept += 1;
2535
+ continue;
2536
+ }
2537
+ const setSelfDrift = entry.setSelfDrift;
2538
+ const isLspSourced = isAllLspSourced(recordedSources);
2539
+ let ownVerdict;
2540
+ if (setSelfDrift) {
2541
+ const hashBudgetWasExhausted = hashBudget.exhausted;
2542
+ const selfDrift = await bounded(detectSelfDrift({
2543
+ filePath: entry.filePath,
2544
+ recordedSize: entry.recordedSize,
2545
+ recordedHash: entry.recordedHash,
2546
+ signal: options?.signal,
2547
+ budget: hashBudget
2548
+ }), {
2549
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
2550
+ signal: options?.signal,
2551
+ hook: "turn_end",
2552
+ label: "detectSelfDrift"
2553
+ }) ?? {
2554
+ verdict: "unverifiable",
2555
+ unverifiableReason: "bound-expired"
2556
+ };
2557
+ ownVerdict = selfDrift.verdict;
2558
+ if (ownVerdict === "unverifiable") {
2559
+ counts.selfUnverifiable += 1;
2560
+ const reason = selfDrift.unverifiableReason ?? (hashBudgetWasExhausted || hashBudget.exhausted ? "hash-budget-exhausted" : "hash-unavailable");
2561
+ recordDegradationOnce({
2562
+ kind: "self-drift-unverifiable",
2563
+ subject: `inline-blocker:${toRunnerDisplayPath(cwd, entry.filePath)}#${reason}`,
2564
+ reason: `the self-drift check was unverifiable: ${reason}`
2565
+ });
2566
+ if (reason === "hash-budget-exhausted") {
2567
+ recordDegradationOnce({
2568
+ kind: "self-drift-hash-budget-exhausted",
2569
+ subject: `inline-blocker:${toRunnerDisplayPath(cwd, entry.filePath)}`,
2570
+ reason: "the aggregate self-drift hash budget was exhausted"
2571
+ });
2572
+ }
2573
+ }
2574
+ }
2575
+ if (!isLspSourced) {
2576
+ if (!setSelfDrift || ownVerdict === void 0) {
2577
+ counts.kept += 1;
2578
+ continue;
2579
+ }
2580
+ if (ownVerdict === "unverifiable") {
2581
+ if (selfDriftDemoted)
2582
+ counts.alreadyStale += 1;
2583
+ else
2584
+ counts.kept += 1;
2585
+ continue;
2586
+ }
2587
+ const shouldDemote = ownVerdict === "drift";
2588
+ const transitioned = setSelfDrift(shouldDemote);
2589
+ if (shouldDemote) {
2590
+ if (transitioned)
2591
+ counts.revalidated += 1;
2592
+ else
2593
+ counts.alreadyStale += 1;
2594
+ } else if (transitioned) {
2595
+ counts.selfHealed += 1;
2596
+ } else {
2597
+ counts.kept += 1;
2598
+ }
2599
+ continue;
2600
+ }
2601
+ let contentDecidedOwnFile = false;
2602
+ if (setSelfDrift !== void 0 && ownVerdict !== void 0 && entry.recordedSize !== void 0) {
2603
+ if (ownVerdict === "drift") {
2604
+ const transitioned = setSelfDrift(true);
2605
+ if (transitioned)
2606
+ counts.revalidated += 1;
2607
+ else
2608
+ counts.alreadyStale += 1;
2609
+ continue;
2610
+ }
2611
+ if (ownVerdict === "unchanged") {
2612
+ contentDecidedOwnFile = true;
2613
+ const transitioned = setSelfDrift(false);
2614
+ if (transitioned)
2615
+ counts.selfHealed += 1;
2616
+ } else {
2617
+ if (selfDriftDemoted) {
2618
+ counts.alreadyStale += 1;
2619
+ continue;
2620
+ }
2621
+ }
2622
+ }
2623
+ const skipOwnFile = contentDecidedOwnFile;
2624
+ const { drifted, truncated } = await detectDrift(cwd, entry.filePath, entry.recordedAtMs, resolveForwardImports, turnIndex, implicitScope, skipOwnFile);
2625
+ if (truncated)
2626
+ counts.truncatedImports += 1;
2627
+ if (drifted.length > 0) {
2628
+ entry.demote();
2629
+ counts.revalidated += 1;
2630
+ } else {
2631
+ counts.kept += 1;
2632
+ }
2633
+ } catch {
2634
+ counts.kept += 1;
2635
+ }
2636
+ }
2637
+ counts.hashBudgetExhausted = hashBudget.exhausted ? 1 : 0;
2638
+ return counts;
2639
+ }
2640
+
2641
+ export {
2642
+ getRunnerTimeoutFloorMs,
2643
+ RUNTIME_CONFIG,
2644
+ exceedsLspSyncLimits,
2645
+ loadAstGrepNapi,
2646
+ isGoSourceFile,
2647
+ goPackageFiles,
2648
+ implicitScopeOf,
2649
+ goPackageChangedAt,
2650
+ feedsJsTsGlobalScope,
2651
+ pathBirthtimeMs,
2652
+ jsTsCandidatePaths,
2653
+ resolveAliasedImport,
2654
+ resolveProjectReferenceImport,
2655
+ resolveWorkspacePackageImport,
2656
+ importSourceNameParts,
2657
+ fileAnswerNames,
2658
+ resolveImportToFiles,
2659
+ symbolExtractionGrammar,
2660
+ TreeSitterSymbolExtractor,
2661
+ DEPENDENCY_DRIFT_MAX_DELIVERIES,
2662
+ _getForwardImportParsesForTests,
2663
+ extractForwardImports,
2664
+ extractForwardImportPaths,
2665
+ collectForwardImportMtimes,
2666
+ sweepInlineBlockerFreshness
2667
+ };