farming-code 2.2.29 → 2.2.32

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 (385) hide show
  1. package/README.md +25 -66
  2. package/README.zh_cn.md +15 -66
  3. package/THIRD_PARTY_NOTICES.md +7 -0
  4. package/backend/acp/{client-services.js → client-services.cjs} +132 -134
  5. package/backend/acp/packaged-claude-acp.cjs +37 -0
  6. package/backend/acp/packaged-codex-acp.cjs +37 -0
  7. package/backend/acp/patch-decisions.cjs +182 -0
  8. package/backend/acp/permission-security.cjs +131 -0
  9. package/backend/{acp-checkpoint-store.js → acp-checkpoint-store.cjs} +90 -59
  10. package/backend/{acp-runtime.js → acp-runtime.cjs} +1219 -1125
  11. package/backend/acp-session-state.cjs +622 -0
  12. package/backend/acp-transcript.cjs +525 -0
  13. package/backend/agent-env.cjs +257 -0
  14. package/backend/agent-extension-discovery.cjs +452 -0
  15. package/backend/agent-extension-inventory.cjs +116 -0
  16. package/backend/agent-json-stream.cjs +283 -0
  17. package/backend/agent-lifecycle-journal.cjs +185 -0
  18. package/backend/{agent-manager.js → agent-manager.cjs} +3116 -4275
  19. package/backend/agent-order-transaction.cjs +85 -0
  20. package/backend/agent-order.cjs +159 -0
  21. package/backend/agent-provider-session.cjs +98 -0
  22. package/backend/agent-runtime-binding.cjs +179 -0
  23. package/backend/agent-session-history.cjs +1022 -0
  24. package/backend/agent-session-inventory.cjs +291 -0
  25. package/backend/async-cache.cjs +104 -0
  26. package/backend/atomic-json-store.cjs +67 -0
  27. package/backend/auth.cjs +261 -0
  28. package/backend/authoritative-inventory-cache.cjs +383 -0
  29. package/backend/business-health.cjs +68 -0
  30. package/backend/chat-runtime.cjs +50 -0
  31. package/backend/claude-settings.cjs +132 -0
  32. package/backend/cli-agents.cjs +435 -0
  33. package/backend/codex-context-window.cjs +196 -0
  34. package/backend/codex-models.cjs +228 -0
  35. package/backend/codex-session-archive.cjs +82 -0
  36. package/backend/codex-session-history.cjs +590 -0
  37. package/backend/{codex-terminal-profile.js → codex-terminal-profile.cjs} +149 -155
  38. package/backend/codex-transcript-sanitizer.cjs +130 -0
  39. package/backend/codex-transcript.cjs +2107 -0
  40. package/backend/command-runner-child.cjs +105 -0
  41. package/backend/config-manager.cjs +1263 -0
  42. package/backend/control-api.cjs +439 -0
  43. package/backend/data/LICENSE.cua +21 -0
  44. package/backend/executable-discovery.cjs +204 -0
  45. package/backend/farming-agent-bootstrap.cjs +81 -0
  46. package/backend/farming-agent-bootstrap.zh_cn.md +6 -2
  47. package/backend/{farming-app-cli.js → farming-app-cli.cjs} +441 -493
  48. package/backend/farming-cli.cjs +414 -0
  49. package/backend/farming-net-pass.cjs +319 -0
  50. package/backend/farming-net-registry.cjs +123 -0
  51. package/backend/farming-net-server.cjs +309 -0
  52. package/backend/farming-session-store.cjs +651 -0
  53. package/backend/git-worktree-info.cjs +181 -0
  54. package/backend/haiku-token.cjs +828 -0
  55. package/backend/index-html.cjs +96 -0
  56. package/backend/input-parts.cjs +68 -0
  57. package/backend/input-routing.cjs +40 -0
  58. package/backend/{local-session-engine.js → local-session-engine.cjs} +276 -282
  59. package/backend/main-agent-skills.cjs +309 -0
  60. package/backend/main-page-session.cjs +132 -0
  61. package/backend/native-pty-controller-generation.cjs +139 -0
  62. package/backend/{native-pty-host-client.js → native-pty-host-client.cjs} +338 -346
  63. package/backend/native-pty-host-identity.cjs +125 -0
  64. package/backend/native-pty-host-path.cjs +68 -0
  65. package/backend/{native-pty-host.js → native-pty-host.cjs} +480 -569
  66. package/backend/native-session-engine.cjs +296 -0
  67. package/backend/network.cjs +57 -0
  68. package/backend/npm-update-helper.cjs +399 -0
  69. package/backend/packaged-node-pty.cjs +120 -0
  70. package/backend/preview-session-manager.cjs +89 -0
  71. package/backend/provider-adapters.cjs +492 -0
  72. package/backend/provider-home-configuration.cjs +211 -0
  73. package/backend/provider-session-id.cjs +79 -0
  74. package/backend/provider-session-service.cjs +333 -0
  75. package/backend/qr-share-tickets.cjs +119 -0
  76. package/backend/resource-broadcast-protocol.cjs +50 -0
  77. package/backend/review-diff-router.cjs +202 -0
  78. package/backend/review-diff-service.cjs +1267 -0
  79. package/backend/review-session-router.cjs +93 -0
  80. package/backend/review-session-service.cjs +470 -0
  81. package/backend/review-session-store.cjs +161 -0
  82. package/backend/review-state-router.cjs +132 -0
  83. package/backend/review-state-store.cjs +299 -0
  84. package/backend/run-history-store.cjs +97 -0
  85. package/backend/runtime-dependency-manager.cjs +893 -0
  86. package/backend/runtime-executable-invocation.cjs +37 -0
  87. package/backend/runtime-observation.cjs +96 -0
  88. package/backend/server-process-identity.cjs +70 -0
  89. package/backend/server.cjs +3356 -0
  90. package/backend/session-engine-bridge.cjs +144 -0
  91. package/backend/session-engine-router.cjs +105 -0
  92. package/backend/session-engine.cjs +64 -0
  93. package/backend/session-stream-protocol.cjs +181 -0
  94. package/backend/shell-busy-integration.cjs +442 -0
  95. package/backend/slash-command-discovery.cjs +423 -0
  96. package/backend/storage-layout.cjs +263 -0
  97. package/backend/system-monitor.cjs +96 -0
  98. package/backend/terminal-attach-checkpoint.cjs +73 -0
  99. package/backend/terminal-exit-quiescence.cjs +57 -0
  100. package/backend/{terminal-reducer-flow-control.js → terminal-reducer-flow-control.cjs} +45 -22
  101. package/backend/terminal-runtime-cleanup.cjs +99 -0
  102. package/backend/{terminal-screen-state.js → terminal-screen-state.cjs} +100 -108
  103. package/backend/{terminal-screen-worker-pool.js → terminal-screen-worker-pool.cjs} +89 -79
  104. package/backend/{terminal-screen-worker-thread.js → terminal-screen-worker-thread.cjs} +64 -65
  105. package/backend/terminal-screen-worker.cjs +352 -0
  106. package/backend/terminal-state-serialization.cjs +147 -0
  107. package/backend/{terminal-status.js → terminal-status.cjs} +137 -187
  108. package/backend/theme-manager.cjs +185 -0
  109. package/backend/update-service.cjs +675 -0
  110. package/backend/usage-forecast.cjs +95 -0
  111. package/backend/{usage-history-client.js → usage-history-client.cjs} +144 -94
  112. package/backend/usage-history-smoke.cjs +85 -0
  113. package/backend/usage-history-worker.cjs +60 -0
  114. package/backend/{usage-monitor.js → usage-monitor.cjs} +363 -390
  115. package/backend/workspace-directory.cjs +239 -0
  116. package/backend/workspace-discovery.cjs +271 -0
  117. package/backend/{workspace-file-router.js → workspace-file-router.cjs} +213 -282
  118. package/backend/{workspace-file-service.js → workspace-file-service.cjs} +1139 -1318
  119. package/backend/workspace-root-registry.cjs +192 -0
  120. package/bin/farming +1 -1
  121. package/config/farming.deploy.env.example +1 -1
  122. package/config/farming.install.env.example +1 -1
  123. package/dist/acp/codex-acp-1.1.4.mjs +405 -8
  124. package/dist/assets/App-BCkdpWB6.css +1 -0
  125. package/dist/assets/App-CRjKpaGp.js +697 -0
  126. package/dist/assets/CodeSelect-Ci8sKZPU.js +1 -0
  127. package/dist/assets/CodeSelect-T2eNzrOc.css +1 -0
  128. package/dist/assets/{FileEditorMarkdownPreview-Cs9cLv8a.js → FileEditorMarkdownPreview-V06QHlAg.js} +55 -55
  129. package/dist/assets/FileEditorPane-BX-Wjg9U.js +2 -0
  130. package/dist/assets/IconGlyphs-YhV7tqd4.js +1 -0
  131. package/dist/assets/ProjectFilesSection-BDT2_r0o.js +12 -0
  132. package/dist/assets/ReviewPage-BqAs5sG1.js +3 -0
  133. package/dist/assets/code-dark-B7eM_9G9.css +1 -0
  134. package/dist/assets/{cssMode-Bdg5RpcJ.js → cssMode-ADdAlxTO.js} +1 -1
  135. package/dist/assets/{editor.api2-D74ufkbr.js → editor.api2-BFLAr0YF.js} +162 -162
  136. package/dist/assets/file-icons-BpeqWkIB.js +1 -0
  137. package/dist/assets/{freemarker2-DfMDJfnE.js → freemarker2-C90q5jrp.js} +1 -1
  138. package/dist/assets/{handlebars-BEdPtzMT.js → handlebars-BwUQcEDq.js} +1 -1
  139. package/dist/assets/{html-B1fPb1QD.js → html-1s3uPKfh.js} +1 -1
  140. package/dist/assets/{htmlMode-CY2ZjHxH.js → htmlMode-xtKD99de.js} +1 -1
  141. package/dist/assets/{index-CQNaZc3l.js → index-C8MchCem.js} +3 -3
  142. package/dist/assets/{javascript-BV2OsWA0.js → javascript-Ph7ijyny.js} +1 -1
  143. package/dist/assets/{jsonMode-zd07aeuX.js → jsonMode-D-jGAnFH.js} +1 -1
  144. package/dist/assets/linkComputer-CbGyI_qx.js +1 -0
  145. package/dist/assets/{liquid-BParI24X.js → liquid-0IamE_cI.js} +1 -1
  146. package/dist/assets/{lspLanguageFeatures-BzredkoV.js → lspLanguageFeatures-BxnQunWF.js} +1 -1
  147. package/dist/assets/main-CLKXa6hf.css +1 -0
  148. package/dist/assets/{mdx-vGzUJN98.js → mdx-DdNmfivn.js} +1 -1
  149. package/dist/assets/{monaco.contribution-BmaeBQEc.js → monaco.contribution-B43jsUs1.js} +2 -2
  150. package/dist/assets/{python-CUwdLgsI.js → python-IAt4X5TS.js} +1 -1
  151. package/dist/assets/{razor-BScebUxU.js → razor-DRkFYW0S.js} +1 -1
  152. package/dist/assets/review-C8-oPKXk.css +1 -0
  153. package/dist/assets/{tsMode-DzUAibXK.js → tsMode-_d_4U2gf.js} +1 -1
  154. package/dist/assets/{typescript-D9EOeM7N.js → typescript-C7zcMq9l.js} +1 -1
  155. package/dist/assets/{workers-DAiomYZM.js → workers-D0nOyJzf.js} +1 -1
  156. package/dist/assets/workspace-editor-model-DND_Y-Bj.js +1 -0
  157. package/dist/assets/workspace-editor-monaco-BPWhgcIB.js +4 -0
  158. package/dist/assets/workspace-editor-monaco-N641qPjX.js +1 -0
  159. package/dist/assets/workspace-view-state-k1bTlJ_Z.js +13 -0
  160. package/dist/assets/{xml-wWl1dhkQ.js → xml-BR-HvxT8.js} +1 -1
  161. package/dist/assets/{yaml-Dc2WDaOZ.js → yaml-YtFQwh4q.js} +1 -1
  162. package/dist/index.html +2 -1
  163. package/extensions/browser/backend/agent-browser-runtime.cjs +1040 -0
  164. package/extensions/browser/backend/agent-capability.cjs +85 -0
  165. package/extensions/browser/backend/browser-mcp-server.cjs +655 -0
  166. package/extensions/browser/backend/browser-resource-manager.cjs +1486 -0
  167. package/extensions/browser/backend/browser-resource-store.cjs +228 -0
  168. package/extensions/browser/backend/browser-router.cjs +203 -0
  169. package/extensions/browser/backend/executable-discovery.cjs +248 -0
  170. package/extensions/browser/backend/farming-browser-client.cjs +116 -0
  171. package/extensions/browser/backend/index.cjs +32 -0
  172. package/extensions/browser/backend/managed-chromium-installer.cjs +833 -0
  173. package/extensions/browser/bin/farming-browser +16 -7
  174. package/extensions/computer/backend/agent-capability.cjs +72 -0
  175. package/extensions/computer/backend/computer-constants.cjs +59 -0
  176. package/extensions/computer/backend/computer-mcp-server.cjs +182 -0
  177. package/extensions/computer/backend/computer-resource-manager.cjs +1219 -0
  178. package/extensions/computer/backend/computer-resource-store.cjs +190 -0
  179. package/extensions/computer/backend/computer-router.cjs +257 -0
  180. package/extensions/computer/backend/cua-tools.json +3067 -0
  181. package/extensions/computer/backend/farming-computer-client.cjs +84 -0
  182. package/extensions/computer/backend/index.cjs +35 -0
  183. package/extensions/computer/backend/isolated-browser-provider.cjs +157 -0
  184. package/extensions/computer/bin/farming-computer +131 -0
  185. package/frontend/farming-net/app.js +255 -262
  186. package/frontend/ghostty-loader.js +24 -20
  187. package/frontend/reading-anchor.js +210 -193
  188. package/frontend/runtime-paths.js +24 -26
  189. package/frontend/session-bridge.js +90 -111
  190. package/frontend/session-modal-bridge.js +184 -302
  191. package/frontend/skin-bridge.js +20 -23
  192. package/frontend/skins/crt/app.js +7686 -7901
  193. package/frontend/skins/crt/effects.js +7 -6
  194. package/frontend/skins/crt/index.html +6 -6
  195. package/frontend/skins/crt/styles/markdown.css +4 -4
  196. package/frontend/terminal-bridge.js +174 -181
  197. package/frontend/terminal-replay.js +320 -345
  198. package/frontend/theme-loader.js +55 -71
  199. package/index.html +1 -0
  200. package/package.json +64 -37
  201. package/shared/backend-connection-status.js +38 -69
  202. package/shared/browser-protocol.js +235 -110
  203. package/shared/websocket-liveness.js +40 -0
  204. package/backend/acp/packaged-claude-acp.js +0 -13
  205. package/backend/acp/packaged-codex-acp.js +0 -13
  206. package/backend/acp/patch-decisions.js +0 -153
  207. package/backend/acp/permission-security.js +0 -108
  208. package/backend/acp-session-state.js +0 -644
  209. package/backend/acp-transcript.js +0 -512
  210. package/backend/agent-env.js +0 -250
  211. package/backend/agent-json-stream.js +0 -268
  212. package/backend/agent-lifecycle-journal.js +0 -133
  213. package/backend/agent-memory-report.js +0 -583
  214. package/backend/agent-order.js +0 -166
  215. package/backend/agent-provider-session.js +0 -76
  216. package/backend/agent-runtime-binding.js +0 -174
  217. package/backend/agent-session-history.js +0 -953
  218. package/backend/async-cache.js +0 -94
  219. package/backend/atomic-json-store.js +0 -37
  220. package/backend/auth.js +0 -251
  221. package/backend/bundle-update-helper.js +0 -113
  222. package/backend/chat-runtime.js +0 -28
  223. package/backend/claude-settings.js +0 -121
  224. package/backend/cli-agents.js +0 -486
  225. package/backend/codex-context-window.js +0 -189
  226. package/backend/codex-models.js +0 -226
  227. package/backend/codex-rollout-follower.js +0 -326
  228. package/backend/codex-session-archive.js +0 -65
  229. package/backend/codex-session-history.js +0 -647
  230. package/backend/codex-transcript-sanitizer.js +0 -115
  231. package/backend/codex-transcript.js +0 -2564
  232. package/backend/command-runner-child.js +0 -67
  233. package/backend/config-manager.js +0 -1133
  234. package/backend/control-api.js +0 -455
  235. package/backend/executable-discovery.js +0 -216
  236. package/backend/farming-agent-bootstrap.js +0 -49
  237. package/backend/farming-cli.js +0 -457
  238. package/backend/farming-net-pass.js +0 -285
  239. package/backend/farming-net-registry.js +0 -112
  240. package/backend/farming-net-server.js +0 -298
  241. package/backend/farming-session-store.js +0 -677
  242. package/backend/git-worktree-info.js +0 -181
  243. package/backend/haiku-token.js +0 -772
  244. package/backend/index-html.js +0 -62
  245. package/backend/input-parts.js +0 -54
  246. package/backend/input-routing.js +0 -24
  247. package/backend/json-cli-runtime.js +0 -275
  248. package/backend/main-agent-skills.js +0 -305
  249. package/backend/main-page-session.js +0 -122
  250. package/backend/native-pty-controller-generation.js +0 -106
  251. package/backend/native-pty-host-identity.js +0 -86
  252. package/backend/native-pty-host-path.js +0 -42
  253. package/backend/native-session-engine.js +0 -268
  254. package/backend/network.js +0 -23
  255. package/backend/npm-update-helper.js +0 -374
  256. package/backend/packaged-node-pty.js +0 -99
  257. package/backend/preview-session-manager.js +0 -68
  258. package/backend/provider-adapters.js +0 -399
  259. package/backend/provider-session-id.js +0 -47
  260. package/backend/provider-session-service.js +0 -345
  261. package/backend/qr-share-tickets.js +0 -100
  262. package/backend/review-diff-router.js +0 -180
  263. package/backend/review-diff-service.js +0 -1270
  264. package/backend/review-session-router.js +0 -74
  265. package/backend/review-session-service.js +0 -446
  266. package/backend/review-session-store.js +0 -151
  267. package/backend/review-state-router.js +0 -116
  268. package/backend/review-state-store.js +0 -286
  269. package/backend/run-history-store.js +0 -67
  270. package/backend/runtime-dependency-manager.js +0 -759
  271. package/backend/runtime-executable-invocation.js +0 -20
  272. package/backend/runtime-observation.js +0 -81
  273. package/backend/server-process-identity.js +0 -47
  274. package/backend/server.js +0 -3267
  275. package/backend/session-engine-bridge.js +0 -129
  276. package/backend/session-engine-router.js +0 -66
  277. package/backend/session-engine.js +0 -39
  278. package/backend/session-stream-protocol.js +0 -185
  279. package/backend/shell-busy-integration.js +0 -472
  280. package/backend/slash-command-discovery.js +0 -433
  281. package/backend/storage-layout.js +0 -190
  282. package/backend/system-monitor.js +0 -77
  283. package/backend/terminal-attach-checkpoint.js +0 -74
  284. package/backend/terminal-exit-quiescence.js +0 -39
  285. package/backend/terminal-runtime-cleanup.js +0 -80
  286. package/backend/terminal-screen-worker.js +0 -337
  287. package/backend/terminal-state-serialization.js +0 -127
  288. package/backend/theme-manager.js +0 -163
  289. package/backend/update-service.js +0 -1545
  290. package/backend/usage-forecast.js +0 -86
  291. package/backend/usage-history-scanner.ts +0 -2718
  292. package/backend/usage-history-smoke.js +0 -64
  293. package/backend/usage-history-worker.js +0 -34
  294. package/backend/workspace-directory.js +0 -232
  295. package/backend/workspace-discovery.js +0 -311
  296. package/backend/workspace-root-registry.js +0 -166
  297. package/dist/assets/App-BKNUILuL.css +0 -1
  298. package/dist/assets/App-Ex2oVVXO.js +0 -693
  299. package/dist/assets/FileEditorPane-CNtuGjQ3.js +0 -2
  300. package/dist/assets/IconGlyphs-BClgag6E.js +0 -1
  301. package/dist/assets/ProjectFilesSection-Cf2BEcAN.js +0 -12
  302. package/dist/assets/ReviewPage-BRuxFaH5.js +0 -3
  303. package/dist/assets/code-dark-BEZTQwYZ.css +0 -1
  304. package/dist/assets/file-icons-BOlcJ0ZF.js +0 -1
  305. package/dist/assets/main-DK9Sy7-c.css +0 -1
  306. package/dist/assets/review-BcnwQtJv.css +0 -1
  307. package/dist/assets/workspace-editor-model-CSGCl21x.js +0 -1
  308. package/dist/assets/workspace-editor-monaco-CJNwbpCy.js +0 -4
  309. package/dist/assets/workspace-editor-monaco-CO9w8YV0.js +0 -1
  310. package/dist/assets/workspace-view-state-BjKPYp0E.js +0 -7
  311. package/extensions/browser/backend/agent-browser-runtime.js +0 -1041
  312. package/extensions/browser/backend/agent-capability.js +0 -55
  313. package/extensions/browser/backend/browser-mcp-server.js +0 -621
  314. package/extensions/browser/backend/browser-resource-manager.js +0 -1309
  315. package/extensions/browser/backend/browser-resource-store.js +0 -190
  316. package/extensions/browser/backend/browser-router.js +0 -133
  317. package/extensions/browser/backend/executable-discovery.js +0 -224
  318. package/extensions/browser/backend/farming-browser-client.js +0 -83
  319. package/extensions/browser/backend/index.js +0 -7
  320. package/extensions/browser/frontend/BrowserSidebarPortals.tsx +0 -360
  321. package/extensions/browser/frontend/BrowserViewer.tsx +0 -500
  322. package/extensions/browser/frontend/browser-resource-state.ts +0 -60
  323. package/extensions/browser/frontend/browser.css +0 -567
  324. package/extensions/browser/frontend/types.ts +0 -31
  325. package/extensions/browser/frontend/useBrowserResources.ts +0 -158
  326. package/frontend/vendor/ghostty-web/index.d.ts +0 -1842
  327. package/shared/backend-connection-status.d.ts +0 -37
  328. package/shared/browser-protocol.d.ts +0 -5
  329. /package/dist/assets/{api-w2x5QfxB.js → api-ByCV9MEr.js} +0 -0
  330. /package/dist/assets/{core-vPkV8P_m.js → core-Czwdx12k.js} +0 -0
  331. /package/dist/assets/{fsharp-DNZNZyEE.js → fsharp-BjPpkoJe.js} +0 -0
  332. /package/dist/assets/{go-_kPJjNc_.js → go-BkIDmSUP.js} +0 -0
  333. /package/dist/assets/{graphql-4HCNVt5g.js → graphql-CCJTpqi1.js} +0 -0
  334. /package/dist/assets/{hcl-C87ymY0o.js → hcl-s4z8lUFg.js} +0 -0
  335. /package/dist/assets/{ini-BYrrGNTc.js → ini-CyUW1I7Z.js} +0 -0
  336. /package/dist/assets/{java-B211rJbk.js → java-CFCcGRiM.js} +0 -0
  337. /package/dist/assets/{julia-DVPsRtBm.js → julia-DQyH_EYb.js} +0 -0
  338. /package/dist/assets/{kotlin-BYyNerjp.js → kotlin-B8hA5O51.js} +0 -0
  339. /package/dist/assets/{less-EC9UblGE.js → less-DaVdaas0.js} +0 -0
  340. /package/dist/assets/{lexon-B1yuUdqW.js → lexon-DLBhPd3y.js} +0 -0
  341. /package/dist/assets/{lua-Bo6GFzxT.js → lua-gB1c_cif.js} +0 -0
  342. /package/dist/assets/{m3-C-Y9cBVI.js → m3-C2T3z3gB.js} +0 -0
  343. /package/dist/assets/{markdown-BHu-wLbe.js → markdown-CKq-Euj1.js} +0 -0
  344. /package/dist/assets/{mips-Dkds26rX.js → mips-Bcs5qIDX.js} +0 -0
  345. /package/dist/assets/{msdax-PU4g-8_g.js → msdax-CBP48FHF.js} +0 -0
  346. /package/dist/assets/{mysql-fPpuGpS7.js → mysql-c7tiPcFj.js} +0 -0
  347. /package/dist/assets/{objective-c-Wy1e5JUf.js → objective-c-D4BZkeEV.js} +0 -0
  348. /package/dist/assets/{pascal-BIXwvrqU.js → pascal-CJDZg0MN.js} +0 -0
  349. /package/dist/assets/{pascaligo-DwR0AtL_.js → pascaligo-Bygi8Ynl.js} +0 -0
  350. /package/dist/assets/{perl-CGHAl8my.js → perl-aS8bkf7E.js} +0 -0
  351. /package/dist/assets/{pgsql-B91M73E2.js → pgsql-CzNAYFYl.js} +0 -0
  352. /package/dist/assets/{php-C70ttrCx.js → php-DOtoEFTT.js} +0 -0
  353. /package/dist/assets/{pla-BNrXaRh0.js → pla-DugRnao1.js} +0 -0
  354. /package/dist/assets/{postiats-Dv2IAZR9.js → postiats-DsXwusKh.js} +0 -0
  355. /package/dist/assets/{powerquery-CkY5Wq7J.js → powerquery-DL3o72oD.js} +0 -0
  356. /package/dist/assets/{powershell-uGIYLU3C.js → powershell-C1qHekys.js} +0 -0
  357. /package/dist/assets/{protobuf-CavfmJu-.js → protobuf-DHgsc-cH.js} +0 -0
  358. /package/dist/assets/{pug-Drqnyitx.js → pug-FZXlE_Iu.js} +0 -0
  359. /package/dist/assets/{qrcode-7uGsTEmi.js → qrcode-DJDTdjL7.js} +0 -0
  360. /package/dist/assets/{qsharp-C7yF-Dti.js → qsharp-bLyhI3DT.js} +0 -0
  361. /package/dist/assets/{r-HXLGP0_-.js → r-DjFGXbAa.js} +0 -0
  362. /package/dist/assets/{redis-C4y6mf2p.js → redis-DSoZ_V6u.js} +0 -0
  363. /package/dist/assets/{redshift-DdQrFPrv.js → redshift-CzkdCsrg.js} +0 -0
  364. /package/dist/assets/{restructuredtext-CBrf2PNj.js → restructuredtext-B_XPrGCQ.js} +0 -0
  365. /package/dist/assets/{ruby-CVtMoX2B.js → ruby-Cry2RneJ.js} +0 -0
  366. /package/dist/assets/{rust-Ckq4sbMP.js → rust-CWzdbFq1.js} +0 -0
  367. /package/dist/assets/{sb-er66OpE3.js → sb-DmofU59h.js} +0 -0
  368. /package/dist/assets/{scala-BdWZz-hH.js → scala-BUC2l-vN.js} +0 -0
  369. /package/dist/assets/{scheme-kTV_s2kP.js → scheme-CNIB_72f.js} +0 -0
  370. /package/dist/assets/{scss-Bymy2VJQ.js → scss-DcL4-NbX.js} +0 -0
  371. /package/dist/assets/{shell-bwEP1LQ1.js → shell-CgAGvSSA.js} +0 -0
  372. /package/dist/assets/{solidity-DCddeBgL.js → solidity-DSkt6O_P.js} +0 -0
  373. /package/dist/assets/{sophia-BMN3AQig.js → sophia--5ytcjDN.js} +0 -0
  374. /package/dist/assets/{sparql-CSXOLjzi.js → sparql-C1Srr7cD.js} +0 -0
  375. /package/dist/assets/{sql-COg8ewmK.js → sql-2k_O5fj7.js} +0 -0
  376. /package/dist/assets/{st-CIaZtBS_.js → st-DCCCPqfz.js} +0 -0
  377. /package/dist/assets/{swift-DXiOhUHu.js → swift-Dd8mAfOt.js} +0 -0
  378. /package/dist/assets/{systemverilog-BTDE6Dr2.js → systemverilog-B0-aZYXu.js} +0 -0
  379. /package/dist/assets/{tcl-B0zy9NvV.js → tcl-CZAZwPVX.js} +0 -0
  380. /package/dist/assets/{twig-D594l4Z1.js → twig-D-Ag0tdS.js} +0 -0
  381. /package/dist/assets/{typespec-XKVjaPqo.js → typespec-B7kTFp0Z.js} +0 -0
  382. /package/dist/assets/{useWorkspaceMenuKeyboard-Bpy_s69Z.js → useWorkspaceMenuKeyboard-uYZpDxhU.js} +0 -0
  383. /package/dist/assets/{vb-B2wYoKRV.js → vb-20w1JPum.js} +0 -0
  384. /package/dist/assets/{wgsl-D0Yf2LbK.js → wgsl-CLJfqFt0.js} +0 -0
  385. /package/dist/assets/{workspace-working-copy-C3FSR3N6.js → workspace-working-copy-Cfz07cOd.js} +0 -0
@@ -1,272 +1,317 @@
1
- const crypto = require('crypto');
2
- const fs = require('fs');
3
- const fsp = require('fs/promises');
4
- const path = require('path');
5
- const { diffChars } = require('diff');
6
- const { execFile, spawn } = require('child_process');
7
- const readline = require('readline');
8
- const { pathToFileURL } = require('url');
9
-
1
+ // Generated by scripts/build-backend-runtime.ts. Do not edit.
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var workspace_file_service_exports = {};
31
+ __export(workspace_file_service_exports, {
32
+ DEFAULT_MAX_FILE_SIZE: () => DEFAULT_MAX_FILE_SIZE,
33
+ DEFAULT_MAX_WRITE_SIZE: () => DEFAULT_MAX_WRITE_SIZE,
34
+ DEFAULT_WATCH_DEPTH: () => DEFAULT_WATCH_DEPTH,
35
+ WorkspaceFileError: () => WorkspaceFileError,
36
+ WorkspaceFileService: () => WorkspaceFileService,
37
+ createAddedFileLineChangesHunk: () => createAddedFileLineChangesHunk,
38
+ isPackagedRuntime: () => isPackagedRuntime,
39
+ normalizeGitHistoryLimit: () => normalizeGitHistoryLimit,
40
+ normalizeGitHistorySkip: () => normalizeGitHistorySkip,
41
+ parseGitBlamePorcelain: () => parseGitBlamePorcelain,
42
+ parseGitHistoryChanges: () => parseGitHistoryChanges,
43
+ parseGitHistoryLog: () => parseGitHistoryLog,
44
+ parseGitHistoryReferences: () => parseGitHistoryReferences,
45
+ parseUnifiedDiffHunks: () => parseUnifiedDiffHunks,
46
+ parseUnifiedDiffRows: () => parseUnifiedDiffRows,
47
+ resolveCommandRunnerNodePath: () => resolveCommandRunnerNodePath,
48
+ selectUnifiedDiffHunk: () => selectUnifiedDiffHunk
49
+ });
50
+ module.exports = __toCommonJS(workspace_file_service_exports);
51
+ const crypto = require("crypto");
52
+ const fs = require("fs");
53
+ const fsp = require("fs/promises");
54
+ const path = require("path");
55
+ const { diffChars } = require("diff");
56
+ const { execFile, spawn } = require("child_process");
57
+ const readline = require("readline");
58
+ const { pathToFileURL } = require("url");
10
59
  const DEFAULT_MAX_FILE_SIZE = 1024 * 1024;
11
60
  const DEFAULT_MAX_WRITE_SIZE = 2 * 1024 * 1024;
12
61
  const DEFAULT_MAX_PREVIEW_FILE_SIZE = 8 * 1024 * 1024;
13
62
  const DEFAULT_SEARCH_LIMIT = 100;
14
63
  const DEFAULT_GIT_CHANGES_LIMIT = 500;
15
- const DEFAULT_GIT_STATUS_CACHE_TTL_MS = 30000;
64
+ const DEFAULT_GIT_STATUS_CACHE_TTL_MS = 3e4;
16
65
  const DEFAULT_GIT_STATUS_INLINE_TIMEOUT_MS = 80;
17
- const DEFAULT_GIT_STATUS_TIMEOUT_MS = 15000;
18
- const DEFAULT_SEARCH_TIMEOUT_MS = 3000;
19
- const DEFAULT_BLAME_TIMEOUT_MS = 5000;
20
- const DEFAULT_DIFF_TIMEOUT_MS = 5000;
66
+ const DEFAULT_GIT_STATUS_TIMEOUT_MS = 15e3;
67
+ const DEFAULT_SEARCH_TIMEOUT_MS = 3e3;
68
+ const DEFAULT_BLAME_TIMEOUT_MS = 5e3;
69
+ const DEFAULT_DIFF_TIMEOUT_MS = 5e3;
21
70
  const DEFAULT_DIFF_MAX_BUFFER = 1024 * 1024;
22
71
  const DEFAULT_GIT_HISTORY_LIMIT = 50;
23
72
  const MAX_GIT_HISTORY_LIMIT = 100;
24
- const DEFAULT_GIT_HISTORY_TIMEOUT_MS = 5000;
73
+ const DEFAULT_GIT_HISTORY_TIMEOUT_MS = 5e3;
25
74
  const DEFAULT_GIT_HISTORY_MAX_BUFFER = 8 * 1024 * 1024;
26
75
  const DEFAULT_WATCH_DEPTH = 1;
27
76
  const SEARCH_FILE_LIST_MAX_BUFFER = 16 * 1024 * 1024;
28
77
  const BINARY_SNIFF_BYTES = 8192;
29
78
  const PATH_SEARCH_MIN_CANDIDATES = 120;
30
79
  const PATH_SEARCH_CANDIDATE_MULTIPLIER = 8;
31
- const TIMEOUT = Symbol('timeout');
32
- const IMAGE_PREVIEW_MEDIA_TYPES = new Map([
33
- ['.png', 'image/png'],
34
- ['.jpg', 'image/jpeg'],
35
- ['.jpeg', 'image/jpeg'],
36
- ['.gif', 'image/gif'],
37
- ['.webp', 'image/webp'],
38
- ['.bmp', 'image/bmp'],
39
- ['.ico', 'image/x-icon'],
40
- ['.avif', 'image/avif'],
41
- ['.svg', 'image/svg+xml'],
80
+ const TIMEOUT = /* @__PURE__ */ Symbol("timeout");
81
+ function processError(value) {
82
+ if (value instanceof Error) return value;
83
+ return Object.assign(new Error(String(value)), { cause: value });
84
+ }
85
+ function isRecord(value) {
86
+ return typeof value === "object" && value !== null;
87
+ }
88
+ const IMAGE_PREVIEW_MEDIA_TYPES = /* @__PURE__ */ new Map([
89
+ [".png", "image/png"],
90
+ [".jpg", "image/jpeg"],
91
+ [".jpeg", "image/jpeg"],
92
+ [".gif", "image/gif"],
93
+ [".webp", "image/webp"],
94
+ [".bmp", "image/bmp"],
95
+ [".ico", "image/x-icon"],
96
+ [".avif", "image/avif"],
97
+ [".svg", "image/svg+xml"]
42
98
  ]);
43
- const TEXT_IMAGE_PREVIEW_EXTENSIONS = new Set(['.svg']);
44
- const IGNORED_NAMES = new Set([
45
- '.git',
46
- '.farming',
47
- 'node_modules',
48
- 'dist',
49
- 'build',
50
- 'coverage',
51
- '.next',
52
- '.vite',
99
+ const TEXT_IMAGE_PREVIEW_EXTENSIONS = /* @__PURE__ */ new Set([".svg"]);
100
+ const IGNORED_NAMES = /* @__PURE__ */ new Set([
101
+ ".git",
102
+ ".farming",
103
+ "node_modules",
104
+ "dist",
105
+ "build",
106
+ "coverage",
107
+ ".next",
108
+ ".vite"
53
109
  ]);
54
- const SEARCH_IGNORED_NAMES = new Set([
110
+ const SEARCH_IGNORED_NAMES = /* @__PURE__ */ new Set([
55
111
  ...IGNORED_NAMES,
56
- '.dolt',
57
- '.doltcfg',
58
- '.idea',
59
- '.vscode',
60
- '.tmp',
61
- '.DS_Store',
62
- 'dist-release',
63
- 'reference',
64
- 'test-results',
65
- 'playwright-report',
66
- 'server.log',
67
- 'local-farming.log',
68
- '.turbo',
69
- '.cache',
70
- '.pytest_cache',
71
- '.mypy_cache',
72
- '.ruff_cache',
73
- '__pycache__',
112
+ ".dolt",
113
+ ".doltcfg",
114
+ ".idea",
115
+ ".vscode",
116
+ ".tmp",
117
+ ".DS_Store",
118
+ "dist-release",
119
+ "reference",
120
+ "test-results",
121
+ "playwright-report",
122
+ "server.log",
123
+ "local-farming.log",
124
+ ".turbo",
125
+ ".cache",
126
+ ".pytest_cache",
127
+ ".mypy_cache",
128
+ ".ruff_cache",
129
+ "__pycache__"
74
130
  ]);
75
- const EXPANDED_SEARCH_IGNORED_NAMES = new Set([
76
- '.git',
77
- '.DS_Store',
78
- 'server.log',
79
- 'local-farming.log',
80
- 'node_modules',
81
- 'dist',
82
- 'build',
83
- 'coverage',
84
- '.next',
85
- '.vite',
86
- '.turbo',
87
- '.cache',
88
- '.pytest_cache',
89
- '.mypy_cache',
90
- '.ruff_cache',
91
- '__pycache__',
92
- 'test-results',
93
- 'playwright-report',
131
+ const EXPANDED_SEARCH_IGNORED_NAMES = /* @__PURE__ */ new Set([
132
+ ".git",
133
+ ".DS_Store",
134
+ "server.log",
135
+ "local-farming.log",
136
+ "node_modules",
137
+ "dist",
138
+ "build",
139
+ "coverage",
140
+ ".next",
141
+ ".vite",
142
+ ".turbo",
143
+ ".cache",
144
+ ".pytest_cache",
145
+ ".mypy_cache",
146
+ ".ruff_cache",
147
+ "__pycache__",
148
+ "test-results",
149
+ "playwright-report"
94
150
  ]);
95
- const HIDDEN_NAMES = new Set([
151
+ const HIDDEN_NAMES = /* @__PURE__ */ new Set([
96
152
  ...IGNORED_NAMES,
97
- '.dolt',
98
- '.doltcfg',
99
- '.idea',
100
- '.vscode',
101
- '.tmp',
102
- '.DS_Store',
103
- 'dist-release',
104
- 'reference',
105
- 'test-results',
106
- 'playwright-report',
107
- 'server.log',
108
- 'local-farming.log',
109
- '.turbo',
110
- '.cache',
111
- '.pytest_cache',
112
- '.mypy_cache',
113
- '.ruff_cache',
114
- '__pycache__',
153
+ ".dolt",
154
+ ".doltcfg",
155
+ ".idea",
156
+ ".vscode",
157
+ ".tmp",
158
+ ".DS_Store",
159
+ "dist-release",
160
+ "reference",
161
+ "test-results",
162
+ "playwright-report",
163
+ "server.log",
164
+ "local-farming.log",
165
+ ".turbo",
166
+ ".cache",
167
+ ".pytest_cache",
168
+ ".mypy_cache",
169
+ ".ruff_cache",
170
+ "__pycache__"
115
171
  ]);
116
- const TREE_HIDDEN_NAMES = new Set([
117
- '.git',
118
- '.DS_Store',
119
- 'server.log',
120
- 'local-farming.log',
172
+ const TREE_HIDDEN_NAMES = /* @__PURE__ */ new Set([
173
+ ".git",
174
+ ".DS_Store",
175
+ "server.log",
176
+ "local-farming.log"
121
177
  ]);
122
-
123
178
  function gitDiffWhitespaceArgs(value) {
124
- if (value === 'ALL' || value === 'IGNORE_ALL') return ['--ignore-all-space'];
125
- if (value === 'TRAILING' || value === 'IGNORE_TRAILING') return ['--ignore-space-at-eol'];
126
- if (value === 'LEADING_AND_TRAILING' || value === 'IGNORE_LEADING_AND_TRAILING') return ['--ignore-space-change'];
179
+ if (value === "ALL" || value === "IGNORE_ALL") return ["--ignore-all-space"];
180
+ if (value === "TRAILING" || value === "IGNORE_TRAILING") return ["--ignore-space-at-eol"];
181
+ if (value === "LEADING_AND_TRAILING" || value === "IGNORE_LEADING_AND_TRAILING") return ["--ignore-space-change"];
127
182
  return [];
128
183
  }
129
-
130
184
  function gitDiffContextArgs(value) {
131
185
  const context = Number(value);
132
186
  if (!Number.isInteger(context) || context < 0) return [];
133
- return [`--unified=${Math.min(context, 10000)}`];
187
+ return [`--unified=${Math.min(context, 1e4)}`];
134
188
  }
135
-
136
189
  function resolveBundledRipgrepPath() {
137
- const script = path.join(__dirname, '..', 'node_modules', 'ripgrep', 'lib', 'rg.mjs');
190
+ const script = path.join(__dirname, "..", "node_modules", "ripgrep", "lib", "rg.mjs");
138
191
  if (fs.existsSync(script)) return script;
139
- const binaryName = process.platform === 'win32' ? 'rg.cmd' : 'rg';
140
- const bin = path.join(__dirname, '..', 'node_modules', '.bin', binaryName);
141
- return fs.existsSync(bin) ? bin : '';
192
+ const binaryName = process.platform === "win32" ? "rg.cmd" : "rg";
193
+ const bin = path.join(__dirname, "..", "node_modules", ".bin", binaryName);
194
+ return fs.existsSync(bin) ? bin : "";
142
195
  }
143
-
144
196
  function resolveBundledRipgrepModulePath() {
145
197
  try {
146
- return require.resolve('ripgrep');
198
+ return require.resolve("ripgrep");
147
199
  } catch {
148
- const candidate = path.join(__dirname, '..', 'node_modules', 'ripgrep', 'lib', 'index.mjs');
149
- return fs.existsSync(candidate) ? candidate : '';
200
+ const candidate = path.join(__dirname, "..", "node_modules", "ripgrep", "lib", "index.mjs");
201
+ return fs.existsSync(candidate) ? candidate : "";
150
202
  }
151
203
  }
152
-
153
204
  function isCommandUnavailable(error) {
154
- return error && (error.code === 'ENOENT' || error.code === 'EACCES');
205
+ return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "EACCES");
155
206
  }
156
-
157
207
  class WorkspaceFileError extends Error {
208
+ details;
209
+ statusCode;
158
210
  constructor(message, statusCode = 400, details = {}) {
159
211
  super(message);
160
- this.name = 'WorkspaceFileError';
212
+ this.name = "WorkspaceFileError";
161
213
  this.statusCode = statusCode;
162
214
  this.details = details;
163
215
  }
164
216
  }
165
-
166
217
  function resolveCommandRunnerNodePath(options = {}) {
167
218
  return options.nodePath || process.env.FARMING_NODE_BIN || process.execPath;
168
219
  }
169
-
170
220
  function isPackagedRuntime() {
171
- return Boolean(process.pkg) || process.env.FARMING_PACKAGED_RUNTIME === '1';
221
+ return Boolean(process.pkg) || process.env.FARMING_PACKAGED_RUNTIME === "1";
172
222
  }
173
-
174
223
  let chokidarPromise = null;
175
-
176
224
  async function loadChokidar() {
177
225
  if (!chokidarPromise) {
178
- chokidarPromise = import('chokidar').then(module => module.default || module);
226
+ chokidarPromise = import("chokidar").then((module2) => module2.default || module2);
179
227
  }
180
228
  return chokidarPromise;
181
229
  }
182
-
183
230
  class CommandRunner {
231
+ child;
232
+ helperPath;
233
+ nextId;
234
+ nodePath;
235
+ pending;
236
+ ready;
184
237
  constructor(options = {}) {
185
- this.helperPath = options.helperPath || path.join(__dirname, 'command-runner-child.js');
238
+ this.helperPath = options.helperPath || path.join(__dirname, "command-runner-child.cjs");
186
239
  this.nodePath = resolveCommandRunnerNodePath(options);
187
240
  this.nextId = 1;
188
- this.pending = new Map();
241
+ this.pending = /* @__PURE__ */ new Map();
189
242
  this.child = null;
190
243
  this.ready = false;
191
244
  if (!options.disableHelper && !isPackagedRuntime()) {
192
245
  this.start();
193
246
  }
194
247
  }
195
-
196
248
  start() {
197
249
  try {
198
250
  this.child = spawn(this.nodePath, [this.helperPath], {
199
251
  cwd: __dirname,
200
252
  env: process.env,
201
- stdio: ['pipe', 'pipe', 'pipe'],
253
+ stdio: ["pipe", "pipe", "pipe"]
202
254
  });
203
255
  } catch {
204
256
  this.child = null;
205
257
  return;
206
258
  }
207
-
259
+ const child = this.child;
260
+ if (!child) return;
208
261
  this.ready = true;
209
262
  const rl = readline.createInterface({
210
- input: this.child.stdout,
211
- crlfDelay: Infinity,
263
+ input: child.stdout,
264
+ crlfDelay: Infinity
212
265
  });
213
-
214
- rl.on('line', line => {
266
+ rl.on("line", (line) => {
215
267
  let response;
216
268
  try {
217
269
  response = JSON.parse(line);
218
270
  } catch {
219
271
  return;
220
272
  }
221
-
222
273
  const pending = this.pending.get(response.id);
223
274
  if (!pending) return;
224
275
  this.pending.delete(response.id);
225
-
226
276
  if (response.ok) {
227
277
  pending.resolve({
228
- stdout: response.stdout || '',
229
- stderr: response.stderr || '',
278
+ stdout: response.stdout || "",
279
+ stderr: response.stderr || ""
230
280
  });
231
281
  return;
232
282
  }
233
-
234
- const error = new Error(response.error?.message || 'command failed');
283
+ const error = new Error(response.error?.message || "command failed");
235
284
  error.code = response.error?.code;
236
285
  error.signal = response.error?.signal;
237
- error.stdout = response.error?.stdout || '';
238
- error.stderr = response.error?.stderr || '';
286
+ error.stdout = response.error?.stdout || "";
287
+ error.stderr = response.error?.stderr || "";
239
288
  pending.reject(error);
240
289
  });
241
-
242
- this.child.stderr.on('data', data => {
243
- const text = String(data || '').trim();
244
- if (text) console.error('Workspace command helper:', text);
290
+ child.stderr.on("data", (data) => {
291
+ const text = String(data || "").trim();
292
+ if (text) console.error("Workspace command helper:", text);
245
293
  });
246
-
247
- this.child.on('error', () => {
294
+ child.on("error", () => {
248
295
  this.ready = false;
249
296
  this.child = null;
250
297
  const pending = Array.from(this.pending.values());
251
298
  this.pending.clear();
252
- pending.forEach(({ reject }) => reject(new Error('workspace command helper failed')));
299
+ pending.forEach(({ reject }) => reject(new Error("workspace command helper failed")));
253
300
  });
254
-
255
- this.child.on('exit', () => {
301
+ child.on("exit", () => {
256
302
  this.ready = false;
257
303
  this.child = null;
258
304
  const pending = Array.from(this.pending.values());
259
305
  this.pending.clear();
260
- pending.forEach(({ reject }) => reject(new Error('workspace command helper exited')));
306
+ pending.forEach(({ reject }) => reject(new Error("workspace command helper exited")));
261
307
  });
262
308
  }
263
-
264
309
  run(command, args, options = {}) {
265
310
  if (!this.ready || !this.child || !this.child.stdin.writable) {
266
311
  return execFileAsync(command, args, options);
267
312
  }
268
-
269
313
  const id = String(this.nextId++);
314
+ const child = this.child;
270
315
  const request = {
271
316
  id,
272
317
  command,
@@ -274,19 +319,18 @@ class CommandRunner {
274
319
  cwd: options.cwd,
275
320
  env: options.env,
276
321
  maxBuffer: options.maxBuffer,
277
- timeout: options.timeout,
322
+ timeout: options.timeout
278
323
  };
279
-
280
324
  return new Promise((resolve, reject) => {
281
325
  this.pending.set(id, { resolve, reject });
282
- this.child.stdin.write(`${JSON.stringify(request)}\n`, error => {
326
+ child.stdin.write(`${JSON.stringify(request)}
327
+ `, (error) => {
283
328
  if (!error) return;
284
329
  this.pending.delete(id);
285
330
  reject(error);
286
331
  });
287
332
  });
288
333
  }
289
-
290
334
  dispose() {
291
335
  if (!this.child) return;
292
336
  this.child.kill();
@@ -294,76 +338,61 @@ class CommandRunner {
294
338
  this.ready = false;
295
339
  }
296
340
  }
297
-
298
341
  function isInside(root, target) {
299
342
  const relative = path.relative(root, target);
300
- return relative === '' || Boolean(relative) && !relative.startsWith('..') && !path.isAbsolute(relative);
343
+ return relative === "" || Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
301
344
  }
302
-
303
345
  function isInsideAnyRoot(roots, target) {
304
346
  return (Array.isArray(roots) ? roots : []).some((root) => {
305
347
  let realRoot = root;
306
348
  try {
307
349
  realRoot = fs.realpathSync(root);
308
350
  } catch {
309
- // Missing allowed roots cannot authorize a resolved target.
310
351
  }
311
352
  return isInside(realRoot, target);
312
353
  });
313
354
  }
314
-
315
355
  function joinRelativePath(parentPath, name) {
316
- return [String(parentPath || '').replace(/\/+$/, ''), name]
317
- .filter(Boolean)
318
- .join('/')
319
- .replace(/\\/g, '/');
320
- }
321
-
322
- function normalizeUserPath(userPath = '') {
323
- const value = String(userPath || '').trim();
324
- if (!value || value === '.') return '';
356
+ return [String(parentPath || "").replace(/\/+$/, ""), name].filter(Boolean).join("/").replace(/\\/g, "/");
357
+ }
358
+ function normalizeUserPath(userPath = "") {
359
+ const value = String(userPath || "").trim();
360
+ if (!value || value === ".") return "";
325
361
  if (path.isAbsolute(value)) {
326
- throw new WorkspaceFileError('absolute paths are not allowed', 400);
362
+ throw new WorkspaceFileError("absolute paths are not allowed", 400);
327
363
  }
328
364
  const normalized = path.normalize(value);
329
- if (normalized === '..' || normalized.startsWith(`..${path.sep}`)) {
330
- throw new WorkspaceFileError('path must stay inside the workspace', 403);
365
+ if (normalized === ".." || normalized.startsWith(`..${path.sep}`)) {
366
+ throw new WorkspaceFileError("path must stay inside the workspace", 403);
331
367
  }
332
- return normalized === '.' ? '' : normalized;
368
+ return normalized === "." ? "" : normalized;
333
369
  }
334
-
335
370
  function relativeFromRoot(root, target) {
336
371
  const relative = path.relative(root, target);
337
- return relative.split(path.sep).join('/');
372
+ return relative.split(path.sep).join("/");
338
373
  }
339
-
340
374
  function normalizeSearchResultPath(resultPath) {
341
- return String(resultPath || '').replace(/^\.\//, '');
375
+ return String(resultPath || "").replace(/^\.\//, "");
342
376
  }
343
-
344
377
  function searchIgnoredNames(includeIgnored = false) {
345
378
  return includeIgnored ? EXPANDED_SEARCH_IGNORED_NAMES : SEARCH_IGNORED_NAMES;
346
379
  }
347
-
348
380
  function searchExcludeGlobArgs() {
349
381
  const args = [];
350
382
  SEARCH_IGNORED_NAMES.forEach((name) => {
351
- args.push('--glob', `!${name}/**`);
352
- args.push('--glob', `!**/${name}/**`);
383
+ args.push("--glob", `!${name}/**`);
384
+ args.push("--glob", `!**/${name}/**`);
353
385
  });
354
386
  return args;
355
387
  }
356
-
357
388
  function isSearchIgnoredRelativePath(relativePath, includeIgnored = false) {
358
- return String(relativePath || '').split(/[\\/]/).some(part => searchIgnoredNames(includeIgnored).has(part));
389
+ return String(relativePath || "").split(/[\\/]/).some((part) => searchIgnoredNames(includeIgnored).has(part));
359
390
  }
360
-
361
391
  function shouldPruneDirectoryNameSearch(relativePath) {
362
- return String(relativePath || '').split(/[\\/]/).some(part => IGNORED_NAMES.has(part));
392
+ return String(relativePath || "").split(/[\\/]/).some((part) => IGNORED_NAMES.has(part));
363
393
  }
364
-
365
394
  function gitStatusExcludePathspecArgs() {
366
- const args = ['--', '.'];
395
+ const args = ["--", "."];
367
396
  HIDDEN_NAMES.forEach((name) => {
368
397
  args.push(`:(exclude)${name}`);
369
398
  args.push(`:(exclude)${name}/**`);
@@ -372,9 +401,8 @@ function gitStatusExcludePathspecArgs() {
372
401
  });
373
402
  return args;
374
403
  }
375
-
376
- function gitSearchExcludePathspecArgs(includeIgnored = false, searchPath = '.') {
377
- const args = ['--', searchPath];
404
+ function gitSearchExcludePathspecArgs(includeIgnored = false, searchPath = ".") {
405
+ const args = ["--", searchPath];
378
406
  searchIgnoredNames(includeIgnored).forEach((name) => {
379
407
  args.push(`:(exclude)${name}`);
380
408
  args.push(`:(exclude)${name}/**`);
@@ -383,27 +411,22 @@ function gitSearchExcludePathspecArgs(includeIgnored = false, searchPath = '.')
383
411
  });
384
412
  return args;
385
413
  }
386
-
387
414
  function normalizeGitStatusPath(resultPath) {
388
- return normalizeSearchResultPath(resultPath).replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
415
+ return normalizeSearchResultPath(resultPath).replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
389
416
  }
390
-
391
417
  function sha1(buffer) {
392
- return crypto.createHash('sha1').update(buffer).digest('hex');
418
+ return crypto.createHash("sha1").update(buffer).digest("hex");
393
419
  }
394
-
395
420
  function previewForPath(filePath, options = {}) {
396
421
  const extension = path.extname(filePath).toLowerCase();
397
- if (extension === '.pdf') return { kind: 'pdf', mediaType: 'application/pdf' };
422
+ if (extension === ".pdf") return { kind: "pdf", mediaType: "application/pdf" };
398
423
  if (!options.includeTextImages && TEXT_IMAGE_PREVIEW_EXTENSIONS.has(extension)) return null;
399
424
  const mediaType = IMAGE_PREVIEW_MEDIA_TYPES.get(extension);
400
- return mediaType ? { kind: 'image', mediaType } : null;
425
+ return mediaType ? { kind: "image", mediaType } : null;
401
426
  }
402
-
403
427
  function metadataFileVersion(relativePath, stat) {
404
- return sha1(Buffer.from(`${relativePath}:${stat.size}:${stat.mtimeMs}`, 'utf8'));
428
+ return sha1(Buffer.from(`${relativePath}:${stat.size}:${stat.mtimeMs}`, "utf8"));
405
429
  }
406
-
407
430
  function workspaceEntryVersion(stat) {
408
431
  return sha1(Buffer.from([
409
432
  stat.dev,
@@ -411,28 +434,25 @@ function workspaceEntryVersion(stat) {
411
434
  stat.mode,
412
435
  stat.size,
413
436
  stat.mtimeMs,
414
- stat.ctimeMs,
415
- ].join(':'), 'utf8'));
437
+ stat.ctimeMs
438
+ ].join(":"), "utf8"));
416
439
  }
417
-
418
440
  async function readTextPrefix(target, maxBytes) {
419
- const handle = await fsp.open(target, 'r');
441
+ const handle = await fsp.open(target, "r");
420
442
  try {
421
443
  const buffer = Buffer.alloc(maxBytes);
422
444
  const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
423
- return buffer.subarray(0, bytesRead).toString('utf8');
445
+ return buffer.subarray(0, bytesRead).toString("utf8");
424
446
  } finally {
425
447
  await handle.close();
426
448
  }
427
449
  }
428
-
429
450
  async function isProbablyBinaryFile(target) {
430
- const handle = await fsp.open(target, 'r');
451
+ const handle = await fsp.open(target, "r");
431
452
  try {
432
453
  const buffer = Buffer.alloc(BINARY_SNIFF_BYTES);
433
454
  const { bytesRead } = await handle.read(buffer, 0, BINARY_SNIFF_BYTES, 0);
434
455
  if (bytesRead === 0) return false;
435
-
436
456
  let suspiciousControlBytes = 0;
437
457
  for (let index = 0; index < bytesRead; index += 1) {
438
458
  const byte = buffer[index];
@@ -442,48 +462,41 @@ async function isProbablyBinaryFile(target) {
442
462
  suspiciousControlBytes += 1;
443
463
  }
444
464
  }
445
-
446
465
  return suspiciousControlBytes / bytesRead > 0.3;
447
466
  } finally {
448
467
  await handle.close();
449
468
  }
450
469
  }
451
-
452
470
  function shouldIgnorePath(filePath) {
453
- return filePath.split(path.sep).some(part => IGNORED_NAMES.has(part));
471
+ return filePath.split(path.sep).some((part) => IGNORED_NAMES.has(part));
454
472
  }
455
-
456
473
  function shouldHidePath(filePath) {
457
- return String(filePath || '').split(/[\\/]/).some(part => HIDDEN_NAMES.has(part));
474
+ return String(filePath || "").split(/[\\/]/).some((part) => HIDDEN_NAMES.has(part));
458
475
  }
459
-
460
476
  function normalizeEntryName(name) {
461
- const value = String(name || '').trim();
462
- if (!value || value === '.' || value === '..') {
463
- throw new WorkspaceFileError('name is required', 400);
477
+ const value = String(name || "").trim();
478
+ if (!value || value === "." || value === "..") {
479
+ throw new WorkspaceFileError("name is required", 400);
464
480
  }
465
- if (value.includes('/') || value.includes('\\') || path.basename(value) !== value) {
466
- throw new WorkspaceFileError('name must be a single path segment', 400);
481
+ if (value.includes("/") || value.includes("\\") || path.basename(value) !== value) {
482
+ throw new WorkspaceFileError("name must be a single path segment", 400);
467
483
  }
468
484
  if (IGNORED_NAMES.has(value)) {
469
- throw new WorkspaceFileError('ignored paths cannot be changed', 403);
485
+ throw new WorkspaceFileError("ignored paths cannot be changed", 403);
470
486
  }
471
487
  return value;
472
488
  }
473
-
474
489
  function parentDirectory(filePath) {
475
- const normalized = String(filePath || '').replace(/\/+$/, '');
476
- const index = normalized.lastIndexOf('/');
477
- return index === -1 ? '' : normalized.slice(0, index);
490
+ const normalized = String(filePath || "").replace(/\/+$/, "");
491
+ const index = normalized.lastIndexOf("/");
492
+ return index === -1 ? "" : normalized.slice(0, index);
478
493
  }
479
-
480
494
  function execFileAsync(command, args, options = {}) {
481
495
  return new Promise((resolve, reject) => {
482
496
  execFile(command, args, {
483
- encoding: 'utf8',
497
+ encoding: "utf8",
484
498
  maxBuffer: 2 * 1024 * 1024,
485
- stdio: ['ignore', 'pipe', 'pipe'],
486
- ...options,
499
+ ...options
487
500
  }, (error, stdout, stderr) => {
488
501
  if (error) {
489
502
  error.stdout = stdout;
@@ -495,92 +508,83 @@ function execFileAsync(command, args, options = {}) {
495
508
  });
496
509
  });
497
510
  }
498
-
499
511
  function gitStatusKind(statusCode) {
500
- if (!statusCode || statusCode.length < 2) return 'modified';
501
- if (statusCode === '??') return 'untracked';
502
- if (statusCode.includes('U') || statusCode === 'AA' || statusCode === 'DD') return 'conflicted';
503
- if (statusCode.includes('D')) return 'deleted';
504
- if (statusCode.includes('A')) return 'added';
505
- if (statusCode.includes('R')) return 'renamed';
506
- if (statusCode.includes('M')) return 'modified';
507
- return 'modified';
508
- }
509
-
512
+ const code = String(statusCode || "");
513
+ if (code.length < 2) return "modified";
514
+ if (code === "??") return "untracked";
515
+ if (code.includes("U") || code === "AA" || code === "DD") return "conflicted";
516
+ if (code.includes("D")) return "deleted";
517
+ if (code.includes("A")) return "added";
518
+ if (code.includes("R")) return "renamed";
519
+ if (code.includes("M")) return "modified";
520
+ return "modified";
521
+ }
510
522
  function gitStatusLabel(kind) {
511
- if (kind === 'added') return 'A';
512
- if (kind === 'deleted') return 'D';
513
- if (kind === 'renamed') return 'R';
514
- if (kind === 'untracked') return 'U';
515
- if (kind === 'conflicted') return '!';
516
- return 'M';
517
- }
518
-
523
+ if (kind === "added") return "A";
524
+ if (kind === "deleted") return "D";
525
+ if (kind === "renamed") return "R";
526
+ if (kind === "untracked") return "U";
527
+ if (kind === "conflicted") return "!";
528
+ return "M";
529
+ }
519
530
  function gitStatusReviewRank(kind) {
520
- if (kind === 'conflicted') return 0;
521
- if (kind === 'modified') return 1;
522
- if (kind === 'added') return 2;
523
- if (kind === 'deleted') return 3;
524
- if (kind === 'renamed') return 4;
525
- if (kind === 'untracked') return 5;
531
+ if (kind === "conflicted") return 0;
532
+ if (kind === "modified") return 1;
533
+ if (kind === "added") return 2;
534
+ if (kind === "deleted") return 3;
535
+ if (kind === "renamed") return 4;
536
+ if (kind === "untracked") return 5;
526
537
  return 6;
527
538
  }
528
-
529
539
  function normalizeGitHistoryLimit(value) {
530
540
  const parsed = Number(value);
531
541
  if (!Number.isInteger(parsed) || parsed < 1) return DEFAULT_GIT_HISTORY_LIMIT;
532
542
  return Math.min(parsed, MAX_GIT_HISTORY_LIMIT);
533
543
  }
534
-
535
544
  function normalizeGitHistorySkip(value) {
536
545
  const parsed = Number(value);
537
546
  if (!Number.isInteger(parsed) || parsed < 0) return 0;
538
- return Math.min(parsed, 1_000_000);
547
+ return Math.min(parsed, 1e6);
539
548
  }
540
-
541
- function normalizeGitObjectId(value, fieldName = 'commit') {
542
- const normalized = String(value || '').trim();
549
+ function normalizeGitObjectId(value, fieldName = "commit") {
550
+ const normalized = String(value || "").trim();
543
551
  if (!/^[0-9a-f]{40,64}$/i.test(normalized)) {
544
552
  throw new WorkspaceFileError(`${fieldName} must be a full git object id`, 400);
545
553
  }
546
554
  return normalized.toLowerCase();
547
555
  }
548
-
549
556
  function gitEmptyTreeObjectId(objectFormat) {
550
- const algorithm = objectFormat === 'sha256' ? 'sha256' : 'sha1';
551
- return crypto.createHash(algorithm).update(Buffer.from('tree 0\0')).digest('hex');
557
+ const algorithm = objectFormat === "sha256" ? "sha256" : "sha1";
558
+ return crypto.createHash(algorithm).update(Buffer.from("tree 0\0")).digest("hex");
552
559
  }
553
-
554
560
  function gitHistoryReference(value) {
555
- let id = String(value || '').trim();
561
+ let id = String(value || "").trim();
556
562
  if (!id) return null;
557
- if (id.startsWith('tag: ')) id = id.slice(5);
558
- if (id === 'HEAD') return { id, name: id, category: 'head' };
559
- if (id.startsWith('refs/heads/')) {
560
- return { id, name: id.slice('refs/heads/'.length), category: 'local-branch' };
563
+ if (id.startsWith("tag: ")) id = id.slice(5);
564
+ if (id === "HEAD") return { id, name: id, category: "head" };
565
+ if (id.startsWith("refs/heads/")) {
566
+ return { id, name: id.slice("refs/heads/".length), category: "local-branch" };
561
567
  }
562
- if (id.startsWith('refs/remotes/')) {
568
+ if (id.startsWith("refs/remotes/")) {
563
569
  if (/\/HEAD$/.test(id)) return null;
564
- return { id, name: id.slice('refs/remotes/'.length), category: 'remote-branch' };
570
+ return { id, name: id.slice("refs/remotes/".length), category: "remote-branch" };
565
571
  }
566
- if (id.startsWith('refs/tags/')) {
567
- return { id, name: id.slice('refs/tags/'.length), category: 'tag' };
572
+ if (id.startsWith("refs/tags/")) {
573
+ return { id, name: id.slice("refs/tags/".length), category: "tag" };
568
574
  }
569
- return { id, name: id.replace(/^refs\//, ''), category: 'reference' };
575
+ return { id, name: id.replace(/^refs\//, ""), category: "reference" };
570
576
  }
571
-
572
577
  function parseGitHistoryReferences(value) {
573
578
  const references = [];
574
- const seen = new Set();
575
- const add = candidate => {
579
+ const seen = /* @__PURE__ */ new Set();
580
+ const add = (candidate) => {
576
581
  const reference = gitHistoryReference(candidate);
577
582
  if (!reference || seen.has(reference.id)) return;
578
583
  seen.add(reference.id);
579
584
  references.push(reference);
580
585
  };
581
-
582
- String(value || '').split(', ').forEach(decoration => {
583
- const arrowIndex = decoration.indexOf(' -> ');
586
+ String(value || "").split(", ").forEach((decoration) => {
587
+ const arrowIndex = decoration.indexOf(" -> ");
584
588
  if (arrowIndex === -1) {
585
589
  add(decoration);
586
590
  return;
@@ -590,9 +594,8 @@ function parseGitHistoryReferences(value) {
590
594
  });
591
595
  return references;
592
596
  }
593
-
594
597
  function parseGitHistoryLog(stdout) {
595
- const fields = String(stdout || '').split('\0');
598
+ const fields = String(stdout || "").split("\0");
596
599
  const items = [];
597
600
  for (let index = 0; index + 7 < fields.length; index += 8) {
598
601
  const id = fields[index];
@@ -601,170 +604,142 @@ function parseGitHistoryLog(stdout) {
601
604
  items.push({
602
605
  id,
603
606
  displayId: id.slice(0, 8),
604
- parentIds: fields[index + 1].split(' ').filter(Boolean),
607
+ parentIds: fields[index + 1].split(" ").filter(Boolean),
605
608
  author: fields[index + 2],
606
609
  authorEmail: fields[index + 3],
607
- timestamp: Number.isFinite(timestamp) ? timestamp : undefined,
610
+ timestamp: Number.isFinite(timestamp) ? timestamp : void 0,
608
611
  subject: fields[index + 5],
609
- message: String(fields[index + 6] || '').trimEnd(),
610
- references: parseGitHistoryReferences(fields[index + 7]),
612
+ message: String(fields[index + 6] || "").trimEnd(),
613
+ references: parseGitHistoryReferences(fields[index + 7])
611
614
  });
612
615
  }
613
616
  return items;
614
617
  }
615
-
616
618
  function gitHistoryChangeKind(statusCode) {
617
- if (statusCode.startsWith('A')) return 'added';
618
- if (statusCode.startsWith('D')) return 'deleted';
619
- if (statusCode.startsWith('R')) return 'renamed';
620
- if (statusCode.startsWith('C')) return 'copied';
621
- if (statusCode.startsWith('T')) return 'type-changed';
622
- return 'modified';
623
- }
624
-
619
+ const code = String(statusCode || "");
620
+ if (code.startsWith("A")) return "added";
621
+ if (code.startsWith("D")) return "deleted";
622
+ if (code.startsWith("R")) return "renamed";
623
+ if (code.startsWith("C")) return "copied";
624
+ if (code.startsWith("T")) return "type-changed";
625
+ return "modified";
626
+ }
625
627
  function gitHistoryChangeLabel(kind) {
626
- if (kind === 'added') return 'A';
627
- if (kind === 'deleted') return 'D';
628
- if (kind === 'renamed') return 'R';
629
- if (kind === 'copied') return 'C';
630
- if (kind === 'type-changed') return 'T';
631
- return 'M';
632
- }
633
-
628
+ if (kind === "added") return "A";
629
+ if (kind === "deleted") return "D";
630
+ if (kind === "renamed") return "R";
631
+ if (kind === "copied") return "C";
632
+ if (kind === "type-changed") return "T";
633
+ return "M";
634
+ }
634
635
  function parseGitHistoryChanges(stdout) {
635
- const fields = String(stdout || '').split('\0');
636
+ const fields = String(stdout || "").split("\0");
636
637
  const items = [];
637
- for (let index = 0; index < fields.length;) {
638
+ for (let index = 0; index < fields.length; ) {
638
639
  const statusCode = fields[index++];
639
640
  if (!statusCode) continue;
640
641
  const kind = gitHistoryChangeKind(statusCode);
641
- if (kind === 'renamed' || kind === 'copied') {
642
- const previousPath = fields[index++] || '';
643
- const filePath = fields[index++] || '';
644
- if (!filePath) continue;
645
- items.push({ path: filePath, previousPath, status: kind, statusLabel: gitHistoryChangeLabel(kind) });
642
+ if (kind === "renamed" || kind === "copied") {
643
+ const previousPath = fields[index++] || "";
644
+ const filePath2 = fields[index++] || "";
645
+ if (!filePath2) continue;
646
+ items.push({ path: filePath2, previousPath, status: kind, statusLabel: gitHistoryChangeLabel(kind) });
646
647
  continue;
647
648
  }
648
- const filePath = fields[index++] || '';
649
+ const filePath = fields[index++] || "";
649
650
  if (!filePath) continue;
650
651
  items.push({ path: filePath, status: kind, statusLabel: gitHistoryChangeLabel(kind) });
651
652
  }
652
653
  return items;
653
654
  }
654
-
655
655
  async function workspaceEntryTypeForGitChange(root, filePath) {
656
656
  try {
657
- const stat = await fsp.lstat(path.join(root, filePath.replace(/\/+$/, '')));
658
- if (stat.isDirectory()) return 'directory';
659
- if (stat.isFile()) return 'file';
660
- if (stat.isSymbolicLink()) return 'symlink';
661
- return 'other';
657
+ const stat = await fsp.lstat(path.join(root, filePath.replace(/\/+$/, "")));
658
+ if (stat.isDirectory()) return "directory";
659
+ if (stat.isFile()) return "file";
660
+ if (stat.isSymbolicLink()) return "symlink";
661
+ return "other";
662
662
  } catch {
663
- return 'file';
663
+ return "file";
664
664
  }
665
665
  }
666
-
667
666
  function gitStatusRank(kind) {
668
- if (kind === 'conflicted') return 6;
669
- if (kind === 'deleted') return 5;
670
- if (kind === 'modified') return 4;
671
- if (kind === 'renamed') return 3;
672
- if (kind === 'added') return 2;
673
- if (kind === 'untracked') return 1;
667
+ if (kind === "conflicted") return 6;
668
+ if (kind === "deleted") return 5;
669
+ if (kind === "modified") return 4;
670
+ if (kind === "renamed") return 3;
671
+ if (kind === "added") return 2;
672
+ if (kind === "untracked") return 1;
674
673
  return 0;
675
674
  }
676
-
677
675
  function isPathWordBoundary(text, index) {
678
676
  if (index === 0) return true;
679
- const previous = text[index - 1] || '';
680
- const current = text[index] || '';
677
+ const previous = text[index - 1] || "";
678
+ const current = text[index] || "";
681
679
  if (/[-_\s./\\]/.test(previous)) return true;
682
680
  return /[a-z0-9]/.test(previous) && /[A-Z]/.test(current);
683
681
  }
684
-
685
682
  function scoreBoundaryMatch(text, normalizedQuery) {
686
683
  if (!text || !normalizedQuery || normalizedQuery.length > 6) return null;
687
684
  let queryIndex = 0;
688
685
  let score = 0;
689
-
690
686
  for (let textIndex = 0; textIndex < text.length && queryIndex < normalizedQuery.length; textIndex += 1) {
691
687
  if (!isPathWordBoundary(text, textIndex)) continue;
692
688
  if (text[textIndex].toLowerCase() !== normalizedQuery[queryIndex]) continue;
693
689
  score += textIndex;
694
690
  queryIndex += 1;
695
691
  }
696
-
697
692
  return queryIndex === normalizedQuery.length ? score : null;
698
693
  }
699
-
700
694
  function scorePathMatch(filePath, query, options = {}) {
701
695
  const pathText = normalizeSearchResultPath(filePath);
702
696
  const normalizedPath = pathText.toLowerCase();
703
- const normalizedQuery = String(query || '').trim().replace(/^\.\/+/, '').toLowerCase();
697
+ const normalizedQuery = String(query || "").trim().replace(/^\.\/+/, "").toLowerCase();
704
698
  if (!normalizedPath || !normalizedQuery) return null;
705
-
706
699
  const fileNameText = path.posix.basename(pathText);
707
700
  const fileName = fileNameText.toLowerCase();
708
701
  if (normalizedPath === normalizedQuery) return 0;
709
702
  if (fileName === normalizedQuery) return 1;
710
-
711
703
  const nameBoundaryScore = scoreBoundaryMatch(fileNameText, normalizedQuery);
712
704
  if (nameBoundaryScore !== null) return 5 + nameBoundaryScore / 100;
713
-
714
705
  const nameIndex = fileName.indexOf(normalizedQuery);
715
706
  if (nameIndex !== -1) return 10 + nameIndex;
716
-
717
707
  if (options.allowPathMatch === false) return null;
718
-
719
708
  const pathIndex = normalizedPath.indexOf(normalizedQuery);
720
709
  if (pathIndex !== -1) return 40 + pathIndex;
721
-
722
710
  if (normalizedQuery.length > 6) return null;
723
-
724
711
  let queryIndex = 0;
725
712
  let score = 80;
726
- for (let pathIndex = 0; pathIndex < normalizedPath.length && queryIndex < normalizedQuery.length; pathIndex += 1) {
727
- if (normalizedPath[pathIndex] !== normalizedQuery[queryIndex]) continue;
728
- score += pathIndex;
713
+ for (let pathIndex2 = 0; pathIndex2 < normalizedPath.length && queryIndex < normalizedQuery.length; pathIndex2 += 1) {
714
+ if (normalizedPath[pathIndex2] !== normalizedQuery[queryIndex]) continue;
715
+ score += pathIndex2;
729
716
  queryIndex += 1;
730
717
  }
731
-
732
718
  return queryIndex === normalizedQuery.length ? score : null;
733
719
  }
734
-
735
720
  function pathSearchEntryRank(entryType) {
736
- return entryType === 'directory' ? 0 : 1;
721
+ return entryType === "directory" ? 0 : 1;
737
722
  }
738
-
739
723
  function pathSearchDepth(filePath) {
740
- return normalizeSearchResultPath(filePath).split('/').filter(Boolean).length;
724
+ return normalizeSearchResultPath(filePath).split("/").filter(Boolean).length;
741
725
  }
742
-
743
726
  function comparePathSearchMatches(a, b) {
744
- return (
745
- a.score - b.score ||
746
- pathSearchDepth(a.path) - pathSearchDepth(b.path) ||
747
- pathSearchEntryRank(a.entryType) - pathSearchEntryRank(b.entryType) ||
748
- a.path.localeCompare(b.path)
749
- );
750
- }
751
-
727
+ return (a.score ?? 0) - (b.score ?? 0) || pathSearchDepth(a.path) - pathSearchDepth(b.path) || pathSearchEntryRank(a.entryType) - pathSearchEntryRank(b.entryType) || a.path.localeCompare(b.path);
728
+ }
752
729
  function pathSearchMatchForPath(filePath, query, entryType, allowPathMatch) {
753
730
  const score = scorePathMatch(filePath, query, { allowPathMatch });
754
731
  if (score === null) return null;
755
732
  return {
756
- path: filePath,
733
+ path: normalizeSearchResultPath(filePath),
757
734
  entryType,
758
- score,
735
+ score
759
736
  };
760
737
  }
761
-
762
738
  function directoryNameSearchScore(directoryPath, query, allowPathMatch) {
763
739
  const pathText = normalizeSearchResultPath(directoryPath);
764
740
  const normalizedPath = pathText.toLowerCase();
765
- const normalizedQuery = String(query || '').trim().replace(/^\.\/+/, '').toLowerCase();
741
+ const normalizedQuery = String(query || "").trim().replace(/^\.\/+/, "").toLowerCase();
766
742
  if (!normalizedPath || !normalizedQuery) return null;
767
-
768
743
  const directoryName = path.posix.basename(pathText).toLowerCase();
769
744
  if (normalizedPath === normalizedQuery) return 0;
770
745
  if (directoryName === normalizedQuery) return 1;
@@ -778,45 +753,40 @@ function directoryNameSearchScore(directoryPath, query, allowPathMatch) {
778
753
  if (pathIndex !== -1) return 40 + pathIndex;
779
754
  return null;
780
755
  }
781
-
782
756
  function directoryNameSearchMatchForPath(directoryPath, query, allowPathMatch) {
783
757
  const score = directoryNameSearchScore(directoryPath, query, allowPathMatch);
784
758
  if (score === null) return null;
785
759
  return {
786
- path: directoryPath,
787
- entryType: 'directory',
788
- score,
760
+ path: normalizeSearchResultPath(directoryPath),
761
+ entryType: "directory",
762
+ score
789
763
  };
790
764
  }
791
-
792
765
  function ancestorDirectoryPaths(filePath) {
793
- const segments = normalizeSearchResultPath(filePath).split('/').filter(Boolean);
766
+ const segments = normalizeSearchResultPath(filePath).split("/").filter(Boolean);
794
767
  const ancestors = [];
795
768
  for (let index = 1; index < segments.length; index += 1) {
796
- ancestors.push(segments.slice(0, index).join('/'));
769
+ ancestors.push(segments.slice(0, index).join("/"));
797
770
  }
798
771
  return ancestors;
799
772
  }
800
-
801
773
  function pathSearchMatchesForFile(filePath, query, includeIgnored = false) {
802
774
  const allowPathMatch = isLikelyPathQuery(query);
803
775
  const matches = [];
804
- const fileMatch = pathSearchMatchForPath(filePath, query, 'file', allowPathMatch);
776
+ const fileMatch = pathSearchMatchForPath(filePath, query, "file", allowPathMatch);
805
777
  if (fileMatch) matches.push(fileMatch);
806
778
  ancestorDirectoryPaths(filePath).forEach((directoryPath) => {
807
779
  if (isSearchIgnoredRelativePath(directoryPath, includeIgnored)) return;
808
- const directoryMatch = pathSearchMatchForPath(directoryPath, query, 'directory', allowPathMatch);
780
+ const directoryMatch = pathSearchMatchForPath(directoryPath, query, "directory", allowPathMatch);
809
781
  if (directoryMatch) matches.push(directoryMatch);
810
782
  });
811
783
  return matches;
812
784
  }
813
-
814
785
  function isLikelyPathQuery(query) {
815
- return /[./\\]/.test(String(query || '').trim());
786
+ return /[./\\]/.test(String(query || "").trim());
816
787
  }
817
-
818
788
  function dedupePathMatches(matches, limit) {
819
- const seen = new Set();
789
+ const seen = /* @__PURE__ */ new Set();
820
790
  const deduped = [];
821
791
  for (const match of matches) {
822
792
  if (!match || seen.has(match.path)) continue;
@@ -826,110 +796,92 @@ function dedupePathMatches(matches, limit) {
826
796
  }
827
797
  return deduped;
828
798
  }
829
-
830
799
  function parseGitStatus(stdout) {
831
- const records = String(stdout || '').split('\0').filter(Boolean);
832
- const statusByPath = new Map();
833
-
800
+ const records = String(stdout || "").split("\0").filter(Boolean);
801
+ const statusByPath = /* @__PURE__ */ new Map();
834
802
  for (let index = 0; index < records.length; index += 1) {
835
803
  const record = records[index];
836
804
  if (record.length < 4) continue;
837
-
838
805
  const statusCode = record.slice(0, 2);
839
806
  const filePath = normalizeGitStatusPath(record.slice(3));
840
807
  if (!filePath) continue;
841
-
842
808
  const kind = gitStatusKind(statusCode);
843
- const previousPath = statusCode.includes('R') || statusCode.includes('C')
844
- ? normalizeGitStatusPath(records[index + 1])
845
- : '';
809
+ const previousPath = statusCode.includes("R") || statusCode.includes("C") ? normalizeGitStatusPath(records[index + 1]) : "";
846
810
  statusByPath.set(filePath, {
847
811
  kind,
848
812
  label: gitStatusLabel(kind),
849
- ...(previousPath ? { previousPath } : {}),
813
+ ...previousPath ? { previousPath } : {}
850
814
  });
851
-
852
- if (statusCode.includes('R') || statusCode.includes('C')) {
815
+ if (statusCode.includes("R") || statusCode.includes("C")) {
853
816
  index += 1;
854
817
  }
855
818
  }
856
-
857
819
  return statusByPath;
858
820
  }
859
-
860
821
  function parseGitBlamePorcelain(stdout) {
861
- const lines = String(stdout || '').split('\n');
822
+ const lines = String(stdout || "").split("\n");
862
823
  const blameLines = [];
863
824
  let index = 0;
864
-
865
825
  while (index < lines.length) {
866
826
  const header = lines[index];
867
- const match = /^(\^?[0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(header || '');
827
+ const match = /^(\^?[0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(header || "");
868
828
  if (!match) {
869
829
  index += 1;
870
830
  continue;
871
831
  }
872
-
873
832
  const entry = {
874
- commit: match[1].replace(/^\^/, ''),
833
+ commit: match[1].replace(/^\^/, ""),
875
834
  originalLineNumber: Number(match[2]),
876
835
  lineNumber: Number(match[3]),
877
- author: '',
878
- authorMail: '',
836
+ author: "",
837
+ authorMail: "",
879
838
  authorTime: null,
880
- authorTimeIso: '',
881
- summary: '',
882
- content: '',
839
+ authorTimeIso: "",
840
+ summary: "",
841
+ content: ""
883
842
  };
884
843
  index += 1;
885
-
886
844
  while (index < lines.length) {
887
845
  const line = lines[index];
888
846
  index += 1;
889
- if (line.startsWith('\t')) {
847
+ if (line.startsWith(" ")) {
890
848
  entry.content = line.slice(1);
891
849
  break;
892
850
  }
893
-
894
- const separator = line.indexOf(' ');
851
+ const separator = line.indexOf(" ");
895
852
  const fieldName = separator === -1 ? line : line.slice(0, separator);
896
- const value = separator === -1 ? '' : line.slice(separator + 1);
897
- if (fieldName === 'author') entry.author = value;
898
- if (fieldName === 'author-mail') entry.authorMail = value.replace(/^<|>$/g, '');
899
- if (fieldName === 'author-time') {
853
+ const value = separator === -1 ? "" : line.slice(separator + 1);
854
+ if (fieldName === "author") entry.author = value;
855
+ if (fieldName === "author-mail") entry.authorMail = value.replace(/^<|>$/g, "");
856
+ if (fieldName === "author-time") {
900
857
  const time = Number(value);
901
858
  entry.authorTime = Number.isFinite(time) ? time : null;
902
- entry.authorTimeIso = Number.isFinite(time) ? new Date(time * 1000).toISOString() : '';
859
+ entry.authorTimeIso = Number.isFinite(time) ? new Date(time * 1e3).toISOString() : "";
903
860
  }
904
- if (fieldName === 'summary') entry.summary = value;
861
+ if (fieldName === "summary") entry.summary = value;
905
862
  }
906
-
907
863
  const uncommitted = /^0+$/.test(entry.commit);
908
864
  blameLines.push({
909
865
  ...entry,
910
- shortCommit: uncommitted ? 'uncommitted' : entry.commit.slice(0, 8),
911
- uncommitted,
866
+ shortCommit: uncommitted ? "uncommitted" : entry.commit.slice(0, 8),
867
+ uncommitted
912
868
  });
913
869
  }
914
-
915
870
  return blameLines;
916
871
  }
917
-
918
872
  function parseUnifiedDiffHunks(patch) {
919
- const lines = String(patch || '').split('\n');
873
+ const lines = String(patch || "").split("\n");
920
874
  const hunks = [];
921
875
  let hunk = null;
922
-
923
876
  const finishHunk = () => {
924
877
  if (hunk) {
925
878
  hunks.push({
926
879
  ...hunk,
927
- patch: hunk.patchLines.join('\n'),
880
+ patch: hunk.patchLines.join("\n")
928
881
  });
929
882
  }
930
883
  hunk = null;
931
884
  };
932
-
933
885
  for (const line of lines) {
934
886
  const match = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)$/.exec(line);
935
887
  if (match) {
@@ -940,21 +892,18 @@ function parseUnifiedDiffHunks(patch) {
940
892
  oldLines: Number(match[2] || 1),
941
893
  newStart: Number(match[3]),
942
894
  newLines: Number(match[4] || 1),
943
- heading: match[5] ? match[5].trim() : '',
944
- patchLines: [line],
895
+ heading: match[5] ? match[5].trim() : "",
896
+ patchLines: [line]
945
897
  };
946
898
  continue;
947
899
  }
948
-
949
900
  if (hunk) {
950
901
  hunk.patchLines.push(line);
951
902
  }
952
903
  }
953
-
954
904
  finishHunk();
955
905
  return hunks;
956
906
  }
957
-
958
907
  function intralineRanges(leftText, rightText) {
959
908
  const changes = diffChars(leftText, rightText, { timeout: 100 });
960
909
  if (!changes) return {};
@@ -978,28 +927,27 @@ function intralineRanges(leftText, rightText) {
978
927
  rightOffset += length;
979
928
  }
980
929
  return {
981
- ...(left.length ? { left } : {}),
982
- ...(right.length ? { right } : {}),
930
+ ...left.length ? { left } : {},
931
+ ...right.length ? { right } : {}
983
932
  };
984
933
  }
985
-
986
934
  function parseUnifiedDiffRows(patch) {
987
- return parseUnifiedDiffHunks(patch).map(hunk => {
935
+ return parseUnifiedDiffHunks(patch).map((hunk) => {
988
936
  let oldLine = hunk.oldStart;
989
937
  let newLine = hunk.newStart;
990
938
  const rows = [];
991
939
  let previousCell = null;
992
940
  for (const line of hunk.patchLines.slice(1)) {
993
- if (line.startsWith('\')) {
941
+ if (line.startsWith("\")) {
994
942
  if (previousCell) previousCell.missingNewlineAtEnd = true;
995
943
  continue;
996
944
  }
997
- if (line.startsWith(' ')) {
945
+ if (line.startsWith(" ")) {
998
946
  const text = line.slice(1);
999
947
  const row = {
1000
- kind: 'context',
948
+ kind: "context",
1001
949
  left: { line: oldLine, text },
1002
- right: { line: newLine, text },
950
+ right: { line: newLine, text }
1003
951
  };
1004
952
  rows.push(row);
1005
953
  previousCell = row.right;
@@ -1007,40 +955,41 @@ function parseUnifiedDiffRows(patch) {
1007
955
  newLine++;
1008
956
  continue;
1009
957
  }
1010
- if (line.startsWith('-')) {
1011
- const row = { kind: 'deleted', left: { line: oldLine, text: line.slice(1) } };
958
+ if (line.startsWith("-")) {
959
+ const row = { kind: "deleted", left: { line: oldLine, text: line.slice(1) } };
1012
960
  rows.push(row);
1013
961
  previousCell = row.left;
1014
962
  oldLine++;
1015
963
  continue;
1016
964
  }
1017
- if (line.startsWith('+')) {
1018
- const row = { kind: 'added', right: { line: newLine, text: line.slice(1) } };
965
+ if (line.startsWith("+")) {
966
+ const row = { kind: "added", right: { line: newLine, text: line.slice(1) } };
1019
967
  rows.push(row);
1020
968
  previousCell = row.right;
1021
969
  newLine++;
1022
970
  }
1023
971
  }
1024
972
  const pairedRows = [];
1025
- for (let index = 0; index < rows.length;) {
1026
- if (rows[index].kind !== 'deleted') {
973
+ for (let index = 0; index < rows.length; ) {
974
+ if (rows[index].kind !== "deleted") {
1027
975
  pairedRows.push(rows[index]);
1028
976
  index += 1;
1029
977
  continue;
1030
978
  }
1031
979
  const deleted = [];
1032
- while (index < rows.length && rows[index].kind === 'deleted') deleted.push(rows[index++]);
980
+ while (index < rows.length && rows[index].kind === "deleted") deleted.push(rows[index++]);
1033
981
  const added = [];
1034
- while (index < rows.length && rows[index].kind === 'added') added.push(rows[index++]);
982
+ while (index < rows.length && rows[index].kind === "added") added.push(rows[index++]);
1035
983
  const paired = Math.min(deleted.length, added.length);
1036
984
  for (let pairIndex = 0; pairIndex < paired; pairIndex += 1) {
1037
985
  const left = deleted[pairIndex].left;
1038
986
  const right = added[pairIndex].right;
987
+ if (!left || !right) continue;
1039
988
  const intraline = intralineRanges(left.text, right.text);
1040
989
  pairedRows.push({
1041
- kind: 'changed',
1042
- left: { ...left, ...(intraline.left ? { intraline: intraline.left } : {}) },
1043
- right: { ...right, ...(intraline.right ? { intraline: intraline.right } : {}) },
990
+ kind: "changed",
991
+ left: { ...left, ...intraline.left ? { intraline: intraline.left } : {} },
992
+ right: { ...right, ...intraline.right ? { intraline: intraline.right } : {} }
1044
993
  });
1045
994
  }
1046
995
  pairedRows.push(...deleted.slice(paired));
@@ -1052,55 +1001,48 @@ function parseUnifiedDiffRows(patch) {
1052
1001
  oldLines: hunk.oldLines,
1053
1002
  newStart: hunk.newStart,
1054
1003
  newLines: hunk.newLines,
1055
- rows: pairedRows,
1004
+ rows: pairedRows
1056
1005
  };
1057
1006
  });
1058
1007
  }
1059
-
1060
1008
  function lineInUnifiedDiffHunk(hunk, side, lineNumber) {
1061
- const start = side === 'old' ? hunk.oldStart : hunk.newStart;
1062
- const count = side === 'old' ? hunk.oldLines : hunk.newLines;
1009
+ const start = side === "old" ? hunk.oldStart : hunk.newStart;
1010
+ const count = side === "old" ? hunk.oldLines : hunk.newLines;
1063
1011
  const end = Math.max(start, start + count - 1);
1064
1012
  return lineNumber >= start && lineNumber <= end;
1065
1013
  }
1066
-
1067
1014
  function selectUnifiedDiffHunk(patch, side, lineNumber) {
1068
1015
  const hunks = parseUnifiedDiffHunks(patch);
1069
- return hunks.find(hunk => lineInUnifiedDiffHunk(hunk, side, lineNumber)) || null;
1016
+ return hunks.find((hunk) => lineInUnifiedDiffHunk(hunk, side, lineNumber)) || null;
1070
1017
  }
1071
-
1072
1018
  function createAddedFileLineChangesHunk(content, lineNumber, contextLines = 20) {
1073
- const lines = String(content || '').split('\n');
1074
- if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();
1019
+ const lines = String(content || "").split("\n");
1020
+ if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
1075
1021
  if (lines.length === 0) return null;
1076
-
1077
1022
  const startIndex = Math.max(0, lineNumber - contextLines - 1);
1078
1023
  const endIndex = Math.min(lines.length, lineNumber + contextLines);
1079
1024
  const newStart = startIndex + 1;
1080
1025
  const newLines = endIndex - startIndex;
1081
1026
  const patchLines = [
1082
1027
  `@@ -0,0 +${newStart},${newLines} @@`,
1083
- ...lines.slice(startIndex, endIndex).map(line => `+${line}`),
1028
+ ...lines.slice(startIndex, endIndex).map((line) => `+${line}`)
1084
1029
  ];
1085
-
1086
1030
  return {
1087
1031
  header: patchLines[0],
1088
1032
  oldStart: 0,
1089
1033
  oldLines: 0,
1090
1034
  newStart,
1091
1035
  newLines,
1092
- heading: '',
1093
- patch: patchLines.join('\n'),
1036
+ heading: "",
1037
+ patch: patchLines.join("\n")
1094
1038
  };
1095
1039
  }
1096
-
1097
1040
  function strongestStatus(current, next) {
1098
1041
  if (!current) return next;
1099
1042
  return gitStatusRank(next.kind) > gitStatusRank(current.kind) ? next : current;
1100
1043
  }
1101
-
1102
1044
  function buildDescendantGitStatusByDirectory(statusByPath) {
1103
- const statusByDirectory = new Map();
1045
+ const statusByDirectory = /* @__PURE__ */ new Map();
1104
1046
  statusByPath.forEach((status, statusPath) => {
1105
1047
  let directory = parentDirectory(statusPath);
1106
1048
  while (directory) {
@@ -1110,8 +1052,36 @@ function buildDescendantGitStatusByDirectory(statusByPath) {
1110
1052
  });
1111
1053
  return statusByDirectory;
1112
1054
  }
1113
-
1114
1055
  class WorkspaceFileService {
1056
+ blameTimeoutMs;
1057
+ bundledRipgrepModule;
1058
+ bundledRipgrepModulePath;
1059
+ commandRunner;
1060
+ diffMaxBuffer;
1061
+ diffTimeoutMs;
1062
+ disposed;
1063
+ flushWorkspaceWrites;
1064
+ gitHistoryMaxBuffer;
1065
+ gitHistoryTimeoutMs;
1066
+ gitPath;
1067
+ gitStatusCache;
1068
+ gitStatusCacheTtlMs;
1069
+ gitStatusInlineTimeoutMs;
1070
+ gitStatusTimeoutMs;
1071
+ maxFileSize;
1072
+ maxPreviewFileSize;
1073
+ maxWriteSize;
1074
+ mutationQueues;
1075
+ ownsCommandRunner;
1076
+ rgFallbackPath;
1077
+ rgPath;
1078
+ searchLimit;
1079
+ searchTimeoutMs;
1080
+ watchDepth;
1081
+ watchOptions;
1082
+ watcherLifecycleGeneration;
1083
+ watcherSubscriptionGeneration;
1084
+ watchers;
1115
1085
  constructor(options = {}) {
1116
1086
  this.maxFileSize = options.maxFileSize || DEFAULT_MAX_FILE_SIZE;
1117
1087
  this.maxWriteSize = options.maxWriteSize || DEFAULT_MAX_WRITE_SIZE;
@@ -1124,39 +1094,38 @@ class WorkspaceFileService {
1124
1094
  this.gitHistoryTimeoutMs = options.gitHistoryTimeoutMs ?? DEFAULT_GIT_HISTORY_TIMEOUT_MS;
1125
1095
  this.gitHistoryMaxBuffer = options.gitHistoryMaxBuffer ?? DEFAULT_GIT_HISTORY_MAX_BUFFER;
1126
1096
  const bundledRipgrepPath = resolveBundledRipgrepPath();
1127
- this.rgPath = options.rgPath || process.env.FARMING_RG_BIN || bundledRipgrepPath || 'rg';
1128
- this.rgFallbackPath = options.rgFallbackPath ?? (this.rgPath === 'rg' ? bundledRipgrepPath : 'rg');
1097
+ this.rgPath = options.rgPath || process.env.FARMING_RG_BIN || bundledRipgrepPath || "rg";
1098
+ this.rgFallbackPath = options.rgFallbackPath ?? (this.rgPath === "rg" ? bundledRipgrepPath : "rg");
1129
1099
  this.bundledRipgrepModulePath = options.bundledRipgrepModulePath ?? resolveBundledRipgrepModulePath();
1130
1100
  this.bundledRipgrepModule = null;
1131
- this.gitPath = options.gitPath || 'git';
1101
+ this.gitPath = options.gitPath || "git";
1132
1102
  this.gitStatusCacheTtlMs = options.gitStatusCacheTtlMs ?? DEFAULT_GIT_STATUS_CACHE_TTL_MS;
1133
1103
  this.gitStatusInlineTimeoutMs = options.gitStatusInlineTimeoutMs ?? DEFAULT_GIT_STATUS_INLINE_TIMEOUT_MS;
1134
1104
  this.gitStatusTimeoutMs = options.gitStatusTimeoutMs ?? DEFAULT_GIT_STATUS_TIMEOUT_MS;
1135
- this.gitStatusCache = new Map();
1136
- this.mutationQueues = new Map();
1105
+ this.gitStatusCache = /* @__PURE__ */ new Map();
1106
+ this.mutationQueues = /* @__PURE__ */ new Map();
1137
1107
  this.flushWorkspaceWrites = options.flushWorkspaceWrites !== false;
1138
- this.watchers = new Map();
1108
+ this.watchers = /* @__PURE__ */ new Map();
1139
1109
  this.watcherSubscriptionGeneration = 0;
1140
1110
  this.watcherLifecycleGeneration = 0;
1141
1111
  this.disposed = false;
1142
1112
  this.watchOptions = options.watchOptions || {};
1143
- this.watchDepth = Number.isFinite(options.watchDepth) ? Math.max(0, options.watchDepth) : DEFAULT_WATCH_DEPTH;
1113
+ this.watchDepth = Number.isFinite(options.watchDepth) ? Math.max(0, options.watchDepth ?? 0) : DEFAULT_WATCH_DEPTH;
1144
1114
  this.commandRunner = options.commandRunner || new CommandRunner(options.commandRunnerOptions);
1145
1115
  this.ownsCommandRunner = !options.commandRunner;
1146
1116
  }
1147
-
1148
1117
  execFile(command, args, options = {}) {
1149
1118
  return this.commandRunner.run(command, args, {
1150
1119
  maxBuffer: 2 * 1024 * 1024,
1151
- ...options,
1120
+ ...options
1152
1121
  });
1153
1122
  }
1154
-
1155
1123
  async runWorkspaceMutation(workspaceRoot, operation) {
1156
1124
  const root = await this.resolveRoot(workspaceRoot);
1157
1125
  const previous = this.mutationQueues.get(root) || Promise.resolve();
1158
- let release;
1159
- const current = new Promise(resolve => {
1126
+ let release = () => {
1127
+ };
1128
+ const current = new Promise((resolve) => {
1160
1129
  release = resolve;
1161
1130
  });
1162
1131
  const tail = previous.then(() => current);
@@ -1171,18 +1140,17 @@ class WorkspaceFileService {
1171
1140
  }
1172
1141
  }
1173
1142
  }
1174
-
1175
1143
  async waitForWorkspaceMutations(workspaceRoot) {
1176
1144
  const root = await this.resolveRoot(workspaceRoot);
1177
1145
  const pending = this.mutationQueues.get(root);
1178
1146
  if (pending) await pending;
1179
1147
  return root;
1180
1148
  }
1181
-
1182
1149
  async execRipgrep(args, options = {}) {
1183
1150
  try {
1184
1151
  return await this.execFile(this.rgPath, args, options);
1185
- } catch (error) {
1152
+ } catch (caught) {
1153
+ const error = processError(caught);
1186
1154
  if (!isCommandUnavailable(error) || !this.rgFallbackPath || this.rgFallbackPath === this.rgPath) {
1187
1155
  if (isCommandUnavailable(error)) {
1188
1156
  return this.runBundledRipgrep(args, options);
@@ -1192,7 +1160,8 @@ class WorkspaceFileService {
1192
1160
  this.rgPath = this.rgFallbackPath;
1193
1161
  try {
1194
1162
  return await this.execFile(this.rgPath, args, options);
1195
- } catch (fallbackError) {
1163
+ } catch (caughtFallback) {
1164
+ const fallbackError = processError(caughtFallback);
1196
1165
  if (isCommandUnavailable(fallbackError)) {
1197
1166
  return this.runBundledRipgrep(args, options);
1198
1167
  }
@@ -1200,11 +1169,10 @@ class WorkspaceFileService {
1200
1169
  }
1201
1170
  }
1202
1171
  }
1203
-
1204
1172
  async loadBundledRipgrep() {
1205
1173
  if (!this.bundledRipgrepModulePath) {
1206
- const error = new Error('bundled ripgrep is unavailable');
1207
- error.code = 'ENOENT';
1174
+ const error = new Error("bundled ripgrep is unavailable");
1175
+ error.code = "ENOENT";
1208
1176
  throw error;
1209
1177
  }
1210
1178
  if (!this.bundledRipgrepModule) {
@@ -1212,29 +1180,26 @@ class WorkspaceFileService {
1212
1180
  }
1213
1181
  return this.bundledRipgrepModule;
1214
1182
  }
1215
-
1216
1183
  async runBundledRipgrep(args, options = {}) {
1217
1184
  const { ripgrep } = await this.loadBundledRipgrep();
1218
1185
  const cwd = options.cwd || process.cwd();
1219
1186
  const run = ripgrep(args, {
1220
1187
  buffer: true,
1221
1188
  env: options.env || process.env,
1222
- preopens: { '.': cwd },
1189
+ preopens: { ".": cwd }
1223
1190
  });
1224
- const result = await (options.timeout
1225
- ? Promise.race([
1226
- run,
1227
- new Promise((_, reject) => {
1228
- setTimeout(() => {
1229
- const error = new Error('bundled ripgrep timed out');
1230
- error.code = 'ETIMEDOUT';
1231
- reject(error);
1232
- }, options.timeout);
1233
- }),
1234
- ])
1235
- : run);
1236
- const stdout = result.stdout || '';
1237
- const stderr = result.stderr || '';
1191
+ const result = await (options.timeout ? Promise.race([
1192
+ run,
1193
+ new Promise((_, reject) => {
1194
+ setTimeout(() => {
1195
+ const error = new Error("bundled ripgrep timed out");
1196
+ error.code = "ETIMEDOUT";
1197
+ reject(error);
1198
+ }, options.timeout);
1199
+ })
1200
+ ]) : run);
1201
+ const stdout = result.stdout || "";
1202
+ const stderr = result.stderr || "";
1238
1203
  if (result.code && result.code !== 0) {
1239
1204
  const error = new Error(stderr || `ripgrep exited with code ${result.code}`);
1240
1205
  error.code = result.code;
@@ -1244,44 +1209,41 @@ class WorkspaceFileService {
1244
1209
  }
1245
1210
  return { stdout, stderr };
1246
1211
  }
1247
-
1248
1212
  async listSearchFiles(root, searchPath, timeout) {
1249
1213
  try {
1250
1214
  return await this.execFile(this.gitPath, [
1251
- '-C',
1215
+ "-C",
1252
1216
  root,
1253
- 'ls-files',
1254
- '-co',
1255
- '--exclude-standard',
1256
- '--',
1257
- searchPath,
1217
+ "ls-files",
1218
+ "-co",
1219
+ "--exclude-standard",
1220
+ "--",
1221
+ searchPath
1258
1222
  ], { cwd: root, timeout, maxBuffer: SEARCH_FILE_LIST_MAX_BUFFER });
1259
1223
  } catch {
1260
1224
  return this.execRipgrep([
1261
- '--files',
1262
- '--hidden',
1225
+ "--files",
1226
+ "--hidden",
1263
1227
  ...searchExcludeGlobArgs(),
1264
- searchPath,
1228
+ searchPath
1265
1229
  ], { cwd: root, timeout, maxBuffer: SEARCH_FILE_LIST_MAX_BUFFER });
1266
1230
  }
1267
1231
  }
1268
-
1269
1232
  async collectGitIgnoredPathMatchCandidates(root, searchPath, query, limit, timeout) {
1270
1233
  const candidateLimit = Math.max(PATH_SEARCH_MIN_CANDIDATES, limit * PATH_SEARCH_CANDIDATE_MULTIPLIER);
1271
1234
  const { stdout } = await this.execFile(this.gitPath, [
1272
- '-C',
1235
+ "-C",
1273
1236
  root,
1274
- 'ls-files',
1275
- '--others',
1276
- '--ignored',
1277
- '--exclude-standard',
1278
- ...gitSearchExcludePathspecArgs(true, searchPath),
1237
+ "ls-files",
1238
+ "--others",
1239
+ "--ignored",
1240
+ "--exclude-standard",
1241
+ ...gitSearchExcludePathspecArgs(true, searchPath)
1279
1242
  ], { cwd: root, timeout, maxBuffer: SEARCH_FILE_LIST_MAX_BUFFER });
1280
1243
  const scoredMatches = [];
1281
- const seenMatchPaths = new Set();
1244
+ const seenMatchPaths = /* @__PURE__ */ new Set();
1282
1245
  let truncated = false;
1283
-
1284
- for (const line of stdout.split('\n')) {
1246
+ for (const line of String(stdout).split("\n")) {
1285
1247
  const filePath = normalizeSearchResultPath(line);
1286
1248
  if (!filePath || isSearchIgnoredRelativePath(filePath, true)) continue;
1287
1249
  for (const match of pathSearchMatchesForFile(filePath, query, true)) {
@@ -1295,44 +1257,38 @@ class WorkspaceFileService {
1295
1257
  }
1296
1258
  if (truncated) break;
1297
1259
  }
1298
-
1299
1260
  scoredMatches.sort(comparePathSearchMatches);
1300
1261
  return {
1301
- matches: scoredMatches.slice(0, limit).map(match => ({
1302
- kind: 'path',
1262
+ matches: scoredMatches.slice(0, limit).map((match) => ({
1263
+ kind: "path",
1303
1264
  entryType: match.entryType,
1304
1265
  path: match.path,
1305
1266
  lineNumber: 1,
1306
- lines: '',
1307
- ranges: [],
1267
+ lines: "",
1268
+ ranges: []
1308
1269
  })),
1309
- truncated,
1270
+ truncated
1310
1271
  };
1311
1272
  }
1312
-
1313
1273
  streamPathMatches(command, root, searchPath, query, limit, timeout, stopAtLimit = false) {
1314
1274
  const args = [
1315
- '--files',
1316
- '--hidden',
1275
+ "--files",
1276
+ "--hidden",
1317
1277
  ...searchExcludeGlobArgs(),
1318
- searchPath,
1278
+ searchPath
1319
1279
  ];
1320
- const candidateLimit = stopAtLimit
1321
- ? limit
1322
- : Math.max(PATH_SEARCH_MIN_CANDIDATES, limit * PATH_SEARCH_CANDIDATE_MULTIPLIER);
1323
-
1280
+ const candidateLimit = stopAtLimit ? limit : Math.max(PATH_SEARCH_MIN_CANDIDATES, limit * PATH_SEARCH_CANDIDATE_MULTIPLIER);
1324
1281
  return new Promise((resolve, reject) => {
1325
1282
  const child = spawn(command, args, {
1326
1283
  cwd: root,
1327
- stdio: ['ignore', 'pipe', 'pipe'],
1284
+ stdio: ["ignore", "pipe", "pipe"]
1328
1285
  });
1329
- let pending = '';
1330
- let stderr = '';
1286
+ let pending = "";
1287
+ let stderr = "";
1331
1288
  let settled = false;
1332
1289
  let truncated = false;
1333
1290
  const scoredMatches = [];
1334
- const seenMatchPaths = new Set();
1335
-
1291
+ const seenMatchPaths = /* @__PURE__ */ new Set();
1336
1292
  const settle = (error) => {
1337
1293
  if (settled) return;
1338
1294
  settled = true;
@@ -1343,24 +1299,22 @@ class WorkspaceFileService {
1343
1299
  }
1344
1300
  scoredMatches.sort(comparePathSearchMatches);
1345
1301
  resolve({
1346
- matches: scoredMatches.slice(0, limit).map(match => ({
1347
- kind: 'path',
1302
+ matches: scoredMatches.slice(0, limit).map((match) => ({
1303
+ kind: "path",
1348
1304
  entryType: match.entryType,
1349
1305
  path: match.path,
1350
1306
  lineNumber: 1,
1351
- lines: '',
1352
- ranges: [],
1307
+ lines: "",
1308
+ ranges: []
1353
1309
  })),
1354
- truncated,
1310
+ truncated
1355
1311
  });
1356
1312
  };
1357
-
1358
1313
  const stopEarly = () => {
1359
1314
  truncated = true;
1360
1315
  child.kill();
1361
1316
  settle();
1362
1317
  };
1363
-
1364
1318
  const processLine = (line) => {
1365
1319
  const filePath = normalizeSearchResultPath(line);
1366
1320
  if (!filePath || isSearchIgnoredRelativePath(filePath)) return;
@@ -1374,38 +1328,33 @@ class WorkspaceFileService {
1374
1328
  }
1375
1329
  }
1376
1330
  };
1377
-
1378
1331
  const timer = setTimeout(() => {
1379
1332
  truncated = true;
1380
1333
  child.kill();
1381
1334
  settle();
1382
1335
  }, timeout);
1383
-
1384
- child.stdout.on('data', chunk => {
1336
+ child.stdout.on("data", (chunk) => {
1385
1337
  if (settled) return;
1386
- pending += String(chunk || '');
1387
- const lines = pending.split('\n');
1388
- pending = lines.pop() || '';
1338
+ pending += String(chunk || "");
1339
+ const lines = pending.split("\n");
1340
+ pending = lines.pop() || "";
1389
1341
  for (const line of lines) {
1390
1342
  processLine(line);
1391
1343
  if (settled) return;
1392
1344
  }
1393
1345
  });
1394
-
1395
- child.stderr.on('data', chunk => {
1396
- stderr += String(chunk || '');
1346
+ child.stderr.on("data", (chunk) => {
1347
+ stderr += String(chunk || "");
1397
1348
  });
1398
-
1399
- child.on('error', error => {
1349
+ child.on("error", (error) => {
1400
1350
  settle(error);
1401
1351
  });
1402
-
1403
- child.on('close', (code, signal) => {
1352
+ child.on("close", (code, signal) => {
1404
1353
  if (settled) return;
1405
1354
  if (pending) processLine(pending);
1406
1355
  if (settled) return;
1407
- if (code && code !== 0 && signal !== 'SIGTERM') {
1408
- const error = new Error(stderr || 'path search failed');
1356
+ if (code && code !== 0 && signal !== "SIGTERM") {
1357
+ const error = new Error(stderr || "path search failed");
1409
1358
  error.code = code;
1410
1359
  error.signal = signal;
1411
1360
  error.stderr = stderr;
@@ -1416,11 +1365,11 @@ class WorkspaceFileService {
1416
1365
  });
1417
1366
  });
1418
1367
  }
1419
-
1420
1368
  async collectPathMatchCandidates(root, searchPath, query, limit, timeout, stopAtLimit = false) {
1421
1369
  try {
1422
1370
  return await this.streamPathMatches(this.rgPath, root, searchPath, query, limit, timeout, stopAtLimit);
1423
- } catch (error) {
1371
+ } catch (caught) {
1372
+ const error = processError(caught);
1424
1373
  if (!isCommandUnavailable(error) || !this.rgFallbackPath || this.rgFallbackPath === this.rgPath) {
1425
1374
  if (isCommandUnavailable(error)) {
1426
1375
  return this.collectBundledPathMatchCandidates(root, searchPath, query, limit, timeout, stopAtLimit);
@@ -1430,7 +1379,8 @@ class WorkspaceFileService {
1430
1379
  this.rgPath = this.rgFallbackPath;
1431
1380
  try {
1432
1381
  return await this.streamPathMatches(this.rgPath, root, searchPath, query, limit, timeout, stopAtLimit);
1433
- } catch (fallbackError) {
1382
+ } catch (caughtFallback) {
1383
+ const fallbackError = processError(caughtFallback);
1434
1384
  if (isCommandUnavailable(fallbackError)) {
1435
1385
  return this.collectBundledPathMatchCandidates(root, searchPath, query, limit, timeout, stopAtLimit);
1436
1386
  }
@@ -1438,19 +1388,17 @@ class WorkspaceFileService {
1438
1388
  }
1439
1389
  }
1440
1390
  }
1441
-
1442
1391
  async collectDirectoryNameMatchCandidates(root, searchPath, query, limit, timeout) {
1443
1392
  const startedAt = Date.now();
1444
1393
  const allowPathMatch = isLikelyPathQuery(query);
1445
1394
  const candidateLimit = Math.max(PATH_SEARCH_MIN_CANDIDATES, limit * PATH_SEARCH_CANDIDATE_MULTIPLIER);
1446
- const startRelativePath = searchPath === '.' ? '' : normalizeSearchResultPath(searchPath);
1447
- const startDirectory = path.resolve(root, startRelativePath || '.');
1395
+ const startRelativePath = searchPath === "." ? "" : normalizeSearchResultPath(searchPath);
1396
+ const startDirectory = path.resolve(root, startRelativePath || ".");
1448
1397
  const queue = [{ target: startDirectory, relativePath: startRelativePath }];
1449
1398
  const scoredMatches = [];
1450
- const seenMatchPaths = new Set();
1399
+ const seenMatchPaths = /* @__PURE__ */ new Set();
1451
1400
  let visited = 0;
1452
1401
  let truncated = false;
1453
-
1454
1402
  while (queue.length > 0) {
1455
1403
  if (Date.now() - startedAt > timeout) {
1456
1404
  truncated = true;
@@ -1460,18 +1408,15 @@ class WorkspaceFileService {
1460
1408
  truncated = true;
1461
1409
  break;
1462
1410
  }
1463
-
1464
1411
  const current = queue.shift();
1465
1412
  if (!current || shouldPruneDirectoryNameSearch(current.relativePath)) continue;
1466
1413
  visited += 1;
1467
-
1468
1414
  let entries;
1469
1415
  try {
1470
1416
  entries = await fsp.readdir(current.target, { withFileTypes: true });
1471
1417
  } catch {
1472
1418
  continue;
1473
1419
  }
1474
-
1475
1420
  for (const entry of entries) {
1476
1421
  if (!entry.isDirectory()) continue;
1477
1422
  const childRelativePath = normalizeSearchResultPath(
@@ -1492,35 +1437,31 @@ class WorkspaceFileService {
1492
1437
  }
1493
1438
  if (truncated) break;
1494
1439
  }
1495
-
1496
1440
  scoredMatches.sort(comparePathSearchMatches);
1497
1441
  return {
1498
- matches: scoredMatches.slice(0, limit).map(match => ({
1499
- kind: 'path',
1500
- entryType: 'directory',
1442
+ matches: scoredMatches.slice(0, limit).map((match) => ({
1443
+ kind: "path",
1444
+ entryType: "directory",
1501
1445
  path: match.path,
1502
1446
  lineNumber: 1,
1503
- lines: '',
1504
- ranges: [],
1447
+ lines: "",
1448
+ ranges: []
1505
1449
  })),
1506
- truncated,
1450
+ truncated
1507
1451
  };
1508
1452
  }
1509
-
1510
1453
  async collectBundledPathMatchCandidates(root, searchPath, query, limit, timeout, stopAtLimit = false) {
1511
- const candidateLimit = stopAtLimit
1512
- ? limit
1513
- : Math.max(PATH_SEARCH_MIN_CANDIDATES, limit * PATH_SEARCH_CANDIDATE_MULTIPLIER);
1454
+ const candidateLimit = stopAtLimit ? limit : Math.max(PATH_SEARCH_MIN_CANDIDATES, limit * PATH_SEARCH_CANDIDATE_MULTIPLIER);
1514
1455
  const { stdout } = await this.runBundledRipgrep([
1515
- '--files',
1516
- '--hidden',
1456
+ "--files",
1457
+ "--hidden",
1517
1458
  ...searchExcludeGlobArgs(),
1518
- searchPath,
1459
+ searchPath
1519
1460
  ], { cwd: root, timeout, maxBuffer: SEARCH_FILE_LIST_MAX_BUFFER });
1520
1461
  const scoredMatches = [];
1521
- const seenMatchPaths = new Set();
1462
+ const seenMatchPaths = /* @__PURE__ */ new Set();
1522
1463
  let truncated = false;
1523
- for (const line of stdout.split('\n')) {
1464
+ for (const line of String(stdout).split("\n")) {
1524
1465
  const filePath = normalizeSearchResultPath(line);
1525
1466
  if (!filePath || isSearchIgnoredRelativePath(filePath)) continue;
1526
1467
  for (const match of pathSearchMatchesForFile(filePath, query)) {
@@ -1536,310 +1477,284 @@ class WorkspaceFileService {
1536
1477
  }
1537
1478
  scoredMatches.sort(comparePathSearchMatches);
1538
1479
  return {
1539
- matches: scoredMatches.slice(0, limit).map(match => ({
1540
- kind: 'path',
1480
+ matches: scoredMatches.slice(0, limit).map((match) => ({
1481
+ kind: "path",
1541
1482
  entryType: match.entryType,
1542
1483
  path: match.path,
1543
1484
  lineNumber: 1,
1544
- lines: '',
1545
- ranges: [],
1485
+ lines: "",
1486
+ ranges: []
1546
1487
  })),
1547
- truncated,
1488
+ truncated
1548
1489
  };
1549
1490
  }
1550
-
1551
1491
  async directPathMatchCandidate(root, query, includeIgnored = false) {
1552
1492
  if (!isLikelyPathQuery(query)) return null;
1553
- const candidatePath = normalizeSearchResultPath(String(query || '').trim().replace(/^\.\/+/, ''));
1554
- if (!candidatePath || candidatePath.includes('\0') || isSearchIgnoredRelativePath(candidatePath, includeIgnored)) return null;
1493
+ const candidatePath = normalizeSearchResultPath(String(query || "").trim().replace(/^\.\/+/, ""));
1494
+ if (!candidatePath || candidatePath.includes("\0") || isSearchIgnoredRelativePath(candidatePath, includeIgnored)) return null;
1555
1495
  try {
1556
1496
  const { target, relativePath } = await this.resolvePath(root, candidatePath);
1557
1497
  if (!relativePath || isSearchIgnoredRelativePath(relativePath, includeIgnored)) return null;
1558
1498
  const stat = await fsp.stat(target);
1559
1499
  if (!stat.isFile()) return null;
1560
1500
  return {
1561
- kind: 'path',
1562
- entryType: 'file',
1501
+ kind: "path",
1502
+ entryType: "file",
1563
1503
  path: relativePath,
1564
1504
  lineNumber: 1,
1565
- lines: '',
1566
- ranges: [],
1505
+ lines: "",
1506
+ ranges: []
1567
1507
  };
1568
1508
  } catch {
1569
1509
  return null;
1570
1510
  }
1571
1511
  }
1572
-
1573
1512
  async resolveRoot(workspaceRoot) {
1574
- if (typeof workspaceRoot !== 'string' || !workspaceRoot.trim()) {
1575
- throw new WorkspaceFileError('workspace root is required', 400);
1513
+ if (typeof workspaceRoot !== "string" || !workspaceRoot.trim()) {
1514
+ throw new WorkspaceFileError("workspace root is required", 400);
1576
1515
  }
1577
-
1578
1516
  let stat;
1579
1517
  try {
1580
1518
  stat = await fsp.stat(workspaceRoot);
1581
1519
  } catch {
1582
- throw new WorkspaceFileError('workspace not found', 404);
1520
+ throw new WorkspaceFileError("workspace not found", 404);
1583
1521
  }
1584
-
1585
1522
  if (!stat.isDirectory()) {
1586
- throw new WorkspaceFileError('workspace must be a directory', 400);
1523
+ throw new WorkspaceFileError("workspace must be a directory", 400);
1587
1524
  }
1588
-
1589
1525
  return fsp.realpath(workspaceRoot);
1590
1526
  }
1591
-
1592
- async resolvePath(workspaceRoot, userPath = '', options = {}) {
1527
+ async resolvePath(workspaceRoot, userPath = "", options = {}) {
1593
1528
  const root = await this.resolveRoot(workspaceRoot);
1594
1529
  const normalized = normalizeUserPath(userPath);
1595
1530
  const target = path.resolve(root, normalized);
1596
1531
  const requestedRelativePath = relativeFromRoot(root, target);
1597
-
1598
1532
  if (!isInside(root, target)) {
1599
- throw new WorkspaceFileError('path must stay inside the workspace', 403);
1533
+ throw new WorkspaceFileError("path must stay inside the workspace", 403);
1600
1534
  }
1601
-
1602
1535
  if (options.allowMissing) {
1603
1536
  const parent = await fsp.realpath(path.dirname(target)).catch(() => null);
1604
1537
  if (!parent || !isInside(root, parent)) {
1605
- throw new WorkspaceFileError('parent path must stay inside the workspace', 403);
1538
+ throw new WorkspaceFileError("parent path must stay inside the workspace", 403);
1606
1539
  }
1607
1540
  return {
1608
1541
  root,
1609
1542
  target,
1610
1543
  relativePath: requestedRelativePath,
1611
1544
  actualRelativePath: requestedRelativePath,
1545
+ external: false,
1546
+ readOnly: false,
1547
+ symbolicLink: false
1612
1548
  };
1613
1549
  }
1614
-
1615
1550
  let realTarget;
1616
1551
  try {
1617
1552
  realTarget = await fsp.realpath(target);
1618
1553
  } catch {
1619
- throw new WorkspaceFileError('path not found', 404);
1554
+ throw new WorkspaceFileError("path not found", 404);
1620
1555
  }
1621
-
1622
1556
  const external = !isInside(root, realTarget);
1623
1557
  if (external && !isInsideAnyRoot(options.allowedExternalRoots, realTarget)) {
1624
- throw new WorkspaceFileError('symlink target is outside allowed workspaces', 403);
1558
+ throw new WorkspaceFileError("symlink target is outside allowed workspaces", 403);
1625
1559
  }
1626
-
1627
1560
  const requestedStat = await fsp.lstat(target).catch(() => null);
1628
-
1629
1561
  return {
1630
1562
  root,
1631
1563
  target: realTarget,
1632
1564
  relativePath: requestedRelativePath,
1633
- actualRelativePath: external ? '' : relativeFromRoot(root, realTarget),
1565
+ actualRelativePath: external ? "" : relativeFromRoot(root, realTarget),
1634
1566
  external,
1635
1567
  readOnly: external,
1636
- symbolicLink: Boolean(requestedStat && requestedStat.isSymbolicLink()),
1568
+ symbolicLink: Boolean(requestedStat && requestedStat.isSymbolicLink())
1637
1569
  };
1638
1570
  }
1639
-
1640
1571
  async resolveEntryPath(workspaceRoot, userPath) {
1641
1572
  const root = await this.resolveRoot(workspaceRoot);
1642
1573
  const normalized = normalizeUserPath(userPath);
1643
1574
  const target = path.resolve(root, normalized);
1644
1575
  const relativePath = relativeFromRoot(root, target);
1645
1576
  if (!relativePath) {
1646
- throw new WorkspaceFileError('workspace root cannot be changed', 400);
1577
+ throw new WorkspaceFileError("workspace root cannot be changed", 400);
1647
1578
  }
1648
1579
  if (!isInside(root, target)) {
1649
- throw new WorkspaceFileError('path must stay inside the workspace', 403);
1580
+ throw new WorkspaceFileError("path must stay inside the workspace", 403);
1650
1581
  }
1651
1582
  try {
1652
1583
  await fsp.lstat(target);
1653
1584
  } catch {
1654
- throw new WorkspaceFileError('path not found', 404);
1585
+ throw new WorkspaceFileError("path not found", 404);
1655
1586
  }
1656
1587
  const realParent = await fsp.realpath(path.dirname(target)).catch(() => null);
1657
1588
  if (!realParent || !isInside(root, realParent)) {
1658
- throw new WorkspaceFileError('symlink target is read-only', 403);
1589
+ throw new WorkspaceFileError("symlink target is read-only", 403);
1659
1590
  }
1660
1591
  return { root, target, relativePath };
1661
1592
  }
1662
-
1663
- async listTree(workspaceRoot, userPath = '', options = {}) {
1593
+ async listTree(workspaceRoot, userPath = "", options = {}) {
1664
1594
  const root = await this.waitForWorkspaceMutations(workspaceRoot);
1665
1595
  const { target, relativePath, external: parentExternal = false } = await this.resolvePath(root, userPath, options);
1666
1596
  const stat = await fsp.stat(target);
1667
1597
  if (!stat.isDirectory()) {
1668
- throw new WorkspaceFileError('path must be a directory', 400);
1598
+ throw new WorkspaceFileError("path must be a directory", 400);
1669
1599
  }
1670
-
1671
1600
  const entries = await fsp.readdir(target, { withFileTypes: true });
1672
- const visibleEntries = entries.filter(entry => !TREE_HIDDEN_NAMES.has(entry.name));
1601
+ const visibleEntries = entries.filter((entry) => !TREE_HIDDEN_NAMES.has(entry.name));
1673
1602
  const [{ value: gitStatusByPath, pending: gitStatusPending }, ignoredPaths] = await Promise.all([
1674
1603
  this.getGitStatusForTree(root),
1675
- this.loadGitIgnoredPaths(root, visibleEntries.map(entry => joinRelativePath(relativePath, entry.name))),
1604
+ this.loadGitIgnoredPaths(root, visibleEntries.map((entry) => joinRelativePath(relativePath, entry.name)))
1676
1605
  ]);
1677
1606
  const descendantGitStatusByPath = buildDescendantGitStatusByDirectory(gitStatusByPath);
1678
- const items = await Promise.all(visibleEntries
1679
- .map(async (entry) => {
1680
- const absolute = path.join(target, entry.name);
1681
- let entryStat;
1607
+ const items = await Promise.all(visibleEntries.map(async (entry) => {
1608
+ const absolute = path.join(target, entry.name);
1609
+ let entryStat;
1610
+ try {
1611
+ entryStat = await fsp.lstat(absolute);
1612
+ } catch (caught) {
1613
+ const error = processError(caught);
1614
+ if (error && error.code === "ENOENT") return null;
1615
+ throw error;
1616
+ }
1617
+ const itemPath = joinRelativePath(relativePath, entry.name);
1618
+ let type = entryStat.isDirectory() ? "directory" : entryStat.isFile() ? "file" : "other";
1619
+ let symbolicLink = false;
1620
+ let external = parentExternal;
1621
+ let readOnly = parentExternal;
1622
+ let linkTarget = "";
1623
+ let linkError = "";
1624
+ let targetStat = entryStat;
1625
+ if (entryStat.isSymbolicLink()) {
1626
+ symbolicLink = true;
1682
1627
  try {
1683
- entryStat = await fsp.lstat(absolute);
1684
- } catch (error) {
1685
- if (error && error.code === 'ENOENT') return null;
1686
- throw error;
1687
- }
1688
- const itemPath = joinRelativePath(relativePath, entry.name);
1689
- let type = entryStat.isDirectory() ? 'directory' : entryStat.isFile() ? 'file' : 'other';
1690
- let symbolicLink = false;
1691
- let external = parentExternal;
1692
- let readOnly = parentExternal;
1693
- let linkTarget = '';
1694
- let linkError = '';
1695
- let targetStat = entryStat;
1696
- if (entryStat.isSymbolicLink()) {
1697
- symbolicLink = true;
1698
- try {
1699
- linkTarget = await fsp.realpath(absolute);
1700
- external = !isInside(root, linkTarget);
1701
- if (external && !isInsideAnyRoot(options.allowedExternalRoots, linkTarget)) {
1702
- type = 'symlink';
1703
- linkError = 'outside-allowed-roots';
1704
- } else {
1705
- targetStat = await fsp.stat(linkTarget);
1706
- type = targetStat.isDirectory() ? 'directory' : targetStat.isFile() ? 'file' : 'other';
1707
- readOnly = external;
1708
- }
1709
- } catch (error) {
1710
- type = 'symlink';
1711
- linkError = error && error.code === 'ENOENT' ? 'broken' : 'unavailable';
1628
+ linkTarget = await fsp.realpath(absolute);
1629
+ external = !isInside(root, linkTarget);
1630
+ if (external && !isInsideAnyRoot(options.allowedExternalRoots, linkTarget)) {
1631
+ type = "symlink";
1632
+ linkError = "outside-allowed-roots";
1633
+ } else {
1634
+ targetStat = await fsp.stat(linkTarget);
1635
+ type = targetStat.isDirectory() ? "directory" : targetStat.isFile() ? "file" : "other";
1636
+ readOnly = external;
1712
1637
  }
1638
+ } catch (caught) {
1639
+ const error = processError(caught);
1640
+ type = "symlink";
1641
+ linkError = error && error.code === "ENOENT" ? "broken" : "unavailable";
1713
1642
  }
1714
- const directGitStatus = gitStatusByPath.get(itemPath);
1715
- let descendantGitStatus = null;
1716
- if (type === 'directory' && !external) {
1717
- descendantGitStatus = descendantGitStatusByPath.get(itemPath) || null;
1718
- }
1719
-
1720
- return {
1721
- name: entry.name,
1722
- path: itemPath,
1723
- type,
1724
- size: targetStat.size,
1725
- mtimeMs: targetStat.mtimeMs,
1726
- version: workspaceEntryVersion(entryStat),
1727
- ...(symbolicLink ? { symbolicLink: true } : {}),
1728
- ...(external ? { external: true } : {}),
1729
- ...(readOnly ? { readOnly: true } : {}),
1730
- ...(linkTarget ? { linkTarget } : {}),
1731
- ...(linkError ? { linkError } : {}),
1732
- ...(ignoredPaths.has(itemPath) ? { ignored: true } : {}),
1733
- ...(directGitStatus ? {
1734
- gitStatus: directGitStatus.kind,
1735
- gitStatusLabel: directGitStatus.label,
1736
- } : {}),
1737
- ...(descendantGitStatus ? {
1738
- descendantGitStatus: descendantGitStatus.kind,
1739
- } : {}),
1740
- };
1741
- }));
1643
+ }
1644
+ const directGitStatus = gitStatusByPath.get(itemPath);
1645
+ let descendantGitStatus = null;
1646
+ if (type === "directory" && !external) {
1647
+ descendantGitStatus = descendantGitStatusByPath.get(itemPath) || null;
1648
+ }
1649
+ return {
1650
+ name: entry.name,
1651
+ path: itemPath,
1652
+ type,
1653
+ size: targetStat.size,
1654
+ mtimeMs: targetStat.mtimeMs,
1655
+ version: workspaceEntryVersion(entryStat),
1656
+ ...symbolicLink ? { symbolicLink: true } : {},
1657
+ ...external ? { external: true } : {},
1658
+ ...readOnly ? { readOnly: true } : {},
1659
+ ...linkTarget ? { linkTarget } : {},
1660
+ ...linkError ? { linkError } : {},
1661
+ ...ignoredPaths.has(itemPath) ? { ignored: true } : {},
1662
+ ...directGitStatus ? {
1663
+ gitStatus: directGitStatus.kind,
1664
+ gitStatusLabel: directGitStatus.label
1665
+ } : {},
1666
+ ...descendantGitStatus ? {
1667
+ descendantGitStatus: descendantGitStatus.kind
1668
+ } : {}
1669
+ };
1670
+ }));
1742
1671
  const visibleItems = items.filter(Boolean);
1743
-
1744
1672
  visibleItems.sort((a, b) => {
1745
- if (a.type === 'directory' && b.type !== 'directory') return -1;
1746
- if (a.type !== 'directory' && b.type === 'directory') return 1;
1673
+ if (a.type === "directory" && b.type !== "directory") return -1;
1674
+ if (a.type !== "directory" && b.type === "directory") return 1;
1747
1675
  return a.name.localeCompare(b.name);
1748
1676
  });
1749
-
1750
1677
  return {
1751
1678
  path: relativePath,
1752
1679
  items: visibleItems,
1753
- gitStatusPending,
1680
+ gitStatusPending
1754
1681
  };
1755
1682
  }
1756
-
1757
1683
  invalidateGitStatus(root) {
1758
1684
  if (root) this.gitStatusCache.delete(root);
1759
1685
  }
1760
-
1761
1686
  async loadGitStatusByPath(root, options = {}) {
1762
- const untrackedFiles = options.untrackedFiles || 'normal';
1687
+ const untrackedFiles = options.untrackedFiles || "normal";
1763
1688
  try {
1764
1689
  const { stdout } = await this.execFile(this.gitPath, [
1765
- 'status',
1766
- '--porcelain=v1',
1767
- '-z',
1690
+ "status",
1691
+ "--porcelain=v1",
1692
+ "-z",
1768
1693
  `--untracked-files=${untrackedFiles}`,
1769
- '--ignored=no',
1770
- ...gitStatusExcludePathspecArgs(),
1771
- ], { cwd: root, timeout: options.timeoutMs ?? this.gitStatusTimeoutMs });
1694
+ "--ignored=no",
1695
+ ...gitStatusExcludePathspecArgs()
1696
+ ], { cwd: root, timeout: Number(options.timeoutMs) || this.gitStatusTimeoutMs });
1772
1697
  return parseGitStatus(stdout);
1773
- } catch (error) {
1774
- if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER' && options.allowPartial === true) {
1775
- const partialOutput = String(error.stdout || '');
1776
- const lastCompleteRecord = partialOutput.lastIndexOf('\0');
1777
- const statusByPath = parseGitStatus(lastCompleteRecord >= 0
1778
- ? partialOutput.slice(0, lastCompleteRecord + 1)
1779
- : '');
1698
+ } catch (caught) {
1699
+ const error = processError(caught);
1700
+ if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" && options.allowPartial === true) {
1701
+ const partialOutput = String(error.stdout || "");
1702
+ const lastCompleteRecord = partialOutput.lastIndexOf("\0");
1703
+ const statusByPath = parseGitStatus(lastCompleteRecord >= 0 ? partialOutput.slice(0, lastCompleteRecord + 1) : "");
1780
1704
  statusByPath.truncated = true;
1781
1705
  return statusByPath;
1782
1706
  }
1783
- if (error.code === 'ENOENT') {
1707
+ if (error.code === "ENOENT") {
1784
1708
  if (options.throwOnError === true) {
1785
- throw new WorkspaceFileError('git is not installed', 501);
1709
+ throw new WorkspaceFileError("git is not installed", 501);
1786
1710
  }
1787
- return new Map();
1711
+ return /* @__PURE__ */ new Map();
1788
1712
  }
1789
- if (error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM') {
1790
- throw new WorkspaceFileError('git status timed out', 504);
1713
+ if (error.code === "ETIMEDOUT" || error.signal === "SIGTERM") {
1714
+ throw new WorkspaceFileError("git status timed out", 504);
1791
1715
  }
1792
- if (error.stderr && /not a git repository/i.test(error.stderr)) return new Map();
1716
+ if (error.stderr && /not a git repository/i.test(String(error.stderr))) return /* @__PURE__ */ new Map();
1793
1717
  if (options.throwOnError === true) {
1794
- throw new WorkspaceFileError(error.stderr || 'git status failed', 500);
1718
+ throw new WorkspaceFileError(String(error.stderr || "git status failed"), 500);
1795
1719
  }
1796
- return new Map();
1720
+ return /* @__PURE__ */ new Map();
1797
1721
  }
1798
1722
  }
1799
-
1800
1723
  async loadGitIgnoredPaths(root, relativePaths) {
1801
- const paths = Array.from(new Set(relativePaths
1802
- .map(normalizeGitStatusPath)
1803
- .filter(Boolean)));
1804
- if (paths.length === 0) return new Set();
1805
-
1724
+ const paths = Array.from(new Set(relativePaths.map(normalizeGitStatusPath).filter(Boolean)));
1725
+ if (paths.length === 0) return /* @__PURE__ */ new Set();
1806
1726
  try {
1807
1727
  const { stdout } = await this.execFile(this.gitPath, [
1808
- 'check-ignore',
1809
- '--',
1810
- ...paths,
1728
+ "check-ignore",
1729
+ "--",
1730
+ ...paths
1811
1731
  ], { cwd: root });
1812
- return new Set(stdout
1813
- .split(/\r?\n/)
1814
- .map(normalizeGitStatusPath)
1815
- .filter(Boolean));
1732
+ return new Set(String(stdout).split(/\r?\n/).map(normalizeGitStatusPath).filter(Boolean));
1816
1733
  } catch {
1817
- return new Set();
1734
+ return /* @__PURE__ */ new Set();
1818
1735
  }
1819
1736
  }
1820
-
1821
1737
  async loadGitStatusForPath(root, relativePath) {
1822
1738
  const normalizedPath = normalizeGitStatusPath(relativePath);
1823
1739
  if (!normalizedPath) return null;
1824
-
1825
1740
  try {
1826
1741
  const { stdout } = await this.execFile(this.gitPath, [
1827
- 'status',
1828
- '--porcelain=v1',
1829
- '-z',
1830
- '--untracked-files=normal',
1831
- '--ignored=no',
1832
- '--',
1833
- normalizedPath,
1742
+ "status",
1743
+ "--porcelain=v1",
1744
+ "-z",
1745
+ "--untracked-files=normal",
1746
+ "--ignored=no",
1747
+ "--",
1748
+ normalizedPath
1834
1749
  ], { cwd: root });
1835
1750
  return parseGitStatus(stdout).get(normalizedPath) || null;
1836
- } catch (error) {
1837
- if (error.code === 'ENOENT') return null;
1838
- if (error.stderr && /not a git repository/i.test(error.stderr)) return null;
1751
+ } catch (caught) {
1752
+ const error = processError(caught);
1753
+ if (error.code === "ENOENT") return null;
1754
+ if (error.stderr && /not a git repository/i.test(String(error.stderr))) return null;
1839
1755
  return null;
1840
1756
  }
1841
1757
  }
1842
-
1843
1758
  async getGitStatusByPath(root) {
1844
1759
  if (this.gitStatusCacheTtlMs > 0) {
1845
1760
  const now = Date.now();
@@ -1850,11 +1765,10 @@ class WorkspaceFileService {
1850
1765
  if (cached?.promise) {
1851
1766
  return cached.promise;
1852
1767
  }
1853
-
1854
1768
  const promise = this.loadGitStatusByPath(root).then((value) => {
1855
1769
  this.gitStatusCache.set(root, {
1856
1770
  value,
1857
- expiresAt: Date.now() + this.gitStatusCacheTtlMs,
1771
+ expiresAt: Date.now() + this.gitStatusCacheTtlMs
1858
1772
  });
1859
1773
  return value;
1860
1774
  }, (error) => {
@@ -1863,113 +1777,101 @@ class WorkspaceFileService {
1863
1777
  });
1864
1778
  this.gitStatusCache.set(root, {
1865
1779
  promise,
1866
- expiresAt: now + this.gitStatusCacheTtlMs,
1780
+ expiresAt: now + this.gitStatusCacheTtlMs
1867
1781
  });
1868
1782
  return promise;
1869
1783
  }
1870
-
1871
1784
  return this.loadGitStatusByPath(root);
1872
1785
  }
1873
-
1874
1786
  async getGitStatusForTree(root) {
1875
1787
  const cached = this.gitStatusCache.get(root);
1876
1788
  if (cached?.value) {
1877
1789
  return { value: cached.value, pending: false };
1878
1790
  }
1879
-
1880
1791
  const promise = this.getGitStatusByPath(root);
1881
1792
  if (this.gitStatusInlineTimeoutMs <= 0) {
1882
1793
  return { value: await promise, pending: false };
1883
1794
  }
1884
-
1885
1795
  const result = await Promise.race([
1886
1796
  promise,
1887
- new Promise(resolve => setTimeout(() => resolve(TIMEOUT), this.gitStatusInlineTimeoutMs)),
1797
+ new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), this.gitStatusInlineTimeoutMs))
1888
1798
  ]);
1889
-
1890
1799
  if (result === TIMEOUT) {
1891
- return { value: new Map(), pending: true };
1800
+ return { value: /* @__PURE__ */ new Map(), pending: true };
1892
1801
  }
1893
-
1894
1802
  return { value: result, pending: false };
1895
1803
  }
1896
-
1897
1804
  async getGitStatusForPath(root, relativePath) {
1898
1805
  const normalizedPath = normalizeGitStatusPath(relativePath);
1899
1806
  if (!normalizedPath) return null;
1900
-
1901
1807
  const cached = this.gitStatusCache.get(root);
1902
1808
  if (cached?.value) {
1903
1809
  return cached.value.get(normalizedPath) || null;
1904
1810
  }
1905
-
1906
1811
  return this.loadGitStatusForPath(root, normalizedPath);
1907
1812
  }
1908
-
1909
1813
  async gitBranch(workspaceRoot) {
1910
1814
  const root = await this.resolveRoot(workspaceRoot);
1911
1815
  try {
1912
- const { stdout } = await this.execFile(this.gitPath, ['-C', root, 'branch', '--show-current'], {
1816
+ const { stdout } = await this.execFile(this.gitPath, ["-C", root, "branch", "--show-current"], {
1913
1817
  timeout: 1500,
1914
- maxBuffer: 64 * 1024,
1818
+ maxBuffer: 64 * 1024
1915
1819
  });
1916
- return String(stdout || '').trim();
1820
+ return String(stdout || "").trim();
1917
1821
  } catch {
1918
- return '';
1822
+ return "";
1919
1823
  }
1920
1824
  }
1921
-
1922
1825
  async gitHistory(workspaceRoot, options = {}) {
1923
1826
  const root = await this.resolveRoot(workspaceRoot);
1924
1827
  const limit = normalizeGitHistoryLimit(options.limit);
1925
1828
  const skip = normalizeGitHistorySkip(options.skip);
1926
- const scope = options.scope === 'all' ? 'all' : 'current';
1927
- let branch = '';
1829
+ const scope = options.scope === "all" ? "all" : "current";
1830
+ let branch = "";
1928
1831
  try {
1929
1832
  branch = await this.gitBranch(root);
1930
- await this.execFile(this.gitPath, ['-C', root, 'rev-parse', '--is-inside-work-tree'], {
1833
+ await this.execFile(this.gitPath, ["-C", root, "rev-parse", "--is-inside-work-tree"], {
1931
1834
  timeout: 1500,
1932
- maxBuffer: 64 * 1024,
1835
+ maxBuffer: 64 * 1024
1933
1836
  });
1934
- } catch (error) {
1935
- if (error.code === 'ENOENT') throw new WorkspaceFileError('git is not installed', 501);
1837
+ } catch (caught) {
1838
+ const error = processError(caught);
1839
+ if (error.code === "ENOENT") throw new WorkspaceFileError("git is not installed", 501);
1936
1840
  if (error.stderr && /not a git repository/i.test(String(error.stderr))) {
1937
- return { isGitRepo: false, branch: '', head: '', scope, items: [], hasMore: false, nextSkip: null };
1841
+ return { isGitRepo: false, branch: "", head: "", scope, items: [], hasMore: false, nextSkip: null };
1938
1842
  }
1939
- throw new WorkspaceFileError(error.stderr || 'git history failed', 500);
1843
+ throw new WorkspaceFileError(String(error.stderr || "git history failed"), 500);
1940
1844
  }
1941
-
1942
- let head = '';
1845
+ let head = "";
1943
1846
  try {
1944
- const result = await this.execFile(this.gitPath, ['-C', root, 'rev-parse', '--verify', 'HEAD'], {
1847
+ const result = await this.execFile(this.gitPath, ["-C", root, "rev-parse", "--verify", "HEAD"], {
1945
1848
  timeout: 1500,
1946
- maxBuffer: 64 * 1024,
1849
+ maxBuffer: 64 * 1024
1947
1850
  });
1948
- head = String(result.stdout || '').trim();
1949
- } catch (error) {
1851
+ head = String(result.stdout || "").trim();
1852
+ } catch (caught) {
1853
+ const error = processError(caught);
1950
1854
  if (error.stderr && /(needed a single revision|unknown revision|bad revision|ambiguous argument)/i.test(String(error.stderr))) {
1951
- return { isGitRepo: true, branch, head: '', scope, items: [], hasMore: false, nextSkip: null };
1855
+ return { isGitRepo: true, branch, head: "", scope, items: [], hasMore: false, nextSkip: null };
1952
1856
  }
1953
- throw new WorkspaceFileError(error.stderr || 'git history failed', 500);
1857
+ throw new WorkspaceFileError(String(error.stderr || "git history failed"), 500);
1954
1858
  }
1955
-
1956
1859
  try {
1957
- const revisionArgs = scope === 'all'
1958
- ? ['HEAD', '--branches', '--tags', '--remotes']
1959
- : ['--first-parent', 'HEAD'];
1860
+ const revisionArgs = scope === "all" ? ["HEAD", "--branches", "--tags", "--remotes"] : ["--first-parent", "HEAD"];
1960
1861
  const { stdout } = await this.execFile(this.gitPath, [
1961
- '-C', root,
1962
- 'log',
1963
- '-z',
1964
- '--date-order',
1965
- '--decorate=full',
1966
- '--format=%H%x00%P%x00%an%x00%ae%x00%aI%x00%s%x00%B%x00%D',
1862
+ "-C",
1863
+ root,
1864
+ "log",
1865
+ "-z",
1866
+ "--date-order",
1867
+ "--decorate=full",
1868
+ "--format=%H%x00%P%x00%an%x00%ae%x00%aI%x00%s%x00%B%x00%D",
1967
1869
  `--max-count=${limit + 1}`,
1968
1870
  `--skip=${skip}`,
1969
- ...revisionArgs,
1871
+ ...revisionArgs
1970
1872
  ], {
1971
1873
  timeout: this.gitHistoryTimeoutMs,
1972
- maxBuffer: this.gitHistoryMaxBuffer,
1874
+ maxBuffer: this.gitHistoryMaxBuffer
1973
1875
  });
1974
1876
  const parsed = parseGitHistoryLog(stdout);
1975
1877
  const hasMore = parsed.length > limit;
@@ -1981,65 +1883,67 @@ class WorkspaceFileService {
1981
1883
  scope,
1982
1884
  items,
1983
1885
  hasMore,
1984
- nextSkip: hasMore ? skip + items.length : null,
1886
+ nextSkip: hasMore ? skip + items.length : null
1985
1887
  };
1986
- } catch (error) {
1987
- if (error.code === 'ENOENT') throw new WorkspaceFileError('git is not installed', 501);
1988
- if (error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM') {
1989
- throw new WorkspaceFileError('git history timed out', 504);
1888
+ } catch (caught) {
1889
+ const error = processError(caught);
1890
+ if (error.code === "ENOENT") throw new WorkspaceFileError("git is not installed", 501);
1891
+ if (error.code === "ETIMEDOUT" || error.signal === "SIGTERM") {
1892
+ throw new WorkspaceFileError("git history timed out", 504);
1990
1893
  }
1991
- if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
1992
- throw new WorkspaceFileError('git history output is too large', 413);
1894
+ if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
1895
+ throw new WorkspaceFileError("git history output is too large", 413);
1993
1896
  }
1994
1897
  if (error.stderr && /not a git repository/i.test(String(error.stderr))) {
1995
- return { isGitRepo: false, branch: '', head: '', scope, items: [], hasMore: false, nextSkip: null };
1898
+ return { isGitRepo: false, branch: "", head: "", scope, items: [], hasMore: false, nextSkip: null };
1996
1899
  }
1997
- throw new WorkspaceFileError(error.stderr || 'git history failed', 500);
1900
+ throw new WorkspaceFileError(String(error.stderr || "git history failed"), 500);
1998
1901
  }
1999
1902
  }
2000
-
2001
1903
  async gitHistoryChanges(workspaceRoot, commitValue, parentValue, options = {}) {
2002
1904
  const root = await this.resolveRoot(workspaceRoot);
2003
1905
  const commit = normalizeGitObjectId(commitValue);
2004
- const requestedParent = parentValue ? normalizeGitObjectId(parentValue, 'parent') : '';
2005
- const limit = Math.max(1, Math.min(2000, Number(options.limit) || DEFAULT_GIT_CHANGES_LIMIT));
2006
-
1906
+ const requestedParent = parentValue ? normalizeGitObjectId(parentValue, "parent") : "";
1907
+ const limit = Math.max(1, Math.min(2e3, Number(options.limit) || DEFAULT_GIT_CHANGES_LIMIT));
2007
1908
  try {
2008
1909
  const { stdout: parentOutput } = await this.execFile(this.gitPath, [
2009
- '-C', root, 'rev-list', '--parents', '-n', '1', commit,
1910
+ "-C",
1911
+ root,
1912
+ "rev-list",
1913
+ "--parents",
1914
+ "-n",
1915
+ "1",
1916
+ commit
2010
1917
  ], {
2011
1918
  timeout: this.gitHistoryTimeoutMs,
2012
- maxBuffer: 64 * 1024,
1919
+ maxBuffer: 64 * 1024
2013
1920
  });
2014
- const revision = String(parentOutput || '').trim().split(/\s+/).filter(Boolean);
1921
+ const revision = String(parentOutput || "").trim().split(/\s+/).filter(Boolean);
2015
1922
  if (!revision.length || revision[0].toLowerCase() !== commit) {
2016
- throw new WorkspaceFileError('commit was not found', 404);
1923
+ throw new WorkspaceFileError("commit was not found", 404);
2017
1924
  }
2018
- const parentIds = revision.slice(1).map(value => value.toLowerCase());
1925
+ const parentIds = revision.slice(1).map((value) => value.toLowerCase());
2019
1926
  if (requestedParent && !parentIds.includes(requestedParent)) {
2020
- throw new WorkspaceFileError('parent is not a parent of commit', 400);
1927
+ throw new WorkspaceFileError("parent is not a parent of commit", 400);
2021
1928
  }
2022
- const parent = requestedParent || parentIds[0] || '';
1929
+ const parent = requestedParent || parentIds[0] || "";
2023
1930
  let comparisonBase = parent;
2024
1931
  if (!comparisonBase) {
2025
- let objectFormat = 'sha1';
1932
+ let objectFormat = "sha1";
2026
1933
  try {
2027
- const result = await this.execFile(this.gitPath, ['-C', root, 'rev-parse', '--show-object-format'], {
1934
+ const result = await this.execFile(this.gitPath, ["-C", root, "rev-parse", "--show-object-format"], {
2028
1935
  timeout: 1500,
2029
- maxBuffer: 64 * 1024,
1936
+ maxBuffer: 64 * 1024
2030
1937
  });
2031
- objectFormat = String(result.stdout || '').trim();
1938
+ objectFormat = String(result.stdout || "").trim();
2032
1939
  } catch {
2033
- // Git versions without --show-object-format use SHA-1 repositories.
2034
1940
  }
2035
1941
  comparisonBase = gitEmptyTreeObjectId(objectFormat);
2036
1942
  }
2037
- const args = parent
2038
- ? ['-C', root, 'diff', '--name-status', '-z', '-M', parent, commit, '--']
2039
- : ['-C', root, 'diff-tree', '--root', '--no-commit-id', '--name-status', '-r', '-z', '-M', commit];
1943
+ const args = parent ? ["-C", root, "diff", "--name-status", "-z", "-M", parent, commit, "--"] : ["-C", root, "diff-tree", "--root", "--no-commit-id", "--name-status", "-r", "-z", "-M", commit];
2040
1944
  const { stdout } = await this.execFile(this.gitPath, args, {
2041
1945
  timeout: this.gitHistoryTimeoutMs,
2042
- maxBuffer: this.gitHistoryMaxBuffer,
1946
+ maxBuffer: this.gitHistoryMaxBuffer
2043
1947
  });
2044
1948
  const allItems = parseGitHistoryChanges(stdout);
2045
1949
  return {
@@ -2048,97 +1952,91 @@ class WorkspaceFileService {
2048
1952
  parent: parent || null,
2049
1953
  parentIds,
2050
1954
  items: allItems.slice(0, limit),
2051
- truncated: allItems.length > limit,
1955
+ truncated: allItems.length > limit
2052
1956
  };
2053
- } catch (error) {
1957
+ } catch (caught) {
1958
+ const error = processError(caught);
2054
1959
  if (error instanceof WorkspaceFileError) throw error;
2055
- if (error.code === 'ENOENT') throw new WorkspaceFileError('git is not installed', 501);
2056
- if (error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM') {
2057
- throw new WorkspaceFileError('git commit changes timed out', 504);
1960
+ if (error.code === "ENOENT") throw new WorkspaceFileError("git is not installed", 501);
1961
+ if (error.code === "ETIMEDOUT" || error.signal === "SIGTERM") {
1962
+ throw new WorkspaceFileError("git commit changes timed out", 504);
2058
1963
  }
2059
- if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
2060
- throw new WorkspaceFileError('git commit changes output is too large', 413);
1964
+ if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
1965
+ throw new WorkspaceFileError("git commit changes output is too large", 413);
2061
1966
  }
2062
1967
  if (error.stderr && /(bad object|unknown revision|ambiguous argument|not a valid object name)/i.test(String(error.stderr))) {
2063
- throw new WorkspaceFileError('commit was not found', 404);
1968
+ throw new WorkspaceFileError("commit was not found", 404);
2064
1969
  }
2065
1970
  if (error.stderr && /not a git repository/i.test(String(error.stderr))) {
2066
- throw new WorkspaceFileError('workspace is not a git repository', 409);
1971
+ throw new WorkspaceFileError("workspace is not a git repository", 409);
2067
1972
  }
2068
- throw new WorkspaceFileError(error.stderr || 'git commit changes failed', 500);
1973
+ throw new WorkspaceFileError(String(error.stderr || "git commit changes failed"), 500);
2069
1974
  }
2070
1975
  }
2071
-
2072
1976
  async changes(workspaceRoot, options = {}) {
2073
1977
  const root = await this.resolveRoot(workspaceRoot);
2074
- const limit = Math.max(1, Math.min(2000, Number(options.limit) || DEFAULT_GIT_CHANGES_LIMIT));
1978
+ const limit = Math.max(1, Math.min(2e3, Number(options.limit) || DEFAULT_GIT_CHANGES_LIMIT));
2075
1979
  const gitStatusByPath = await this.loadGitStatusByPath(root, {
2076
1980
  allowPartial: true,
2077
1981
  throwOnError: true,
2078
- untrackedFiles: 'all',
1982
+ untrackedFiles: "all"
2079
1983
  });
2080
1984
  this.invalidateGitStatus(root);
2081
- const allItems = await Promise.all(Array.from(gitStatusByPath.entries())
2082
- .map(async ([filePath, status]) => ({
2083
- path: filePath,
2084
- name: path.posix.basename(filePath),
2085
- type: await workspaceEntryTypeForGitChange(root, filePath),
2086
- gitStatus: status.kind,
2087
- gitStatusLabel: status.label,
2088
- ...(status.previousPath ? { previousPath: status.previousPath } : {}),
2089
- })))
2090
- allItems
2091
- .sort((left, right) => (
2092
- gitStatusReviewRank(left.gitStatus) - gitStatusReviewRank(right.gitStatus)
2093
- || left.path.localeCompare(right.path)
2094
- ));
2095
-
1985
+ const allItems = await Promise.all(Array.from(gitStatusByPath.entries()).map(async ([filePath, status]) => ({
1986
+ path: filePath,
1987
+ name: path.posix.basename(filePath),
1988
+ type: await workspaceEntryTypeForGitChange(root, filePath),
1989
+ gitStatus: status.kind,
1990
+ gitStatusLabel: status.label,
1991
+ ...status.previousPath ? { previousPath: status.previousPath } : {}
1992
+ })));
1993
+ allItems.sort((left, right) => gitStatusReviewRank(left.gitStatus) - gitStatusReviewRank(right.gitStatus) || left.path.localeCompare(right.path));
1994
+ const typedGitStatusByPath = gitStatusByPath;
2096
1995
  return {
2097
1996
  items: allItems.slice(0, limit),
2098
- truncated: gitStatusByPath.truncated === true || allItems.length > limit,
1997
+ truncated: typedGitStatusByPath.truncated === true || allItems.length > limit
2099
1998
  };
2100
1999
  }
2101
-
2102
2000
  async readFile(workspaceRoot, userPath, options = {}) {
2103
2001
  const { target, relativePath, external, readOnly, symbolicLink } = await this.resolvePath(workspaceRoot, userPath, options);
2104
2002
  const stat = await fsp.stat(target);
2105
2003
  if (!stat.isFile()) {
2106
- throw new WorkspaceFileError('path must be a file', 400);
2004
+ throw new WorkspaceFileError("path must be a file", 400);
2107
2005
  }
2108
2006
  const preview = previewForPath(relativePath);
2109
2007
  if (preview) {
2110
2008
  if (stat.size > this.maxPreviewFileSize) {
2111
- throw new WorkspaceFileError('file is too large to preview', 413, { size: stat.size });
2009
+ throw new WorkspaceFileError("file is too large to preview", 413, { size: stat.size });
2112
2010
  }
2113
- const buffer = await fsp.readFile(target);
2011
+ const buffer2 = await fsp.readFile(target);
2114
2012
  return {
2115
2013
  path: relativePath,
2116
- content: '',
2014
+ content: "",
2117
2015
  size: stat.size,
2118
2016
  mtimeMs: stat.mtimeMs,
2119
- sha1: sha1(buffer),
2017
+ sha1: sha1(buffer2),
2120
2018
  binary: true,
2121
2019
  preview,
2122
- ...(symbolicLink ? { symbolicLink: true } : {}),
2123
- ...(external ? { external: true } : {}),
2124
- ...(readOnly ? { readOnly: true } : {}),
2020
+ ...symbolicLink ? { symbolicLink: true } : {},
2021
+ ...external ? { external: true } : {},
2022
+ ...readOnly ? { readOnly: true } : {}
2125
2023
  };
2126
2024
  }
2127
2025
  if (await isProbablyBinaryFile(target)) {
2128
2026
  return {
2129
2027
  path: relativePath,
2130
- content: '',
2028
+ content: "",
2131
2029
  size: stat.size,
2132
2030
  mtimeMs: stat.mtimeMs,
2133
2031
  sha1: metadataFileVersion(relativePath, stat),
2134
2032
  binary: true,
2135
2033
  preview: {
2136
- kind: 'binary',
2137
- mediaType: 'application/octet-stream',
2034
+ kind: "binary",
2035
+ mediaType: "application/octet-stream"
2138
2036
  },
2139
- ...(symbolicLink ? { symbolicLink: true } : {}),
2140
- ...(external ? { external: true } : {}),
2141
- ...(readOnly ? { readOnly: true } : {}),
2037
+ ...symbolicLink ? { symbolicLink: true } : {},
2038
+ ...external ? { external: true } : {},
2039
+ ...readOnly ? { readOnly: true } : {}
2142
2040
  };
2143
2041
  }
2144
2042
  if (stat.size > this.maxFileSize) {
@@ -2149,41 +2047,39 @@ class WorkspaceFileService {
2149
2047
  mtimeMs: stat.mtimeMs,
2150
2048
  sha1: metadataFileVersion(relativePath, stat),
2151
2049
  preview: {
2152
- kind: 'large-text',
2153
- mediaType: 'text/plain',
2154
- truncated: true,
2050
+ kind: "large-text",
2051
+ mediaType: "text/plain",
2052
+ truncated: true
2155
2053
  },
2156
- ...(symbolicLink ? { symbolicLink: true } : {}),
2157
- ...(external ? { external: true } : {}),
2158
- ...(readOnly ? { readOnly: true } : {}),
2054
+ ...symbolicLink ? { symbolicLink: true } : {},
2055
+ ...external ? { external: true } : {},
2056
+ ...readOnly ? { readOnly: true } : {}
2159
2057
  };
2160
2058
  }
2161
-
2162
2059
  const buffer = await fsp.readFile(target);
2163
2060
  return {
2164
2061
  path: relativePath,
2165
- content: buffer.toString('utf8'),
2062
+ content: buffer.toString("utf8"),
2166
2063
  size: stat.size,
2167
2064
  mtimeMs: stat.mtimeMs,
2168
2065
  sha1: sha1(buffer),
2169
- ...(symbolicLink ? { symbolicLink: true } : {}),
2170
- ...(external ? { external: true } : {}),
2171
- ...(readOnly ? { readOnly: true } : {}),
2066
+ ...symbolicLink ? { symbolicLink: true } : {},
2067
+ ...external ? { external: true } : {},
2068
+ ...readOnly ? { readOnly: true } : {}
2172
2069
  };
2173
2070
  }
2174
-
2175
2071
  async readPreviewFile(workspaceRoot, userPath, options = {}) {
2176
2072
  const { target, relativePath, external, readOnly, symbolicLink } = await this.resolvePath(workspaceRoot, userPath, options);
2177
2073
  const stat = await fsp.stat(target);
2178
2074
  if (!stat.isFile()) {
2179
- throw new WorkspaceFileError('path must be a file', 400);
2075
+ throw new WorkspaceFileError("path must be a file", 400);
2180
2076
  }
2181
2077
  const preview = previewForPath(relativePath, { includeTextImages: true });
2182
2078
  if (!preview) {
2183
- throw new WorkspaceFileError('file preview is not available', 415);
2079
+ throw new WorkspaceFileError("file preview is not available", 415);
2184
2080
  }
2185
2081
  if (stat.size > this.maxPreviewFileSize) {
2186
- throw new WorkspaceFileError('file is too large to preview', 413, { size: stat.size });
2082
+ throw new WorkspaceFileError("file is too large to preview", 413, { size: stat.size });
2187
2083
  }
2188
2084
  const buffer = await fsp.readFile(target);
2189
2085
  return {
@@ -2193,20 +2089,19 @@ class WorkspaceFileService {
2193
2089
  mtimeMs: stat.mtimeMs,
2194
2090
  sha1: sha1(buffer),
2195
2091
  preview,
2196
- ...(symbolicLink ? { symbolicLink: true } : {}),
2197
- ...(external ? { external: true } : {}),
2198
- ...(readOnly ? { readOnly: true } : {}),
2092
+ ...symbolicLink ? { symbolicLink: true } : {},
2093
+ ...external ? { external: true } : {},
2094
+ ...readOnly ? { readOnly: true } : {}
2199
2095
  };
2200
2096
  }
2201
-
2202
2097
  async readResourceFile(workspaceRoot, userPath, options = {}) {
2203
2098
  const { target, relativePath } = await this.resolvePath(workspaceRoot, userPath, options);
2204
2099
  const stat = await fsp.stat(target);
2205
2100
  if (!stat.isFile()) {
2206
- throw new WorkspaceFileError('path must be a file', 400);
2101
+ throw new WorkspaceFileError("path must be a file", 400);
2207
2102
  }
2208
2103
  if (stat.size > this.maxPreviewFileSize) {
2209
- throw new WorkspaceFileError('file is too large to preview', 413, { size: stat.size });
2104
+ throw new WorkspaceFileError("file is too large to preview", 413, { size: stat.size });
2210
2105
  }
2211
2106
  const buffer = await fsp.readFile(target);
2212
2107
  return {
@@ -2214,144 +2109,135 @@ class WorkspaceFileService {
2214
2109
  buffer,
2215
2110
  size: stat.size,
2216
2111
  mtimeMs: stat.mtimeMs,
2217
- sha1: sha1(buffer),
2112
+ sha1: sha1(buffer)
2218
2113
  };
2219
2114
  }
2220
-
2221
2115
  async blameCapability(workspaceRoot, userPath, options = {}) {
2222
2116
  const { root, target, relativePath, actualRelativePath, external } = await this.resolvePath(workspaceRoot, userPath, options);
2223
- const capability = (available, reason = '') => ({
2224
- isGitRepo: reason !== 'not-git-repo' && reason !== 'git-unavailable',
2117
+ const capability = (available, reason = "") => ({
2118
+ isGitRepo: reason !== "not-git-repo" && reason !== "git-unavailable",
2225
2119
  path: relativePath,
2226
2120
  available,
2227
- ...(reason ? { reason } : {}),
2121
+ ...reason ? { reason } : {}
2228
2122
  });
2229
2123
  const stat = await fsp.stat(target);
2230
2124
  if (!stat.isFile()) {
2231
- throw new WorkspaceFileError('path must be a file', 400);
2125
+ throw new WorkspaceFileError("path must be a file", 400);
2232
2126
  }
2233
2127
  if (external) {
2234
- return capability(false, 'external');
2128
+ return capability(false, "external");
2235
2129
  }
2236
2130
  if (stat.size > this.maxFileSize) {
2237
- return capability(false, 'too-large');
2131
+ return capability(false, "too-large");
2238
2132
  }
2239
2133
  if (await isProbablyBinaryFile(target)) {
2240
- return capability(false, 'binary');
2134
+ return capability(false, "binary");
2241
2135
  }
2242
-
2243
2136
  const gitPath = actualRelativePath || relativePath;
2244
2137
  const directGitStatus = await this.getGitStatusForPath(root, gitPath);
2245
- if (directGitStatus && ['added', 'deleted', 'renamed', 'untracked', 'conflicted'].includes(directGitStatus.kind)) {
2138
+ if (directGitStatus && ["added", "deleted", "renamed", "untracked", "conflicted"].includes(directGitStatus.kind)) {
2246
2139
  return capability(false, directGitStatus.kind);
2247
2140
  }
2248
-
2249
2141
  try {
2250
2142
  await this.execFile(this.gitPath, [
2251
- 'ls-files',
2252
- '--error-unmatch',
2253
- '--',
2254
- gitPath,
2143
+ "ls-files",
2144
+ "--error-unmatch",
2145
+ "--",
2146
+ gitPath
2255
2147
  ], { cwd: root });
2256
2148
  return capability(true);
2257
- } catch (error) {
2258
- if (error.code === 'ENOENT') return capability(false, 'git-unavailable');
2259
- if (error.stderr && /not a git repository/i.test(error.stderr)) return capability(false, 'not-git-repo');
2260
- return capability(false, 'untracked');
2149
+ } catch (caught) {
2150
+ const error = processError(caught);
2151
+ if (error.code === "ENOENT") return capability(false, "git-unavailable");
2152
+ if (error.stderr && /not a git repository/i.test(String(error.stderr))) return capability(false, "not-git-repo");
2153
+ return capability(false, "untracked");
2261
2154
  }
2262
2155
  }
2263
-
2264
2156
  async writeFile(workspaceRoot, userPath, content, options = {}) {
2265
- return this.runWorkspaceMutation(workspaceRoot, root => (
2266
- this.writeFileUnlocked(root, userPath, content, options)
2267
- ));
2157
+ return this.runWorkspaceMutation(workspaceRoot, (root) => this.writeFileUnlocked(root, userPath, content, options));
2268
2158
  }
2269
-
2270
2159
  async flushWorkspaceFileHandle(handle) {
2271
2160
  if (!this.flushWorkspaceWrites) return;
2272
2161
  try {
2273
2162
  await handle.datasync();
2274
- } catch (error) {
2275
- if (!['EINVAL', 'ENOSYS', 'ENOTSUP', 'EOPNOTSUPP'].includes(error?.code)) {
2163
+ } catch (caught) {
2164
+ const error = processError(caught);
2165
+ if (!["EINVAL", "ENOSYS", "ENOTSUP", "EOPNOTSUPP"].includes(String(error.code))) {
2276
2166
  throw error;
2277
2167
  }
2278
2168
  this.flushWorkspaceWrites = false;
2279
- console.warn('Workspace file datasync is unsupported; disabling it for this server process:', error);
2169
+ console.warn("Workspace file datasync is unsupported; disabling it for this server process:", error);
2280
2170
  }
2281
2171
  }
2282
-
2283
2172
  async writeFileUnlocked(workspaceRoot, userPath, content, options = {}) {
2284
- if (typeof content !== 'string') {
2285
- throw new WorkspaceFileError('content must be a string', 400);
2173
+ if (typeof content !== "string") {
2174
+ throw new WorkspaceFileError("content must be a string", 400);
2286
2175
  }
2287
- if (Buffer.byteLength(content, 'utf8') > this.maxWriteSize) {
2288
- throw new WorkspaceFileError('file is too large to save', 413);
2176
+ if (Buffer.byteLength(content, "utf8") > this.maxWriteSize) {
2177
+ throw new WorkspaceFileError("file is too large to save", 413);
2289
2178
  }
2290
-
2291
2179
  const { root, target, relativePath } = await this.resolvePath(workspaceRoot, userPath, { allowMissing: true });
2292
2180
  let writeTarget = target;
2293
- const baseSha1 = typeof options.baseSha1 === 'string' ? options.baseSha1 : '';
2181
+ const baseSha1 = typeof options.baseSha1 === "string" ? options.baseSha1 : "";
2294
2182
  const overwrite = options.overwrite === true;
2295
- let currentMode = 0o666;
2296
- let currentSha1 = '';
2183
+ let currentMode = 438;
2184
+ let currentSha1 = "";
2297
2185
  let currentStat = null;
2298
2186
  let exists = false;
2299
2187
  let symbolicLink = false;
2300
-
2301
2188
  try {
2302
2189
  const requestedStat = await fsp.lstat(target);
2303
2190
  symbolicLink = requestedStat.isSymbolicLink();
2304
2191
  const realTarget = await fsp.realpath(target);
2305
2192
  if (!isInside(root, realTarget)) {
2306
- throw new WorkspaceFileError('symlinks outside the workspace are not allowed', 403);
2193
+ throw new WorkspaceFileError("symlinks outside the workspace are not allowed", 403);
2307
2194
  }
2308
2195
  writeTarget = realTarget;
2309
- } catch (error) {
2196
+ } catch (caught) {
2197
+ const error = processError(caught);
2310
2198
  if (error instanceof WorkspaceFileError) throw error;
2311
- if (error.code !== 'ENOENT') throw error;
2199
+ if (error.code !== "ENOENT") throw error;
2312
2200
  }
2313
-
2314
2201
  try {
2315
2202
  currentStat = await fsp.stat(writeTarget);
2316
2203
  if (!currentStat.isFile()) {
2317
- throw new WorkspaceFileError('path must be a file', 400);
2204
+ throw new WorkspaceFileError("path must be a file", 400);
2318
2205
  }
2319
2206
  if (currentStat.size > this.maxFileSize) {
2320
- throw new WorkspaceFileError('existing file is too large to overwrite safely', 413, { size: currentStat.size });
2207
+ throw new WorkspaceFileError("existing file is too large to overwrite safely", 413, { size: currentStat.size });
2321
2208
  }
2322
2209
  if (await isProbablyBinaryFile(writeTarget)) {
2323
- throw new WorkspaceFileError('binary files cannot be overwritten as text', 415);
2210
+ throw new WorkspaceFileError("binary files cannot be overwritten as text", 415);
2324
2211
  }
2325
2212
  const currentBuffer = await fsp.readFile(writeTarget);
2326
2213
  currentSha1 = sha1(currentBuffer);
2327
2214
  currentMode = currentStat.mode;
2328
2215
  exists = true;
2329
- } catch (error) {
2216
+ } catch (caught) {
2217
+ const error = processError(caught);
2330
2218
  if (error instanceof WorkspaceFileError) throw error;
2331
- if (error.code !== 'ENOENT') throw error;
2219
+ if (error.code !== "ENOENT") throw error;
2332
2220
  }
2333
-
2334
- const desiredSha1 = sha1(Buffer.from(content, 'utf8'));
2221
+ const desiredSha1 = sha1(Buffer.from(content, "utf8"));
2335
2222
  if (baseSha1 && currentSha1 && baseSha1 !== currentSha1 && !overwrite) {
2336
2223
  if (currentSha1 === desiredSha1) {
2337
2224
  return {
2338
2225
  path: relativePath,
2339
2226
  content,
2340
- size: Buffer.byteLength(content, 'utf8'),
2227
+ size: Buffer.byteLength(content, "utf8"),
2341
2228
  mtimeMs: currentStat.mtimeMs,
2342
2229
  sha1: desiredSha1,
2343
- ...(symbolicLink ? { symbolicLink: true } : {}),
2230
+ ...symbolicLink ? { symbolicLink: true } : {}
2344
2231
  };
2345
2232
  }
2346
- throw new WorkspaceFileError('file changed on disk', 409, {
2233
+ throw new WorkspaceFileError("file changed on disk", 409, {
2347
2234
  path: relativePath,
2348
- currentSha1,
2235
+ currentSha1
2349
2236
  });
2350
2237
  }
2351
2238
  if (baseSha1 && !exists && !overwrite) {
2352
- throw new WorkspaceFileError('file was deleted on disk', 409, { path: relativePath });
2239
+ throw new WorkspaceFileError("file was deleted on disk", 409, { path: relativePath });
2353
2240
  }
2354
-
2355
2241
  const tempPath = path.join(
2356
2242
  path.dirname(writeTarget),
2357
2243
  `.${path.basename(writeTarget)}.farming-${process.pid}-${crypto.randomUUID()}.tmp`
@@ -2359,189 +2245,171 @@ class WorkspaceFileService {
2359
2245
  let tempHandle = null;
2360
2246
  let committedStat = null;
2361
2247
  try {
2362
- tempHandle = await fsp.open(tempPath, 'wx', currentMode);
2248
+ tempHandle = await fsp.open(tempPath, "wx", currentMode);
2363
2249
  await tempHandle.writeFile(content);
2364
2250
  await this.flushWorkspaceFileHandle(tempHandle);
2365
2251
  committedStat = await tempHandle.stat();
2366
2252
  await tempHandle.close();
2367
2253
  tempHandle = null;
2368
2254
  await fsp.rename(tempPath, writeTarget);
2369
- } catch (error) {
2255
+ } catch (caught) {
2256
+ const error = processError(caught);
2370
2257
  if (tempHandle) {
2371
- await tempHandle.close().catch(() => {});
2258
+ await tempHandle.close().catch(() => {
2259
+ });
2372
2260
  }
2373
- await fsp.unlink(tempPath).catch(unlinkError => {
2374
- if (unlinkError.code !== 'ENOENT') {
2375
- console.error('Failed to clean up workspace file temporary:', unlinkError);
2261
+ await fsp.unlink(tempPath).catch((caughtUnlink) => {
2262
+ const unlinkError = processError(caughtUnlink);
2263
+ if (unlinkError.code !== "ENOENT") {
2264
+ console.error("Failed to clean up workspace file temporary:", unlinkError);
2376
2265
  }
2377
2266
  });
2378
2267
  throw error;
2379
2268
  }
2380
2269
  this.invalidateGitStatus(root);
2381
-
2382
2270
  return {
2383
2271
  path: relativePath,
2384
2272
  content,
2385
- size: Buffer.byteLength(content, 'utf8'),
2273
+ size: Buffer.byteLength(content, "utf8"),
2386
2274
  mtimeMs: committedStat.mtimeMs,
2387
2275
  sha1: desiredSha1,
2388
- ...(symbolicLink ? { symbolicLink: true } : {}),
2276
+ ...symbolicLink ? { symbolicLink: true } : {}
2389
2277
  };
2390
2278
  }
2391
-
2392
- async moveEntry(workspaceRoot, sourcePath, targetDirectory = '', options = {}) {
2393
- return this.runWorkspaceMutation(workspaceRoot, root => (
2394
- this.moveEntryUnlocked(root, sourcePath, targetDirectory, options)
2395
- ));
2279
+ async moveEntry(workspaceRoot, sourcePath, targetDirectory = "", options = {}) {
2280
+ return this.runWorkspaceMutation(workspaceRoot, (root) => this.moveEntryUnlocked(root, sourcePath, targetDirectory, options));
2396
2281
  }
2397
-
2398
- async moveEntryUnlocked(workspaceRoot, sourcePath, targetDirectory = '', options = {}) {
2282
+ async moveEntryUnlocked(workspaceRoot, sourcePath, targetDirectory = "", options = {}) {
2399
2283
  const root = await this.resolveRoot(workspaceRoot);
2400
2284
  const normalizedSource = normalizeUserPath(sourcePath);
2401
2285
  const normalizedTargetDirectory = normalizeUserPath(targetDirectory);
2402
-
2403
2286
  if (!normalizedSource) {
2404
- throw new WorkspaceFileError('source path is required', 400);
2287
+ throw new WorkspaceFileError("source path is required", 400);
2405
2288
  }
2406
2289
  if (shouldIgnorePath(normalizedSource) || shouldIgnorePath(normalizedTargetDirectory)) {
2407
- throw new WorkspaceFileError('ignored paths cannot be moved', 403);
2290
+ throw new WorkspaceFileError("ignored paths cannot be moved", 403);
2408
2291
  }
2409
-
2410
2292
  const source = await this.resolveEntryPath(workspaceRoot, normalizedSource);
2411
2293
  const targetDirectoryPath = path.resolve(root, normalizedTargetDirectory);
2412
2294
  let targetDirectoryRealPath;
2413
2295
  try {
2414
2296
  targetDirectoryRealPath = await fsp.realpath(targetDirectoryPath);
2415
2297
  } catch {
2416
- throw new WorkspaceFileError('target directory not found', 404);
2298
+ throw new WorkspaceFileError("target directory not found", 404);
2417
2299
  }
2418
2300
  if (!isInside(root, targetDirectoryRealPath)) {
2419
- throw new WorkspaceFileError('target directory must stay inside the workspace', 403);
2301
+ throw new WorkspaceFileError("target directory must stay inside the workspace", 403);
2420
2302
  }
2421
-
2422
2303
  const targetDirectoryStat = await fsp.stat(targetDirectoryRealPath);
2423
2304
  if (!targetDirectoryStat.isDirectory()) {
2424
- throw new WorkspaceFileError('target path must be a directory', 400);
2305
+ throw new WorkspaceFileError("target path must be a directory", 400);
2425
2306
  }
2426
-
2427
2307
  const target = path.join(targetDirectoryRealPath, path.basename(source.target));
2428
2308
  const targetPath = relativeFromRoot(root, target);
2429
2309
  if (shouldIgnorePath(targetPath)) {
2430
- throw new WorkspaceFileError('ignored paths cannot be moved', 403);
2310
+ throw new WorkspaceFileError("ignored paths cannot be moved", 403);
2431
2311
  }
2432
-
2433
2312
  if (target === source.target) {
2434
2313
  return {
2435
2314
  sourcePath: source.relativePath,
2436
2315
  targetPath,
2437
2316
  sourceDirectory: parentDirectory(source.relativePath),
2438
- targetDirectory: normalizedTargetDirectory,
2317
+ targetDirectory: normalizedTargetDirectory
2439
2318
  };
2440
2319
  }
2441
-
2442
2320
  const sourceStat = await fsp.lstat(source.target);
2443
2321
  const sourceVersion = workspaceEntryVersion(sourceStat);
2444
2322
  if (options.expectedVersion && options.expectedVersion !== sourceVersion) {
2445
- throw new WorkspaceFileError('source changed on disk', 409, {
2323
+ throw new WorkspaceFileError("source changed on disk", 409, {
2446
2324
  path: source.relativePath,
2447
- currentVersion: sourceVersion,
2325
+ currentVersion: sourceVersion
2448
2326
  });
2449
2327
  }
2450
2328
  if (sourceStat.isDirectory() && isInside(source.target, target)) {
2451
- throw new WorkspaceFileError('directory cannot be moved into itself', 400);
2329
+ throw new WorkspaceFileError("directory cannot be moved into itself", 400);
2452
2330
  }
2453
-
2454
2331
  try {
2455
2332
  await fsp.lstat(target);
2456
- throw new WorkspaceFileError('target path already exists', 409, { path: targetPath });
2457
- } catch (error) {
2333
+ throw new WorkspaceFileError("target path already exists", 409, { path: targetPath });
2334
+ } catch (caught) {
2335
+ const error = processError(caught);
2458
2336
  if (error instanceof WorkspaceFileError) throw error;
2459
- if (error.code !== 'ENOENT') throw error;
2337
+ if (error.code !== "ENOENT") throw error;
2460
2338
  }
2461
-
2462
2339
  await fsp.rename(source.target, target);
2463
2340
  const targetStat = await fsp.lstat(target);
2464
2341
  this.invalidateGitStatus(root);
2465
-
2466
2342
  return {
2467
2343
  sourcePath: source.relativePath,
2468
2344
  targetPath,
2469
2345
  sourceDirectory: parentDirectory(source.relativePath),
2470
2346
  targetDirectory: normalizedTargetDirectory,
2471
2347
  sourceVersion,
2472
- targetVersion: workspaceEntryVersion(targetStat),
2348
+ targetVersion: workspaceEntryVersion(targetStat)
2473
2349
  };
2474
2350
  }
2475
-
2476
- async createEntry(workspaceRoot, parentPath = '', name, type = 'file', content = '') {
2477
- return this.runWorkspaceMutation(workspaceRoot, root => (
2478
- this.createEntryUnlocked(root, parentPath, name, type, content)
2479
- ));
2351
+ async createEntry(workspaceRoot, parentPath = "", name, type = "file", content = "") {
2352
+ return this.runWorkspaceMutation(workspaceRoot, (root) => this.createEntryUnlocked(root, parentPath, name, type, content));
2480
2353
  }
2481
-
2482
- async createEntryUnlocked(workspaceRoot, parentPath = '', name, type = 'file', content = '') {
2354
+ async createEntryUnlocked(workspaceRoot, parentPath = "", name, type = "file", content = "") {
2483
2355
  const root = await this.resolveRoot(workspaceRoot);
2484
2356
  const normalizedParentPath = normalizeUserPath(parentPath);
2485
2357
  const entryName = normalizeEntryName(name);
2486
- const entryType = type === 'directory' ? 'directory' : 'file';
2487
-
2358
+ const entryType = type === "directory" ? "directory" : "file";
2488
2359
  if (shouldIgnorePath(normalizedParentPath)) {
2489
- throw new WorkspaceFileError('ignored paths cannot be changed', 403);
2360
+ throw new WorkspaceFileError("ignored paths cannot be changed", 403);
2490
2361
  }
2491
- if (entryType === 'file' && Buffer.byteLength(String(content || ''), 'utf8') > this.maxWriteSize) {
2492
- throw new WorkspaceFileError('file is too large to save', 413);
2362
+ if (entryType === "file" && Buffer.byteLength(String(content || ""), "utf8") > this.maxWriteSize) {
2363
+ throw new WorkspaceFileError("file is too large to save", 413);
2493
2364
  }
2494
-
2495
2365
  const parentDirectoryPath = path.resolve(root, normalizedParentPath);
2496
2366
  let parentDirectoryRealPath;
2497
2367
  try {
2498
2368
  parentDirectoryRealPath = await fsp.realpath(parentDirectoryPath);
2499
2369
  } catch {
2500
- throw new WorkspaceFileError('parent directory not found', 404);
2370
+ throw new WorkspaceFileError("parent directory not found", 404);
2501
2371
  }
2502
2372
  if (!isInside(root, parentDirectoryRealPath)) {
2503
- throw new WorkspaceFileError('parent directory must stay inside the workspace', 403);
2373
+ throw new WorkspaceFileError("parent directory must stay inside the workspace", 403);
2504
2374
  }
2505
-
2506
2375
  const parentStat = await fsp.stat(parentDirectoryRealPath);
2507
2376
  if (!parentStat.isDirectory()) {
2508
- throw new WorkspaceFileError('parent path must be a directory', 400);
2377
+ throw new WorkspaceFileError("parent path must be a directory", 400);
2509
2378
  }
2510
-
2511
2379
  const target = path.join(parentDirectoryRealPath, entryName);
2512
2380
  const targetPath = relativeFromRoot(root, target);
2513
2381
  if (shouldIgnorePath(targetPath)) {
2514
- throw new WorkspaceFileError('ignored paths cannot be changed', 403);
2382
+ throw new WorkspaceFileError("ignored paths cannot be changed", 403);
2515
2383
  }
2516
-
2517
- if (entryType === 'directory') {
2384
+ if (entryType === "directory") {
2518
2385
  try {
2519
2386
  await fsp.mkdir(target);
2520
- } catch (error) {
2521
- if (error.code === 'EEXIST') {
2522
- throw new WorkspaceFileError('target path already exists', 409, { path: targetPath });
2387
+ } catch (caught) {
2388
+ const error = processError(caught);
2389
+ if (error.code === "EEXIST") {
2390
+ throw new WorkspaceFileError("target path already exists", 409, { path: targetPath });
2523
2391
  }
2524
2392
  throw error;
2525
2393
  }
2526
- const stat = await fsp.lstat(target);
2394
+ const stat2 = await fsp.lstat(target);
2527
2395
  this.invalidateGitStatus(root);
2528
2396
  return {
2529
2397
  entry: {
2530
2398
  name: entryName,
2531
2399
  path: targetPath,
2532
- type: 'directory',
2533
- size: stat.size,
2534
- mtimeMs: stat.mtimeMs,
2535
- version: workspaceEntryVersion(stat),
2536
- },
2400
+ type: "directory",
2401
+ size: stat2.size,
2402
+ mtimeMs: stat2.mtimeMs,
2403
+ version: workspaceEntryVersion(stat2)
2404
+ }
2537
2405
  };
2538
2406
  }
2539
-
2540
2407
  try {
2541
- await fsp.writeFile(target, String(content || ''), { flag: 'wx' });
2542
- } catch (error) {
2543
- if (error.code === 'EEXIST') {
2544
- throw new WorkspaceFileError('target path already exists', 409, { path: targetPath });
2408
+ await fsp.writeFile(target, String(content || ""), { flag: "wx" });
2409
+ } catch (caught) {
2410
+ const error = processError(caught);
2411
+ if (error.code === "EEXIST") {
2412
+ throw new WorkspaceFileError("target path already exists", 409, { path: targetPath });
2545
2413
  }
2546
2414
  throw error;
2547
2415
  }
@@ -2551,139 +2419,121 @@ class WorkspaceFileService {
2551
2419
  entry: {
2552
2420
  name: entryName,
2553
2421
  path: targetPath,
2554
- type: 'file',
2422
+ type: "file",
2555
2423
  size: stat.size,
2556
2424
  mtimeMs: stat.mtimeMs,
2557
- version: workspaceEntryVersion(stat),
2425
+ version: workspaceEntryVersion(stat)
2558
2426
  },
2559
- file: await this.readFile(workspaceRoot, targetPath),
2427
+ file: await this.readFile(workspaceRoot, targetPath)
2560
2428
  };
2561
2429
  }
2562
-
2563
2430
  async renameEntry(workspaceRoot, sourcePath, name, options = {}) {
2564
- return this.runWorkspaceMutation(workspaceRoot, root => (
2565
- this.renameEntryUnlocked(root, sourcePath, name, options)
2566
- ));
2431
+ return this.runWorkspaceMutation(workspaceRoot, (root) => this.renameEntryUnlocked(root, sourcePath, name, options));
2567
2432
  }
2568
-
2569
2433
  async renameEntryUnlocked(workspaceRoot, sourcePath, name, options = {}) {
2570
2434
  const root = await this.resolveRoot(workspaceRoot);
2571
2435
  const normalizedSource = normalizeUserPath(sourcePath);
2572
2436
  const entryName = normalizeEntryName(name);
2573
-
2574
2437
  if (!normalizedSource) {
2575
- throw new WorkspaceFileError('source path is required', 400);
2438
+ throw new WorkspaceFileError("source path is required", 400);
2576
2439
  }
2577
2440
  if (shouldIgnorePath(normalizedSource)) {
2578
- throw new WorkspaceFileError('ignored paths cannot be changed', 403);
2441
+ throw new WorkspaceFileError("ignored paths cannot be changed", 403);
2579
2442
  }
2580
-
2581
2443
  const source = await this.resolveEntryPath(workspaceRoot, normalizedSource);
2582
2444
  const target = path.join(path.dirname(source.target), entryName);
2583
2445
  const targetPath = relativeFromRoot(root, target);
2584
2446
  if (shouldIgnorePath(targetPath)) {
2585
- throw new WorkspaceFileError('ignored paths cannot be changed', 403);
2447
+ throw new WorkspaceFileError("ignored paths cannot be changed", 403);
2586
2448
  }
2587
-
2588
2449
  if (target === source.target) {
2589
2450
  return {
2590
2451
  sourcePath: source.relativePath,
2591
2452
  targetPath,
2592
2453
  sourceDirectory: parentDirectory(source.relativePath),
2593
- targetDirectory: parentDirectory(targetPath),
2454
+ targetDirectory: parentDirectory(targetPath)
2594
2455
  };
2595
2456
  }
2596
-
2597
2457
  const sourceStat = await fsp.lstat(source.target);
2598
2458
  const sourceVersion = workspaceEntryVersion(sourceStat);
2599
2459
  if (options.expectedVersion && options.expectedVersion !== sourceVersion) {
2600
- throw new WorkspaceFileError('source changed on disk', 409, {
2460
+ throw new WorkspaceFileError("source changed on disk", 409, {
2601
2461
  path: source.relativePath,
2602
- currentVersion: sourceVersion,
2462
+ currentVersion: sourceVersion
2603
2463
  });
2604
2464
  }
2605
-
2606
2465
  try {
2607
2466
  await fsp.lstat(target);
2608
- throw new WorkspaceFileError('target path already exists', 409, { path: targetPath });
2609
- } catch (error) {
2467
+ throw new WorkspaceFileError("target path already exists", 409, { path: targetPath });
2468
+ } catch (caught) {
2469
+ const error = processError(caught);
2610
2470
  if (error instanceof WorkspaceFileError) throw error;
2611
- if (error.code !== 'ENOENT') throw error;
2471
+ if (error.code !== "ENOENT") throw error;
2612
2472
  }
2613
-
2614
2473
  await fsp.rename(source.target, target);
2615
2474
  const targetStat = await fsp.lstat(target);
2616
2475
  this.invalidateGitStatus(root);
2617
-
2618
2476
  return {
2619
2477
  sourcePath: source.relativePath,
2620
2478
  targetPath,
2621
2479
  sourceDirectory: parentDirectory(source.relativePath),
2622
2480
  targetDirectory: parentDirectory(targetPath),
2623
2481
  sourceVersion,
2624
- targetVersion: workspaceEntryVersion(targetStat),
2482
+ targetVersion: workspaceEntryVersion(targetStat)
2625
2483
  };
2626
2484
  }
2627
-
2628
2485
  async deleteEntry(workspaceRoot, userPath, options = {}) {
2629
- return this.runWorkspaceMutation(workspaceRoot, root => (
2630
- this.deleteEntryUnlocked(root, userPath, options)
2631
- ));
2486
+ return this.runWorkspaceMutation(workspaceRoot, (root) => this.deleteEntryUnlocked(root, userPath, options));
2632
2487
  }
2633
-
2634
2488
  async deleteEntryUnlocked(workspaceRoot, userPath, options = {}) {
2635
2489
  const { root, target, relativePath } = await this.resolveEntryPath(workspaceRoot, userPath);
2636
2490
  if (!relativePath) {
2637
- throw new WorkspaceFileError('workspace root cannot be deleted', 400);
2491
+ throw new WorkspaceFileError("workspace root cannot be deleted", 400);
2638
2492
  }
2639
2493
  if (shouldIgnorePath(relativePath)) {
2640
- throw new WorkspaceFileError('ignored paths cannot be changed', 403);
2494
+ throw new WorkspaceFileError("ignored paths cannot be changed", 403);
2641
2495
  }
2642
-
2643
2496
  const stat = await fsp.lstat(target);
2644
2497
  const version = workspaceEntryVersion(stat);
2645
2498
  if (options.expectedVersion && options.expectedVersion !== version) {
2646
- throw new WorkspaceFileError('entry changed on disk', 409, {
2499
+ throw new WorkspaceFileError("entry changed on disk", 409, {
2647
2500
  path: relativePath,
2648
- currentVersion: version,
2501
+ currentVersion: version
2649
2502
  });
2650
2503
  }
2651
- const type = stat.isDirectory() ? 'directory' : stat.isFile() ? 'file' : stat.isSymbolicLink() ? 'symlink' : 'other';
2504
+ const type = stat.isDirectory() ? "directory" : stat.isFile() ? "file" : stat.isSymbolicLink() ? "symlink" : "other";
2652
2505
  if (stat.isDirectory()) {
2653
2506
  await fsp.rm(target, { recursive: true, force: false });
2654
2507
  } else {
2655
2508
  await fsp.unlink(target);
2656
2509
  }
2657
2510
  this.invalidateGitStatus(root);
2658
-
2659
2511
  return {
2660
2512
  path: relativePath,
2661
2513
  parentDirectory: parentDirectory(relativePath),
2662
2514
  type,
2663
- version,
2515
+ version
2664
2516
  };
2665
2517
  }
2666
-
2667
2518
  async search(workspaceRoot, query, options = {}) {
2668
- if (typeof query !== 'string' || !query.trim()) {
2669
- throw new WorkspaceFileError('query is required', 400);
2519
+ if (typeof query !== "string" || !query.trim()) {
2520
+ throw new WorkspaceFileError("query is required", 400);
2670
2521
  }
2671
- const { root, relativePath } = await this.resolvePath(workspaceRoot, options.path || '');
2522
+ const { root, relativePath } = await this.resolvePath(workspaceRoot, options.path || "");
2672
2523
  const limit = Math.max(1, Math.min(500, Number(options.limit) || this.searchLimit));
2673
- const searchPath = relativePath || '.';
2674
- const timeout = Math.max(1000, Number(options.timeoutMs) || this.searchTimeoutMs);
2524
+ const searchPath = relativePath || ".";
2525
+ const timeout = Math.max(1e3, Number(options.timeoutMs) || this.searchTimeoutMs);
2675
2526
  const includeIgnored = options.includeIgnored === true;
2676
2527
  const likelyPathQuery = isLikelyPathQuery(query);
2677
- const result = (matches, truncated) => ({
2528
+ const result = (matches2, truncated) => ({
2678
2529
  query,
2679
2530
  path: searchPath,
2680
- matches,
2531
+ matches: matches2,
2681
2532
  truncated,
2682
- timeoutMs: timeout,
2533
+ timeoutMs: timeout
2683
2534
  });
2684
2535
  let pathMatchCandidates = [];
2685
2536
  let searchOutputTruncated = false;
2686
-
2687
2537
  if (includeIgnored) {
2688
2538
  try {
2689
2539
  const directPathMatch = likelyPathQuery ? await this.directPathMatchCandidate(root, query, true) : null;
@@ -2692,14 +2542,14 @@ class WorkspaceFileService {
2692
2542
  }
2693
2543
  const ignoredPathSearch = await this.collectGitIgnoredPathMatchCandidates(root, searchPath, query, limit, timeout);
2694
2544
  return result(ignoredPathSearch.matches, ignoredPathSearch.truncated);
2695
- } catch (error) {
2696
- if (error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM' || error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
2545
+ } catch (caught) {
2546
+ const error = processError(caught);
2547
+ if (error.code === "ETIMEDOUT" || error.signal === "SIGTERM" || error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
2697
2548
  return result([], true);
2698
2549
  }
2699
2550
  return result([], false);
2700
2551
  }
2701
2552
  }
2702
-
2703
2553
  try {
2704
2554
  const directPathMatch = likelyPathQuery ? await this.directPathMatchCandidate(root, query) : null;
2705
2555
  if (directPathMatch) {
@@ -2709,101 +2559,98 @@ class WorkspaceFileService {
2709
2559
  const directoryNameSearch = await this.collectDirectoryNameMatchCandidates(root, searchPath, query, limit, timeout);
2710
2560
  pathMatchCandidates = dedupePathMatches([
2711
2561
  ...directoryNameSearch.matches,
2712
- ...pathSearch.matches,
2562
+ ...pathSearch.matches
2713
2563
  ], limit);
2714
2564
  searchOutputTruncated = pathSearch.truncated || directoryNameSearch.truncated;
2715
- } catch (error) {
2716
- if (error.code === 'ENOENT') {
2717
- throw new WorkspaceFileError('ripgrep is not installed', 501);
2565
+ } catch (caught) {
2566
+ const error = processError(caught);
2567
+ if (error.code === "ENOENT") {
2568
+ throw new WorkspaceFileError("ripgrep is not installed", 501);
2718
2569
  }
2719
- if (error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM') {
2570
+ if (error.code === "ETIMEDOUT" || error.signal === "SIGTERM") {
2720
2571
  searchOutputTruncated = true;
2721
2572
  }
2722
2573
  pathMatchCandidates = [];
2723
2574
  }
2724
-
2725
2575
  if (pathMatchCandidates.length >= limit || pathMatchCandidates.length > 0 && likelyPathQuery) {
2726
2576
  return result(pathMatchCandidates, searchOutputTruncated || pathMatchCandidates.length >= limit);
2727
2577
  }
2728
-
2729
2578
  const args = [
2730
- '--json',
2731
- '--color',
2732
- 'never',
2733
- '--line-number',
2734
- '--column',
2735
- '--max-count',
2579
+ "--json",
2580
+ "--color",
2581
+ "never",
2582
+ "--line-number",
2583
+ "--column",
2584
+ "--max-count",
2736
2585
  String(Math.min(3, limit)),
2737
- '--hidden',
2586
+ "--hidden",
2738
2587
  ...searchExcludeGlobArgs(),
2739
- '--',
2588
+ "--",
2740
2589
  query,
2741
- searchPath,
2590
+ searchPath
2742
2591
  ];
2743
-
2744
2592
  let stdout;
2745
2593
  try {
2746
2594
  ({ stdout } = await this.execRipgrep(args, { cwd: root, timeout }));
2747
- } catch (error) {
2595
+ } catch (caught) {
2596
+ const error = processError(caught);
2748
2597
  if (error.code === 1) {
2749
- stdout = error.stdout || '';
2750
- } else if (error.code === 'ENOENT') {
2751
- throw new WorkspaceFileError('ripgrep is not installed', 501);
2752
- } else if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
2753
- stdout = error.stdout || '';
2598
+ stdout = error.stdout || "";
2599
+ } else if (error.code === "ENOENT") {
2600
+ throw new WorkspaceFileError("ripgrep is not installed", 501);
2601
+ } else if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
2602
+ stdout = error.stdout || "";
2754
2603
  searchOutputTruncated = true;
2755
- } else if (error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM') {
2756
- stdout = error.stdout || '';
2604
+ } else if (error.code === "ETIMEDOUT" || error.signal === "SIGTERM") {
2605
+ stdout = error.stdout || "";
2757
2606
  searchOutputTruncated = true;
2758
2607
  } else {
2759
- console.error('Workspace search command failed:', {
2608
+ console.error("Workspace search command failed:", {
2760
2609
  code: error.code,
2761
2610
  signal: error.signal,
2762
2611
  message: error.message,
2763
- stderr: error.stderr,
2612
+ stderr: error.stderr
2764
2613
  });
2765
- throw new WorkspaceFileError(error.stderr || 'search failed', 500);
2614
+ throw new WorkspaceFileError(String(error.stderr || "search failed"), 500);
2766
2615
  }
2767
2616
  }
2768
-
2769
2617
  const matches = [];
2770
- stdout.split('\n').filter(Boolean).forEach((line) => {
2618
+ String(stdout).split("\n").filter(Boolean).forEach((line) => {
2771
2619
  let event;
2772
2620
  try {
2773
2621
  event = JSON.parse(line);
2774
2622
  } catch {
2775
2623
  return;
2776
2624
  }
2777
- if (event.type !== 'match') return;
2778
- const data = event.data || {};
2779
- const submatches = Array.isArray(data.submatches) ? data.submatches : [];
2780
- const resultPath = normalizeSearchResultPath(data.path && data.path.text ? data.path.text : '');
2625
+ if (!isRecord(event) || event.type !== "match") return;
2626
+ const data = isRecord(event.data) ? event.data : {};
2627
+ const submatches = Array.isArray(data.submatches) ? data.submatches.filter(isRecord) : [];
2628
+ const pathData = isRecord(data.path) ? data.path : {};
2629
+ const linesData = isRecord(data.lines) ? data.lines : {};
2630
+ const resultPath = normalizeSearchResultPath(pathData.text);
2781
2631
  if (!resultPath || isSearchIgnoredRelativePath(resultPath)) return;
2782
2632
  matches.push({
2783
- kind: 'content',
2784
- entryType: 'file',
2633
+ kind: "content",
2634
+ entryType: "file",
2785
2635
  path: resultPath,
2786
2636
  lineNumber: data.line_number,
2787
- lines: data.lines && data.lines.text ? data.lines.text.replace(/\n$/, '') : '',
2788
- ranges: submatches.map(match => ({
2637
+ lines: typeof linesData.text === "string" ? linesData.text.replace(/\n$/, "") : "",
2638
+ ranges: submatches.map((match) => ({
2789
2639
  start: match.start,
2790
- end: match.end,
2791
- })),
2640
+ end: match.end
2641
+ }))
2792
2642
  });
2793
2643
  });
2794
-
2795
2644
  const combinedMatches = [];
2796
- pathMatchCandidates.forEach(match => {
2645
+ pathMatchCandidates.forEach((match) => {
2797
2646
  combinedMatches.push(match);
2798
2647
  });
2799
- matches.forEach(match => {
2648
+ matches.forEach((match) => {
2800
2649
  combinedMatches.push(match);
2801
2650
  });
2802
-
2803
2651
  return result(combinedMatches.slice(0, limit), searchOutputTruncated || combinedMatches.length > limit);
2804
2652
  }
2805
-
2806
- async diff(workspaceRoot, userPath = '', options = {}) {
2653
+ async diff(workspaceRoot, userPath = "", options = {}) {
2807
2654
  const root = await this.resolveRoot(workspaceRoot);
2808
2655
  const normalized = normalizeUserPath(userPath);
2809
2656
  let target = null;
@@ -2816,7 +2663,8 @@ class WorkspaceFileService {
2816
2663
  target = resolved.target;
2817
2664
  relativePath = resolved.relativePath;
2818
2665
  gitRelativePath = resolved.actualRelativePath || resolved.relativePath;
2819
- } catch (error) {
2666
+ } catch (caught) {
2667
+ const error = processError(caught);
2820
2668
  if (error instanceof WorkspaceFileError && error.statusCode === 404) {
2821
2669
  targetMissing = true;
2822
2670
  } else {
@@ -2824,46 +2672,42 @@ class WorkspaceFileService {
2824
2672
  }
2825
2673
  }
2826
2674
  }
2827
- const args = ['-C', root, 'diff', ...gitDiffWhitespaceArgs(options.ignoreWhitespace), ...gitDiffContextArgs(options.context), 'HEAD', '--'];
2675
+ const args = ["-C", root, "diff", ...gitDiffWhitespaceArgs(options.ignoreWhitespace), ...gitDiffContextArgs(options.context), "HEAD", "--"];
2828
2676
  if (normalized) args.push(gitRelativePath);
2829
-
2830
2677
  try {
2831
2678
  const { stdout } = await this.execFile(this.gitPath, args, {
2832
2679
  cwd: root,
2833
2680
  timeout: this.diffTimeoutMs,
2834
- maxBuffer: this.diffMaxBuffer,
2681
+ maxBuffer: this.diffMaxBuffer
2835
2682
  });
2836
2683
  const result = {
2837
2684
  isGitRepo: true,
2838
2685
  path: relativePath,
2839
- patch: stdout,
2686
+ patch: stdout
2840
2687
  };
2841
2688
  if (!normalized) {
2842
2689
  return result;
2843
2690
  }
2844
-
2845
2691
  if (targetMissing) {
2846
- const status = await this.getGitStatusForPath(root, gitRelativePath);
2847
- if (status?.kind !== 'deleted') {
2848
- throw new WorkspaceFileError('path not found', 404);
2692
+ const status2 = await this.getGitStatusForPath(root, gitRelativePath);
2693
+ if (status2?.kind !== "deleted") {
2694
+ throw new WorkspaceFileError("path not found", 404);
2849
2695
  }
2850
2696
  const original = await this.execFile(this.gitPath, [
2851
- '-C',
2697
+ "-C",
2852
2698
  root,
2853
- 'show',
2854
- `HEAD:${gitRelativePath}`,
2855
- ], { cwd: root, encoding: 'buffer' });
2856
- const originalContent = Buffer.isBuffer(original.stdout)
2857
- ? original.stdout.toString('utf8')
2858
- : String(original.stdout || '');
2699
+ "show",
2700
+ `HEAD:${gitRelativePath}`
2701
+ ], { cwd: root, encoding: "buffer" });
2702
+ const originalContent2 = Buffer.isBuffer(original.stdout) ? original.stdout.toString("utf8") : String(original.stdout || "");
2859
2703
  return {
2860
2704
  ...result,
2861
- originalContent,
2862
- modifiedContent: '',
2863
- deleted: true,
2705
+ originalContent: originalContent2,
2706
+ modifiedContent: "",
2707
+ deleted: true
2864
2708
  };
2865
2709
  }
2866
-
2710
+ if (!target) return result;
2867
2711
  const stat = await fsp.stat(target);
2868
2712
  if (!stat.isFile()) {
2869
2713
  return result;
@@ -2873,96 +2717,89 @@ class WorkspaceFileService {
2873
2717
  ...result,
2874
2718
  path: relativePath,
2875
2719
  binary: true,
2876
- size: stat.size,
2720
+ size: stat.size
2877
2721
  };
2878
2722
  }
2879
-
2880
2723
  const modifiedBuffer = await fsp.readFile(target);
2881
2724
  const gitStatusByPath = await this.loadGitStatusByPath(root);
2882
2725
  if (this.gitStatusCacheTtlMs > 0) {
2883
2726
  this.gitStatusCache.set(root, {
2884
2727
  value: gitStatusByPath,
2885
- expiresAt: Date.now() + this.gitStatusCacheTtlMs,
2728
+ expiresAt: Date.now() + this.gitStatusCacheTtlMs
2886
2729
  });
2887
2730
  }
2888
2731
  const status = gitStatusByPath.get(normalizeGitStatusPath(gitRelativePath)) || null;
2889
- const originalGitPath = status?.kind === 'renamed' && status.previousPath
2890
- ? status.previousPath
2891
- : gitRelativePath;
2892
- let originalContent = '';
2732
+ const originalGitPath = status?.kind === "renamed" && status.previousPath ? status.previousPath : gitRelativePath;
2733
+ let originalContent = "";
2893
2734
  let untracked = false;
2894
2735
  try {
2895
2736
  const original = await this.execFile(this.gitPath, [
2896
- '-C',
2737
+ "-C",
2897
2738
  root,
2898
- 'show',
2899
- `HEAD:${originalGitPath}`,
2900
- ], { cwd: root, encoding: 'buffer' });
2901
- originalContent = Buffer.isBuffer(original.stdout)
2902
- ? original.stdout.toString('utf8')
2903
- : String(original.stdout || '');
2904
- } catch (error) {
2739
+ "show",
2740
+ `HEAD:${originalGitPath}`
2741
+ ], { cwd: root, encoding: "buffer" });
2742
+ originalContent = Buffer.isBuffer(original.stdout) ? original.stdout.toString("utf8") : String(original.stdout || "");
2743
+ } catch (caught) {
2744
+ const error = processError(caught);
2905
2745
  if (error.stderr && /(exists on disk, but not in|path .* exists on disk|does not exist in|not in HEAD|invalid object name)/i.test(String(error.stderr))) {
2906
2746
  untracked = true;
2907
2747
  } else {
2908
2748
  throw error;
2909
2749
  }
2910
2750
  }
2911
-
2912
2751
  return {
2913
2752
  ...result,
2914
2753
  path: relativePath,
2915
2754
  originalContent,
2916
- modifiedContent: modifiedBuffer.toString('utf8'),
2917
- untracked,
2755
+ modifiedContent: modifiedBuffer.toString("utf8"),
2756
+ untracked
2918
2757
  };
2919
- } catch (error) {
2920
- if (error.code === 'ENOENT') {
2921
- throw new WorkspaceFileError('git is not installed', 501);
2758
+ } catch (caught) {
2759
+ const error = processError(caught);
2760
+ if (error.code === "ENOENT") {
2761
+ throw new WorkspaceFileError("git is not installed", 501);
2922
2762
  }
2923
- if (error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM') {
2924
- throw new WorkspaceFileError('git diff timed out', 504);
2763
+ if (error.code === "ETIMEDOUT" || error.signal === "SIGTERM") {
2764
+ throw new WorkspaceFileError("git diff timed out", 504);
2925
2765
  }
2926
- if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
2766
+ if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
2927
2767
  return {
2928
2768
  isGitRepo: true,
2929
2769
  path: relativePath,
2930
- patch: error.stdout || '',
2931
- truncated: true,
2770
+ patch: error.stdout || "",
2771
+ truncated: true
2932
2772
  };
2933
2773
  }
2934
- if (error.stderr && /not a git repository/i.test(error.stderr)) {
2774
+ if (error.stderr && /not a git repository/i.test(String(error.stderr))) {
2935
2775
  return {
2936
2776
  isGitRepo: false,
2937
2777
  path: normalized,
2938
- patch: '',
2778
+ patch: ""
2939
2779
  };
2940
2780
  }
2941
- throw new WorkspaceFileError(error.stderr || 'git diff failed', 500);
2781
+ throw new WorkspaceFileError(String(error.stderr || "git diff failed"), 500);
2942
2782
  }
2943
2783
  }
2944
-
2945
- async lineChanges(workspaceRoot, userPath, lineNumber, mode = 'working') {
2784
+ async lineChanges(workspaceRoot, userPath, lineNumber, mode = "working") {
2946
2785
  const requestedLineNumber = Number(lineNumber);
2947
2786
  if (!Number.isInteger(requestedLineNumber) || requestedLineNumber < 1) {
2948
- throw new WorkspaceFileError('lineNumber must be a positive integer', 400);
2787
+ throw new WorkspaceFileError("lineNumber must be a positive integer", 400);
2949
2788
  }
2950
- if (mode !== 'working' && mode !== 'previous') {
2951
- throw new WorkspaceFileError('mode must be working or previous', 400);
2789
+ if (mode !== "working" && mode !== "previous") {
2790
+ throw new WorkspaceFileError("mode must be working or previous", 400);
2952
2791
  }
2953
-
2954
2792
  const { root, target, relativePath, actualRelativePath } = await this.resolvePath(workspaceRoot, userPath);
2955
2793
  const stat = await fsp.stat(target);
2956
2794
  if (!stat.isFile()) {
2957
- throw new WorkspaceFileError('path must be a file', 400);
2795
+ throw new WorkspaceFileError("path must be a file", 400);
2958
2796
  }
2959
2797
  if (stat.size > this.maxFileSize) {
2960
- throw new WorkspaceFileError('file is too large to inspect line changes', 413, { size: stat.size });
2798
+ throw new WorkspaceFileError("file is too large to inspect line changes", 413, { size: stat.size });
2961
2799
  }
2962
2800
  if (await isProbablyBinaryFile(target)) {
2963
- throw new WorkspaceFileError('binary files cannot be inspected as text', 415);
2801
+ throw new WorkspaceFileError("binary files cannot be inspected as text", 415);
2964
2802
  }
2965
-
2966
2803
  const gitRelativePath = actualRelativePath || relativePath;
2967
2804
  const baseResult = {
2968
2805
  isGitRepo: true,
@@ -2970,232 +2807,226 @@ class WorkspaceFileService {
2970
2807
  mode,
2971
2808
  lineNumber: requestedLineNumber,
2972
2809
  lookupLineNumber: requestedLineNumber,
2973
- targetSide: mode === 'working' ? 'working' : 'revision',
2810
+ targetSide: mode === "working" ? "working" : "revision",
2974
2811
  available: false,
2975
- reason: '',
2976
- patch: '',
2977
- hunk: null,
2812
+ reason: "",
2813
+ patch: "",
2814
+ hunk: null
2978
2815
  };
2979
-
2980
2816
  try {
2981
- if (mode === 'working') {
2817
+ if (mode === "working") {
2982
2818
  const directGitStatus = await this.getGitStatusForPath(root, gitRelativePath);
2983
- if (directGitStatus?.kind === 'untracked') {
2984
- const modifiedContent = await fsp.readFile(target, 'utf8');
2985
- const hunk = createAddedFileLineChangesHunk(modifiedContent, requestedLineNumber);
2819
+ if (directGitStatus?.kind === "untracked") {
2820
+ const modifiedContent = await fsp.readFile(target, "utf8");
2821
+ const hunk3 = createAddedFileLineChangesHunk(modifiedContent, requestedLineNumber);
2986
2822
  return {
2987
2823
  ...baseResult,
2988
- available: Boolean(hunk),
2989
- reason: hunk ? '' : 'unchanged',
2990
- patch: hunk ? hunk.patch : '',
2991
- hunk,
2824
+ available: Boolean(hunk3),
2825
+ reason: hunk3 ? "" : "unchanged",
2826
+ patch: hunk3 ? hunk3.patch : "",
2827
+ hunk: hunk3
2992
2828
  };
2993
2829
  }
2994
-
2995
- const { stdout } = await this.execFile(this.gitPath, [
2996
- '-C',
2830
+ const { stdout: stdout2 } = await this.execFile(this.gitPath, [
2831
+ "-C",
2997
2832
  root,
2998
- 'diff',
2999
- '--unified=20',
3000
- 'HEAD',
3001
- '--',
3002
- gitRelativePath,
2833
+ "diff",
2834
+ "--unified=20",
2835
+ "HEAD",
2836
+ "--",
2837
+ gitRelativePath
3003
2838
  ], {
3004
2839
  cwd: root,
3005
2840
  timeout: this.diffTimeoutMs,
3006
- maxBuffer: this.diffMaxBuffer,
2841
+ maxBuffer: this.diffMaxBuffer
3007
2842
  });
3008
- const hunk = selectUnifiedDiffHunk(stdout, 'new', requestedLineNumber);
2843
+ const hunk2 = selectUnifiedDiffHunk(stdout2, "new", requestedLineNumber);
3009
2844
  return {
3010
2845
  ...baseResult,
3011
- available: Boolean(hunk),
3012
- reason: hunk ? '' : 'unchanged',
3013
- patch: hunk ? hunk.patch : '',
3014
- hunk,
2846
+ available: Boolean(hunk2),
2847
+ reason: hunk2 ? "" : "unchanged",
2848
+ patch: hunk2 ? hunk2.patch : "",
2849
+ hunk: hunk2
3015
2850
  };
3016
2851
  }
3017
-
3018
2852
  const blame = await this.blame(workspaceRoot, userPath);
3019
2853
  if (!blame.isGitRepo) {
3020
2854
  return {
3021
2855
  ...baseResult,
3022
2856
  isGitRepo: false,
3023
- reason: 'not-git-repo',
2857
+ reason: "not-git-repo"
3024
2858
  };
3025
2859
  }
3026
- const blameLine = blame.lines.find(line => line.lineNumber === requestedLineNumber);
2860
+ const blameLine = blame.lines.find((line) => line.lineNumber === requestedLineNumber);
3027
2861
  if (!blameLine) {
3028
2862
  return {
3029
2863
  ...baseResult,
3030
- reason: 'line-not-found',
2864
+ reason: "line-not-found"
3031
2865
  };
3032
2866
  }
3033
2867
  if (blameLine.uncommitted) {
3034
2868
  return {
3035
2869
  ...baseResult,
3036
- reason: 'uncommitted',
2870
+ reason: "uncommitted",
3037
2871
  commit: {
3038
2872
  hash: blameLine.commit,
3039
2873
  shortHash: blameLine.shortCommit,
3040
2874
  author: blameLine.author,
3041
2875
  authorTimeIso: blameLine.authorTimeIso,
3042
- summary: blameLine.summary,
3043
- },
2876
+ summary: blameLine.summary
2877
+ }
3044
2878
  };
3045
2879
  }
3046
-
3047
2880
  const { stdout } = await this.execFile(this.gitPath, [
3048
- '-C',
2881
+ "-C",
3049
2882
  root,
3050
- 'show',
3051
- '--format=',
3052
- '--unified=20',
2883
+ "show",
2884
+ "--format=",
2885
+ "--unified=20",
3053
2886
  blameLine.commit,
3054
- '--',
3055
- gitRelativePath,
2887
+ "--",
2888
+ gitRelativePath
3056
2889
  ], {
3057
2890
  cwd: root,
3058
2891
  timeout: this.diffTimeoutMs,
3059
- maxBuffer: this.diffMaxBuffer,
2892
+ maxBuffer: this.diffMaxBuffer
3060
2893
  });
3061
- const hunk = selectUnifiedDiffHunk(stdout, 'new', blameLine.originalLineNumber);
2894
+ const hunk = selectUnifiedDiffHunk(stdout, "new", blameLine.originalLineNumber);
3062
2895
  return {
3063
2896
  ...baseResult,
3064
2897
  lookupLineNumber: blameLine.originalLineNumber,
3065
2898
  available: Boolean(hunk),
3066
- reason: hunk ? '' : 'not-found',
3067
- patch: hunk ? hunk.patch : '',
2899
+ reason: hunk ? "" : "not-found",
2900
+ patch: hunk ? hunk.patch : "",
3068
2901
  hunk,
3069
2902
  commit: {
3070
2903
  hash: blameLine.commit,
3071
2904
  shortHash: blameLine.shortCommit,
3072
2905
  author: blameLine.author,
3073
2906
  authorTimeIso: blameLine.authorTimeIso,
3074
- summary: blameLine.summary,
3075
- },
2907
+ summary: blameLine.summary
2908
+ }
3076
2909
  };
3077
- } catch (error) {
2910
+ } catch (caught) {
2911
+ const error = processError(caught);
3078
2912
  if (error instanceof WorkspaceFileError) {
3079
- if (mode === 'previous' && error.statusCode === 409) {
2913
+ if (mode === "previous" && error.statusCode === 409) {
3080
2914
  return {
3081
2915
  ...baseResult,
3082
- reason: 'untracked',
2916
+ reason: "untracked"
3083
2917
  };
3084
2918
  }
3085
2919
  throw error;
3086
2920
  }
3087
- if (error.code === 'ENOENT') {
3088
- throw new WorkspaceFileError('git is not installed', 501);
2921
+ if (error.code === "ENOENT") {
2922
+ throw new WorkspaceFileError("git is not installed", 501);
3089
2923
  }
3090
- if (error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM') {
3091
- throw new WorkspaceFileError('git line changes timed out', 504);
2924
+ if (error.code === "ETIMEDOUT" || error.signal === "SIGTERM") {
2925
+ throw new WorkspaceFileError("git line changes timed out", 504);
3092
2926
  }
3093
- if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
2927
+ if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
3094
2928
  return {
3095
2929
  ...baseResult,
3096
2930
  available: false,
3097
- reason: 'truncated',
3098
- patch: error.stdout || '',
3099
- truncated: true,
2931
+ reason: "truncated",
2932
+ patch: error.stdout || "",
2933
+ truncated: true
3100
2934
  };
3101
2935
  }
3102
- if (error.stderr && /not a git repository/i.test(error.stderr)) {
2936
+ if (error.stderr && /not a git repository/i.test(String(error.stderr))) {
3103
2937
  return {
3104
2938
  ...baseResult,
3105
2939
  isGitRepo: false,
3106
- reason: 'not-git-repo',
2940
+ reason: "not-git-repo"
3107
2941
  };
3108
2942
  }
3109
- if (mode === 'previous' && error.stderr && /(no such path|does not exist in|not in HEAD|path .* exists on disk|invalid object name)/i.test(String(error.stderr))) {
2943
+ if (mode === "previous" && error.stderr && /(no such path|does not exist in|not in HEAD|path .* exists on disk|invalid object name)/i.test(String(error.stderr))) {
3110
2944
  return {
3111
2945
  ...baseResult,
3112
- reason: 'not-found',
2946
+ reason: "not-found"
3113
2947
  };
3114
2948
  }
3115
- throw new WorkspaceFileError(error.stderr || 'git line changes failed', 500);
2949
+ throw new WorkspaceFileError(String(error.stderr || "git line changes failed"), 500);
3116
2950
  }
3117
2951
  }
3118
-
3119
2952
  async blame(workspaceRoot, userPath) {
3120
2953
  const { root, target, relativePath, actualRelativePath } = await this.resolvePath(workspaceRoot, userPath);
3121
2954
  const stat = await fsp.stat(target);
3122
2955
  if (!stat.isFile()) {
3123
- throw new WorkspaceFileError('path must be a file', 400);
2956
+ throw new WorkspaceFileError("path must be a file", 400);
3124
2957
  }
3125
2958
  if (stat.size > this.maxFileSize) {
3126
- throw new WorkspaceFileError('file is too large to blame', 413, { size: stat.size });
2959
+ throw new WorkspaceFileError("file is too large to blame", 413, { size: stat.size });
3127
2960
  }
3128
2961
  if (await isProbablyBinaryFile(target)) {
3129
- throw new WorkspaceFileError('binary files cannot be blamed as text', 415);
2962
+ throw new WorkspaceFileError("binary files cannot be blamed as text", 415);
3130
2963
  }
3131
-
3132
2964
  try {
3133
2965
  const { stdout } = await this.execFile(this.gitPath, [
3134
- 'blame',
3135
- '--line-porcelain',
3136
- '--',
3137
- actualRelativePath || relativePath,
2966
+ "blame",
2967
+ "--line-porcelain",
2968
+ "--",
2969
+ actualRelativePath || relativePath
3138
2970
  ], { cwd: root, timeout: this.blameTimeoutMs });
3139
2971
  return {
3140
2972
  isGitRepo: true,
3141
2973
  path: relativePath,
3142
- lines: parseGitBlamePorcelain(stdout),
2974
+ lines: parseGitBlamePorcelain(stdout)
3143
2975
  };
3144
- } catch (error) {
3145
- if (error.code === 'ENOENT') {
3146
- throw new WorkspaceFileError('git is not installed', 501);
2976
+ } catch (caught) {
2977
+ const error = processError(caught);
2978
+ if (error.code === "ENOENT") {
2979
+ throw new WorkspaceFileError("git is not installed", 501);
3147
2980
  }
3148
- if (error.code === 'ETIMEDOUT' || error.signal === 'SIGTERM') {
3149
- throw new WorkspaceFileError('git blame timed out', 504);
2981
+ if (error.code === "ETIMEDOUT" || error.signal === "SIGTERM") {
2982
+ throw new WorkspaceFileError("git blame timed out", 504);
3150
2983
  }
3151
- if (error.stderr && /not a git repository/i.test(error.stderr)) {
2984
+ if (error.stderr && /not a git repository/i.test(String(error.stderr))) {
3152
2985
  return {
3153
2986
  isGitRepo: false,
3154
2987
  path: relativePath,
3155
- lines: [],
2988
+ lines: []
3156
2989
  };
3157
2990
  }
3158
- if (error.stderr && /(no such path|no such ref|does not exist in|not in HEAD|no such file)/i.test(error.stderr)) {
3159
- throw new WorkspaceFileError('file is not tracked by git', 409, { path: relativePath });
2991
+ if (error.stderr && /(no such path|no such ref|does not exist in|not in HEAD|no such file)/i.test(String(error.stderr))) {
2992
+ throw new WorkspaceFileError("file is not tracked by git", 409, { path: relativePath });
3160
2993
  }
3161
- throw new WorkspaceFileError(error.stderr || 'git blame failed', 500);
2994
+ throw new WorkspaceFileError(String(error.stderr || "git blame failed"), 500);
3162
2995
  }
3163
2996
  }
3164
-
3165
2997
  async subscribe(workspaceRoot, callback) {
3166
- if (this.disposed) return async () => {};
2998
+ if (this.disposed) return async () => {
2999
+ };
3167
3000
  const lifecycleGeneration = this.watcherLifecycleGeneration;
3168
3001
  const root = await this.resolveRoot(workspaceRoot);
3169
- if (this.disposed || this.watcherLifecycleGeneration !== lifecycleGeneration) return async () => {};
3002
+ if (this.disposed || this.watcherLifecycleGeneration !== lifecycleGeneration) return async () => {
3003
+ };
3170
3004
  const watchRoot = path.resolve(workspaceRoot);
3171
3005
  let record = this.watchers.get(root);
3172
-
3173
3006
  if (!record) {
3174
- const subscribers = new Set();
3007
+ const subscribers = /* @__PURE__ */ new Set();
3175
3008
  const generation = this.watcherSubscriptionGeneration + 1;
3176
3009
  this.watcherSubscriptionGeneration = generation;
3177
-
3178
3010
  const emit = (eventType, absolutePath) => {
3179
- const relative = relativeFromRoot(watchRoot, absolutePath);
3011
+ const relative = relativeFromRoot(watchRoot, String(absolutePath));
3180
3012
  if (shouldHidePath(relative)) return;
3181
3013
  this.invalidateGitStatus(root);
3182
3014
  subscribers.forEach((subscriber) => {
3183
3015
  subscriber({
3184
3016
  type: eventType,
3185
- path: relative,
3017
+ path: relative
3186
3018
  });
3187
3019
  });
3188
3020
  };
3189
-
3190
- const emitError = (error) => {
3021
+ const emitError = (caught) => {
3022
+ const error = processError(caught);
3191
3023
  subscribers.forEach((subscriber) => {
3192
3024
  subscriber({
3193
- type: 'error',
3194
- message: error.message,
3025
+ type: "error",
3026
+ message: error.message
3195
3027
  });
3196
3028
  });
3197
3029
  };
3198
-
3199
3030
  record = {
3200
3031
  watcher: null,
3201
3032
  subscribers,
@@ -3203,62 +3034,56 @@ class WorkspaceFileService {
3203
3034
  cancelled: false,
3204
3035
  cancelInitialization: null,
3205
3036
  closePromise: null,
3206
- ready: null,
3037
+ ready: null
3207
3038
  };
3208
3039
  this.watchers.set(root, record);
3040
+ const initializingRecord = record;
3209
3041
  record.ready = (async () => {
3210
3042
  const configuredIgnored = this.watchOptions.ignored;
3211
3043
  const ignored = (candidatePath) => {
3212
- const relative = path.relative(watchRoot, candidatePath);
3044
+ const candidate = String(candidatePath);
3045
+ const relative = path.relative(watchRoot, candidate);
3213
3046
  if (shouldHidePath(relative)) return true;
3214
- if (typeof configuredIgnored === 'function') return configuredIgnored(candidatePath);
3215
- if (configuredIgnored instanceof RegExp) return configuredIgnored.test(candidatePath);
3047
+ if (typeof configuredIgnored === "function") return Boolean(configuredIgnored(candidate));
3048
+ if (configuredIgnored instanceof RegExp) return configuredIgnored.test(candidate);
3216
3049
  if (Array.isArray(configuredIgnored)) {
3217
- return configuredIgnored.some(pattern => (
3218
- pattern instanceof RegExp ? pattern.test(candidatePath) : String(candidatePath).includes(String(pattern))
3219
- ));
3050
+ return configuredIgnored.some((pattern) => pattern instanceof RegExp ? pattern.test(candidate) : candidate.includes(String(pattern)));
3220
3051
  }
3221
3052
  return false;
3222
3053
  };
3223
3054
  const chokidar = await loadChokidar();
3224
- if (
3225
- record.cancelled
3226
- || this.watchers.get(root)?.generation !== generation
3227
- ) return false;
3055
+ if (initializingRecord.cancelled || this.watchers.get(root)?.generation !== generation) return false;
3228
3056
  const watcher = chokidar.watch(watchRoot, {
3229
3057
  ignoreInitial: true,
3230
3058
  depth: this.watchDepth,
3231
3059
  ...this.watchOptions,
3232
- ignored,
3060
+ ignored
3233
3061
  });
3234
- record.watcher = watcher;
3235
-
3236
- ['add', 'change', 'unlink', 'addDir', 'unlinkDir'].forEach((eventType) => {
3237
- watcher.on(eventType, filePath => emit(eventType, filePath));
3062
+ initializingRecord.watcher = watcher;
3063
+ ["add", "change", "unlink", "addDir", "unlinkDir"].forEach((eventType) => {
3064
+ watcher.on(eventType, (filePath) => emit(eventType, filePath));
3238
3065
  });
3239
- watcher.on('error', emitError);
3240
- const initialized = await new Promise(resolve => {
3066
+ watcher.on("error", emitError);
3067
+ const initialized2 = await new Promise((resolve) => {
3241
3068
  let settled = false;
3242
- const finish = value => {
3069
+ const finish = (value) => {
3243
3070
  if (settled) return;
3244
3071
  settled = true;
3245
3072
  resolve(value);
3246
3073
  };
3247
- watcher.once('ready', () => finish(true));
3248
- record.cancelInitialization = () => finish(false);
3074
+ watcher.once("ready", () => finish(true));
3075
+ initializingRecord.cancelInitialization = () => finish(false);
3249
3076
  });
3250
- record.cancelInitialization = null;
3251
- return initialized
3252
- && !record.cancelled
3253
- && this.watchers.get(root)?.generation === generation;
3077
+ initializingRecord.cancelInitialization = null;
3078
+ return initialized2 && !initializingRecord.cancelled && this.watchers.get(root)?.generation === generation;
3254
3079
  })();
3255
3080
  }
3256
-
3257
3081
  record.subscribers.add(callback);
3258
3082
  let initialized = false;
3259
3083
  try {
3260
- initialized = await record.ready;
3261
- } catch (error) {
3084
+ initialized = await (record.ready ?? Promise.resolve(false));
3085
+ } catch (caught) {
3086
+ const error = processError(caught);
3262
3087
  record.subscribers.delete(callback);
3263
3088
  if (record.subscribers.size === 0 && this.watchers.get(root)?.generation === record.generation) {
3264
3089
  this.watchers.delete(root);
@@ -3268,20 +3093,17 @@ class WorkspaceFileService {
3268
3093
  }
3269
3094
  if (!initialized) {
3270
3095
  record.subscribers.delete(callback);
3271
- return async () => {};
3096
+ return async () => {
3097
+ };
3272
3098
  }
3273
3099
  return async () => {
3274
3100
  if (!record.subscribers.delete(callback)) return;
3275
- if (
3276
- record.subscribers.size === 0
3277
- && this.watchers.get(root)?.generation === record.generation
3278
- ) {
3101
+ if (record.subscribers.size === 0 && this.watchers.get(root)?.generation === record.generation) {
3279
3102
  this.watchers.delete(root);
3280
3103
  await this.closeWorkspaceWatcherRecord(record);
3281
3104
  }
3282
3105
  };
3283
3106
  }
3284
-
3285
3107
  async closeWorkspaceWatcherRecord(record) {
3286
3108
  if (record.closePromise) return record.closePromise;
3287
3109
  record.cancelled = true;
@@ -3290,40 +3112,39 @@ class WorkspaceFileService {
3290
3112
  await record.ready?.catch(() => false);
3291
3113
  if (!record.watcher) return;
3292
3114
  const closeResult = record.watcher.close();
3293
- if (closeResult && typeof closeResult.then === 'function') await closeResult;
3115
+ if (closeResult && typeof closeResult.then === "function") await closeResult;
3294
3116
  })();
3295
3117
  return record.closePromise;
3296
3118
  }
3297
-
3298
3119
  async dispose() {
3299
3120
  const watchers = Array.from(this.watchers.values());
3300
3121
  this.watchers.clear();
3301
3122
  this.disposed = true;
3302
3123
  this.watcherLifecycleGeneration += 1;
3303
3124
  this.gitStatusCache.clear();
3304
- await Promise.all(watchers.map(record => this.closeWorkspaceWatcherRecord(record)));
3125
+ await Promise.all(watchers.map((record) => this.closeWorkspaceWatcherRecord(record)));
3305
3126
  if (this.ownsCommandRunner) {
3306
3127
  this.commandRunner.dispose();
3307
3128
  }
3308
3129
  }
3309
3130
  }
3310
-
3311
- module.exports = {
3312
- WorkspaceFileService,
3313
- WorkspaceFileError,
3131
+ // Annotate the CommonJS export names for ESM import in node:
3132
+ 0 && (module.exports = {
3314
3133
  DEFAULT_MAX_FILE_SIZE,
3315
3134
  DEFAULT_MAX_WRITE_SIZE,
3316
3135
  DEFAULT_WATCH_DEPTH,
3317
- isPackagedRuntime,
3318
- parseGitBlamePorcelain,
3319
- parseUnifiedDiffRows,
3136
+ WorkspaceFileError,
3137
+ WorkspaceFileService,
3320
3138
  createAddedFileLineChangesHunk,
3321
- parseUnifiedDiffHunks,
3322
- selectUnifiedDiffHunk,
3323
- resolveCommandRunnerNodePath,
3139
+ isPackagedRuntime,
3324
3140
  normalizeGitHistoryLimit,
3325
3141
  normalizeGitHistorySkip,
3142
+ parseGitBlamePorcelain,
3326
3143
  parseGitHistoryChanges,
3327
3144
  parseGitHistoryLog,
3328
3145
  parseGitHistoryReferences,
3329
- };
3146
+ parseUnifiedDiffHunks,
3147
+ parseUnifiedDiffRows,
3148
+ resolveCommandRunnerNodePath,
3149
+ selectUnifiedDiffHunk
3150
+ });