@ashx-j/lunr-dev 0.2.13-dev.7.1 → 0.2.16-dev.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts +14 -0
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +37 -3
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/index.d.ts +1 -15
- package/dist/builtin-extensions/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +7 -21
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +6 -2
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts +4 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js +163 -169
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts +46 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js +109 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js.map +1 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts +7 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +211 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts +0 -5
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js +0 -202
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.js +251 -94
- package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts +4 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js +10 -3
- package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.js +6 -1
- package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +2 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +38 -14
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +2 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +2 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.js +105 -54
- package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts +1 -1
- package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/gemini-search.js +82 -65
- package/dist/builtin-extensions/pi-web-access/gemini-search.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.js +2 -0
- package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.js +167 -77
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/lazy.d.ts +18 -0
- package/dist/builtin-extensions/pi-web-access/lazy.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/lazy.js +19 -0
- package/dist/builtin-extensions/pi-web-access/lazy.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +6 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js +17 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -0
- package/dist/catalog/models.json +1 -1
- package/dist/catalog/providers/amazon-bedrock.json +1 -1
- package/dist/catalog/providers/azure-openai-responses.json +1 -1
- package/dist/catalog/providers/fireworks.json +1 -1
- package/dist/catalog/providers/github-copilot.json +1 -1
- package/dist/catalog/providers/openai-codex.json +1 -1
- package/dist/catalog/providers/openai.json +1 -1
- package/dist/catalog/providers/opencode.json +1 -1
- package/dist/catalog/providers/openrouter.json +1 -1
- package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
- package/dist/catalog/publication.json +131 -3
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +1 -0
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli-runtime.d.ts +3 -0
- package/dist/cli-runtime.d.ts.map +1 -0
- package/dist/cli-runtime.js +73 -0
- package/dist/cli-runtime.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -53
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +3 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +56 -12
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/catalog-cache-scope.d.ts +3 -0
- package/dist/core/catalog-cache-scope.d.ts.map +1 -0
- package/dist/core/catalog-cache-scope.js +12 -0
- package/dist/core/catalog-cache-scope.js.map +1 -0
- package/dist/core/catalog-merge.d.ts +2 -0
- package/dist/core/catalog-merge.d.ts.map +1 -1
- package/dist/core/catalog-merge.js +29 -2
- package/dist/core/catalog-merge.js.map +1 -1
- package/dist/core/catalog-refresh-polling.d.ts +8 -0
- package/dist/core/catalog-refresh-polling.d.ts.map +1 -0
- package/dist/core/catalog-refresh-polling.js +30 -0
- package/dist/core/catalog-refresh-polling.js.map +1 -0
- package/dist/core/codex-catalog.d.ts +3 -0
- package/dist/core/codex-catalog.d.ts.map +1 -0
- package/dist/core/codex-catalog.js +15 -0
- package/dist/core/codex-catalog.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +10 -5
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/host-static.d.ts.map +1 -1
- package/dist/core/extensions/host-static.js +34 -12
- package/dist/core/extensions/host-static.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +10 -64
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +2 -0
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/live-catalog.d.ts +5 -2
- package/dist/core/live-catalog.d.ts.map +1 -1
- package/dist/core/live-catalog.js +108 -27
- package/dist/core/live-catalog.js.map +1 -1
- package/dist/core/model-runtime.d.ts +8 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +64 -12
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/official-catalog.d.ts +4 -0
- package/dist/core/official-catalog.d.ts.map +1 -1
- package/dist/core/official-catalog.js +86 -21
- package/dist/core/official-catalog.js.map +1 -1
- package/dist/core/permissions.d.ts +0 -2
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +10 -18
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +0 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/timings.d.ts +2 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +17 -2
- package/dist/core/timings.js.map +1 -1
- package/dist/gateway/commands.d.ts +1 -1
- package/dist/gateway/commands.d.ts.map +1 -1
- package/dist/gateway/commands.js +1 -18
- package/dist/gateway/commands.js.map +1 -1
- package/dist/main.d.ts +2 -2
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +66 -48
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +4 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +13 -22
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +141 -207
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +95 -84
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/node-runtime/agent-session-services-IFJMSX7Z.js +36 -0
- package/dist/node-runtime/benchmark-request-UC7PD3V3.js +57 -0
- package/dist/node-runtime/brave-UWDA3I22.js +143 -0
- package/dist/node-runtime/builtin-extensions-LYZ3BFWQ.js +28 -0
- package/dist/node-runtime/chunk-2QKLKMKC.js +173 -0
- package/dist/node-runtime/chunk-3GHXVUAK.js +21 -0
- package/dist/node-runtime/chunk-3RCWXWAY.js +466 -0
- package/dist/node-runtime/chunk-3WFAEVKS.js +115 -0
- package/dist/node-runtime/chunk-3X3KZKMI.js +49 -0
- package/dist/node-runtime/chunk-4HXHDDCF.js +16 -0
- package/dist/node-runtime/chunk-4TLO4PO2.js +3427 -0
- package/dist/node-runtime/chunk-4ZWGEEAS.js +69 -0
- package/dist/node-runtime/chunk-53YJI7JU.js +137 -0
- package/dist/node-runtime/chunk-5P7PZMRU.js +610 -0
- package/dist/node-runtime/chunk-7N3YHTYF.js +26 -0
- package/dist/node-runtime/chunk-7NBZ5P7I.js +24 -0
- package/dist/node-runtime/chunk-A2KQXUJD.js +105 -0
- package/dist/node-runtime/chunk-AD6VXAO7.js +342 -0
- package/dist/node-runtime/chunk-AHSTJBIB.js +64 -0
- package/dist/node-runtime/chunk-AJX357CL.js +20211 -0
- package/dist/node-runtime/chunk-ARC4KOTI.js +11178 -0
- package/dist/node-runtime/chunk-AUXVGBO4.js +19 -0
- package/dist/node-runtime/chunk-AY6CAJSL.js +30 -0
- package/dist/node-runtime/chunk-BHO7O6E5.js +296 -0
- package/dist/node-runtime/chunk-BI3DHYCN.js +8845 -0
- package/dist/node-runtime/chunk-BR7YDUKA.js +109 -0
- package/dist/node-runtime/chunk-BRJX2PRC.js +53 -0
- package/dist/node-runtime/chunk-C4O2RSQQ.js +37 -0
- package/dist/node-runtime/chunk-CCZJ3PYY.js +17 -0
- package/dist/node-runtime/chunk-CDFZEWPU.js +8388 -0
- package/dist/node-runtime/chunk-CGBXPL3E.js +1171 -0
- package/dist/node-runtime/chunk-CJECDQKP.js +19 -0
- package/dist/node-runtime/chunk-DAPBPCBP.js +53 -0
- package/dist/node-runtime/chunk-DDGMHUOW.js +305 -0
- package/dist/node-runtime/chunk-DMMTJQMQ.js +43 -0
- package/dist/node-runtime/chunk-DVGWF6A2.js +1180 -0
- package/dist/node-runtime/chunk-ECD5L7YI.js +1369 -0
- package/dist/node-runtime/chunk-EDBJNRSM.js +46 -0
- package/dist/node-runtime/chunk-F4VQFJI2.js +1269 -0
- package/dist/node-runtime/chunk-FVDOXRUD.js +96 -0
- package/dist/node-runtime/chunk-GZQHMEWL.js +126 -0
- package/dist/node-runtime/chunk-H2EOZYMP.js +57 -0
- package/dist/node-runtime/chunk-HQCKFM2O.js +56 -0
- package/dist/node-runtime/chunk-HYHJIRDW.js +19 -0
- package/dist/node-runtime/chunk-I6R76AG7.js +22 -0
- package/dist/node-runtime/chunk-IPA627GP.js +296 -0
- package/dist/node-runtime/chunk-JHYKXIMM.js +378 -0
- package/dist/node-runtime/chunk-KKSIVROT.js +88 -0
- package/dist/node-runtime/chunk-LZAW4ZX3.js +633 -0
- package/dist/node-runtime/chunk-M3LZLQW2.js +98 -0
- package/dist/node-runtime/chunk-MGWCVY6Y.js +47 -0
- package/dist/node-runtime/chunk-MJQDX3BA.js +72 -0
- package/dist/node-runtime/chunk-MKKH27PH.js +8956 -0
- package/dist/node-runtime/chunk-MX3GJNXJ.js +986 -0
- package/dist/node-runtime/chunk-N77MY65L.js +966 -0
- package/dist/node-runtime/chunk-NUIFPZWX.js +209 -0
- package/dist/node-runtime/chunk-OBMWP3I5.js +68 -0
- package/dist/node-runtime/chunk-OR4L3IB2.js +42 -0
- package/dist/node-runtime/chunk-PBC2Y6GS.js +903 -0
- package/dist/node-runtime/chunk-PCGNAY2O.js +1324 -0
- package/dist/node-runtime/chunk-PCIGF2XK.js +250 -0
- package/dist/node-runtime/chunk-PGAJ5MO5.js +7558 -0
- package/dist/node-runtime/chunk-PL6JKXWI.js +3622 -0
- package/dist/node-runtime/chunk-PVCDKGR6.js +55 -0
- package/dist/node-runtime/chunk-Q4Q6D4IY.js +364 -0
- package/dist/node-runtime/chunk-Q5ZU2VRT.js +83 -0
- package/dist/node-runtime/chunk-QFWG2RVL.js +5477 -0
- package/dist/node-runtime/chunk-QH42CDX3.js +9451 -0
- package/dist/node-runtime/chunk-RQ2Q5IYR.js +72 -0
- package/dist/node-runtime/chunk-RQ5DNUNG.js +429 -0
- package/dist/node-runtime/chunk-RRIZE6QQ.js +17 -0
- package/dist/node-runtime/chunk-SN6G4OU6.js +4753 -0
- package/dist/node-runtime/chunk-SWSN6HAB.js +53 -0
- package/dist/node-runtime/chunk-TQNSPERU.js +949 -0
- package/dist/node-runtime/chunk-VRB2IYGG.js +53 -0
- package/dist/node-runtime/chunk-VSELYKWU.js +772 -0
- package/dist/node-runtime/chunk-W3CVH5RX.js +62 -0
- package/dist/node-runtime/chunk-WJVFOIB5.js +24921 -0
- package/dist/node-runtime/chunk-WOVTOV5P.js +5653 -0
- package/dist/node-runtime/chunk-XBWPBZBN.js +125 -0
- package/dist/node-runtime/chunk-XTHQ7XR7.js +509 -0
- package/dist/node-runtime/chunk-YF63P5ZT.js +864 -0
- package/dist/node-runtime/chunk-YSKSB3BJ.js +295 -0
- package/dist/node-runtime/chunk-ZCHMJ34K.js +130 -0
- package/dist/node-runtime/chunk-ZKMFUD3Q.js +2834 -0
- package/dist/node-runtime/cli-runtime.js +81 -0
- package/dist/node-runtime/clipboard-OZ63BBC6.js +12 -0
- package/dist/node-runtime/clipboard-image-7QR6GXVA.js +13 -0
- package/dist/node-runtime/command-L3LAHDIH.js +13 -0
- package/dist/node-runtime/commands-RL5YBW5I.js +291 -0
- package/dist/node-runtime/config-FMEOZSWE.js +77 -0
- package/dist/node-runtime/curator-server-XCM43VEF.js +3816 -0
- package/dist/node-runtime/customize-XEXL2DGQ.js +12 -0
- package/dist/node-runtime/direct-tool-executor-JVDXTO7A.js +188 -0
- package/dist/node-runtime/exa-LL64QZR2.js +329 -0
- package/dist/node-runtime/export-html-KR26FOUZ.js +150 -0
- package/dist/node-runtime/extension-input-RV5N6APL.js +14 -0
- package/dist/node-runtime/extension-selector-CPD5LWDD.js +14 -0
- package/dist/node-runtime/extract-XLQSJN4V.js +17 -0
- package/dist/node-runtime/file-sync-HCOP2QVF.js +117 -0
- package/dist/node-runtime/gateway-X2TI5X7F.js +2484 -0
- package/dist/node-runtime/gemini-api-QE7ANVDZ.js +29 -0
- package/dist/node-runtime/gemini-search-U7SUMPQA.js +330 -0
- package/dist/node-runtime/gemini-url-context-PXWCBQHK.js +103 -0
- package/dist/node-runtime/gemini-web-MM446SEH.js +14 -0
- package/dist/node-runtime/github-extract-F64RZOEA.js +619 -0
- package/dist/node-runtime/goal-CZQ7FFQJ.js +1831 -0
- package/dist/node-runtime/index.js +859 -0
- package/dist/node-runtime/init-NJV2T55N.js +28 -0
- package/dist/node-runtime/install-cli-TH7IJJQQ.js +308 -0
- package/dist/node-runtime/interactive-mode-KOM35YF2.js +56 -0
- package/dist/node-runtime/interactive-view-U6OILKF3.js +249 -0
- package/dist/node-runtime/lsp-manager-3ASUSO4T.js +1104 -0
- package/dist/node-runtime/lunr-cron-YJ5UHYRD.js +268 -0
- package/dist/node-runtime/main.js +54 -0
- package/dist/node-runtime/mcp-auth-flow-QIXHW6QN.js +32 -0
- package/dist/node-runtime/mcp-panel-IIGMXQGP.js +501 -0
- package/dist/node-runtime/mcp-setup-panel-FEK7663I.js +355 -0
- package/dist/node-runtime/memory-cap-3S4XRLNP.js +20 -0
- package/dist/node-runtime/model-tiers-NJLHUEGK.js +20 -0
- package/dist/node-runtime/openai-search-B3MSNCQA.js +288 -0
- package/dist/node-runtime/package-manager-cli-TZZHJCVI.js +749 -0
- package/dist/node-runtime/parallel-AI2MNSCU.js +265 -0
- package/dist/node-runtime/parser-manager-TQHWYDH7.js +152 -0
- package/dist/node-runtime/pdf-extract-IPE6H4HP.js +57 -0
- package/dist/node-runtime/perplexity-QVJW3I35.js +12 -0
- package/dist/node-runtime/pi-intercom-EEMBGQID.js +2215 -0
- package/dist/node-runtime/pi-mcp-adapter-QFB37AVN.js +422 -0
- package/dist/node-runtime/pi-ollama-cloud-4VZRMULP.js +1440 -0
- package/dist/node-runtime/pi-prompt-template-model-4QT4LLXF.js +3946 -0
- package/dist/node-runtime/pi-subagents-7XJ4H53P.js +6023 -0
- package/dist/node-runtime/pi-web-access-DPGAVKOA.js +1864 -0
- package/dist/node-runtime/print-mode-VWIRFDAC.js +12 -0
- package/dist/node-runtime/proxy-modes-HEEFK4KC.js +685 -0
- package/dist/node-runtime/rpc-entry.js +62 -0
- package/dist/node-runtime/rpc-mode-SJNSOA4G.js +13 -0
- package/dist/node-runtime/runtime-bridges-PPHNBFWL.js +14 -0
- package/dist/node-runtime/session-manager-MQNBYACL.js +35 -0
- package/dist/node-runtime/session-selector-5AZLJS6U.js +14 -0
- package/dist/node-runtime/settings-manager-5LF2VQWC.js +21 -0
- package/dist/node-runtime/src-Y7JSCK64.js +2407 -0
- package/dist/node-runtime/subagent-executor-5RNDEPIJ.js +8483 -0
- package/dist/node-runtime/summary-review-YSMXKITO.js +228 -0
- package/dist/node-runtime/tavily-WEZFVFP7.js +154 -0
- package/dist/node-runtime/timings-UAD5BEAS.js +16 -0
- package/dist/node-runtime/tool-renderer-MV5O452K.js +204 -0
- package/dist/node-runtime/update-cli-A253SZ7P.js +73 -0
- package/dist/node-runtime/v4-5MNC3H7G.js +424 -0
- package/dist/node-runtime/video-extract-OYT5WKS5.js +304 -0
- package/dist/node-runtime/workspace-index-6DBXSTJ6.js +163 -0
- package/dist/node-runtime/youtube-extract-TQYKGFGS.js +264 -0
- package/dist/startup/benchmark-request.d.ts +3 -0
- package/dist/startup/benchmark-request.d.ts.map +1 -0
- package/dist/startup/benchmark-request.js +65 -0
- package/dist/startup/benchmark-request.js.map +1 -0
- package/dist/startup/interactive-view.d.ts +55 -0
- package/dist/startup/interactive-view.d.ts.map +1 -0
- package/dist/startup/interactive-view.js +332 -0
- package/dist/startup/interactive-view.js.map +1 -0
- package/dist/startup/launch-routing.d.ts.map +1 -1
- package/dist/startup/launch-routing.js +9 -10
- package/dist/startup/launch-routing.js.map +1 -1
- package/dist/startup/startup-milestones.d.ts +1 -1
- package/dist/startup/startup-milestones.d.ts.map +1 -1
- package/dist/startup/startup-milestones.js +3 -2
- package/dist/startup/startup-milestones.js.map +1 -1
- package/dist/utils/await-with-abort.d.ts +2 -0
- package/dist/utils/await-with-abort.d.ts.map +1 -0
- package/dist/utils/await-with-abort.js +19 -0
- package/dist/utils/await-with-abort.js.map +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +10 -4
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/docs/compaction.md +5 -3
- package/docs/development.md +5 -1
- package/docs/features.md +0 -1
- package/docs/interactive-startup.md +98 -0
- package/docs/model-catalog.md +29 -0
- package/docs/usage.md +0 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +20 -20
- package/dist/core/swarm.d.ts +0 -23
- package/dist/core/swarm.d.ts.map +0 -1
- package/dist/core/swarm.js +0 -45
- package/dist/core/swarm.js.map +0 -1
|
@@ -0,0 +1,2407 @@
|
|
|
1
|
+
import { createRequire as __lunrRequire } from "node:module"; const require = __lunrRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
MAX_FILE_SIZE,
|
|
4
|
+
MAX_INDEX_FILES,
|
|
5
|
+
SKIP_DIRS,
|
|
6
|
+
extractSymbols,
|
|
7
|
+
findDefinition,
|
|
8
|
+
getEnclosingDeclaration,
|
|
9
|
+
getNodeAtPosition,
|
|
10
|
+
getSignatureText,
|
|
11
|
+
getSyntaxErrors
|
|
12
|
+
} from "./chunk-BHO7O6E5.js";
|
|
13
|
+
import {
|
|
14
|
+
require_api2 as require_api
|
|
15
|
+
} from "./chunk-SN6G4OU6.js";
|
|
16
|
+
import {
|
|
17
|
+
getLanguageIdFromPath
|
|
18
|
+
} from "./chunk-3X3KZKMI.js";
|
|
19
|
+
import {
|
|
20
|
+
awaitWithAbort
|
|
21
|
+
} from "./chunk-RRIZE6QQ.js";
|
|
22
|
+
import {
|
|
23
|
+
DEFAULT_MAX_BYTES,
|
|
24
|
+
DEFAULT_MAX_LINES,
|
|
25
|
+
truncateHead
|
|
26
|
+
} from "./chunk-53YJI7JU.js";
|
|
27
|
+
import {
|
|
28
|
+
typebox_exports
|
|
29
|
+
} from "./chunk-PL6JKXWI.js";
|
|
30
|
+
import {
|
|
31
|
+
isEditToolResult,
|
|
32
|
+
isReadToolResult,
|
|
33
|
+
isWriteToolResult
|
|
34
|
+
} from "./chunk-OR4L3IB2.js";
|
|
35
|
+
import {
|
|
36
|
+
__toESM
|
|
37
|
+
} from "./chunk-BRJX2PRC.js";
|
|
38
|
+
|
|
39
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/index.js
|
|
40
|
+
var import_vscode_languageserver_protocol3 = __toESM(require_api(), 1);
|
|
41
|
+
|
|
42
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/runtime.js
|
|
43
|
+
var defaultLoaders = {
|
|
44
|
+
loadLspManager: () => import("./lsp-manager-3ASUSO4T.js"),
|
|
45
|
+
loadFileSync: () => import("./file-sync-HCOP2QVF.js"),
|
|
46
|
+
loadTreeSitter: () => import("./parser-manager-TQHWYDH7.js"),
|
|
47
|
+
loadWorkspaceIndex: () => import("./workspace-index-6DBXSTJ6.js")
|
|
48
|
+
}, LspRuntimeHost = class {
|
|
49
|
+
generation = 0;
|
|
50
|
+
services = null;
|
|
51
|
+
initPromise = null;
|
|
52
|
+
bindOptions = null;
|
|
53
|
+
loaders;
|
|
54
|
+
constructor(loaders) {
|
|
55
|
+
this.loaders = { ...defaultLoaders, ...loaders };
|
|
56
|
+
}
|
|
57
|
+
getGeneration() {
|
|
58
|
+
return this.generation;
|
|
59
|
+
}
|
|
60
|
+
hasServices() {
|
|
61
|
+
return this.services !== null;
|
|
62
|
+
}
|
|
63
|
+
getServicesIfReady() {
|
|
64
|
+
return this.services;
|
|
65
|
+
}
|
|
66
|
+
getBindOptions() {
|
|
67
|
+
return this.bindOptions;
|
|
68
|
+
}
|
|
69
|
+
bindSession(options) {
|
|
70
|
+
this.generation++;
|
|
71
|
+
let previous = this.services;
|
|
72
|
+
this.services = null, this.initPromise = null, this.bindOptions = options, previous && this.shutdownServices(previous);
|
|
73
|
+
}
|
|
74
|
+
setPendingProvider(provider) {
|
|
75
|
+
this.bindOptions && (this.bindOptions = { ...this.bindOptions, pendingProvider: provider }), this.services && provider && this.services.manager.setWorkspaceProvider(provider);
|
|
76
|
+
}
|
|
77
|
+
async ensureServices(fallbackOptions) {
|
|
78
|
+
if (this.services)
|
|
79
|
+
return this.services;
|
|
80
|
+
if (this.initPromise)
|
|
81
|
+
return this.initPromise;
|
|
82
|
+
if (!this.bindOptions) {
|
|
83
|
+
if (!fallbackOptions)
|
|
84
|
+
throw new Error("LSP runtime is not bound to a session");
|
|
85
|
+
this.bindOptions = fallbackOptions;
|
|
86
|
+
}
|
|
87
|
+
let gen = this.generation, pending = this.loadServices(gen);
|
|
88
|
+
this.initPromise = pending;
|
|
89
|
+
try {
|
|
90
|
+
return await pending;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw this.generation === gen && this.initPromise === pending && (this.initPromise = null), error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async shutdown() {
|
|
96
|
+
this.generation++;
|
|
97
|
+
let previous = this.services;
|
|
98
|
+
this.services = null, this.initPromise = null, this.bindOptions = null, previous && await this.shutdownServices(previous);
|
|
99
|
+
}
|
|
100
|
+
async loadServices(gen) {
|
|
101
|
+
let [managerMod, fileSyncMod, treeSitterMod, workspaceIndexMod] = await Promise.all([
|
|
102
|
+
this.loaders.loadLspManager(),
|
|
103
|
+
this.loaders.loadFileSync(),
|
|
104
|
+
this.loaders.loadTreeSitter(),
|
|
105
|
+
this.loaders.loadWorkspaceIndex()
|
|
106
|
+
]), options = this.bindOptions;
|
|
107
|
+
if (gen !== this.generation || !options)
|
|
108
|
+
throw new Error("LSP runtime initialization aborted: session replaced");
|
|
109
|
+
let manager = new managerMod.LspManager(options.cwd, void 0, options.callbacks, void 0, options.pendingProvider ?? void 0), treeSitter = new treeSitterMod.TreeSitterManager();
|
|
110
|
+
try {
|
|
111
|
+
let fileSync = new fileSyncMod.FileSync(manager);
|
|
112
|
+
fileSync.setSyntheticDotChecker(options.syntheticDotChecker);
|
|
113
|
+
let workspaceIndex = new workspaceIndexMod.WorkspaceIndex(options.cwd, treeSitter);
|
|
114
|
+
if (fileSync.setTreeSitter(treeSitter, workspaceIndex), options.configureManager?.(manager), gen !== this.generation)
|
|
115
|
+
throw new Error("LSP runtime initialization aborted: session replaced");
|
|
116
|
+
return this.services = { manager, fileSync, treeSitter, workspaceIndex }, this.services;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
throw await manager.shutdownAll().catch(() => {
|
|
119
|
+
}), treeSitter.shutdown(), error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async shutdownServices(services) {
|
|
123
|
+
await services.manager.shutdownAll().catch(() => {
|
|
124
|
+
}), services.treeSitter.shutdown();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/diagnostics.js
|
|
129
|
+
var import_vscode_languageserver_protocol = __toESM(require_api(), 1);
|
|
130
|
+
import { Text } from "@ashx-j/lunr-tui";
|
|
131
|
+
|
|
132
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/resolve-provider.js
|
|
133
|
+
function resolveProvider(filePath, manager, treeSitter) {
|
|
134
|
+
let languageId = manager.getLanguageId(filePath);
|
|
135
|
+
if (languageId) {
|
|
136
|
+
if (manager.getRunningClient(languageId))
|
|
137
|
+
return { type: "lsp" };
|
|
138
|
+
if (manager.isServerStarting(languageId))
|
|
139
|
+
return treeSitter?.hasGrammar(languageId) ? { type: "tree-sitter", languageId } : { type: "none", reason: `LSP server for ${languageId} is still starting up. Try again in a moment.` };
|
|
140
|
+
}
|
|
141
|
+
if (treeSitter) {
|
|
142
|
+
let tsLang = treeSitter.getLanguageId(filePath);
|
|
143
|
+
if (tsLang && treeSitter.hasGrammar(tsLang))
|
|
144
|
+
return { type: "tree-sitter", languageId: tsLang };
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
type: "none",
|
|
148
|
+
reason: `No code intelligence available for ${(filePath.match(/\.[^.]+$/)?.[0] ?? "") || "this file type"}. No LSP server is running and no tree-sitter grammar is available.`
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/diagnostics.js
|
|
153
|
+
import { readFile } from "node:fs/promises";
|
|
154
|
+
import { relative } from "node:path";
|
|
155
|
+
import { fileURLToPath } from "node:url";
|
|
156
|
+
function severityToString(severity) {
|
|
157
|
+
switch (severity) {
|
|
158
|
+
case import_vscode_languageserver_protocol.DiagnosticSeverity.Error:
|
|
159
|
+
return "error";
|
|
160
|
+
case import_vscode_languageserver_protocol.DiagnosticSeverity.Warning:
|
|
161
|
+
return "warning";
|
|
162
|
+
case import_vscode_languageserver_protocol.DiagnosticSeverity.Information:
|
|
163
|
+
return "info";
|
|
164
|
+
case import_vscode_languageserver_protocol.DiagnosticSeverity.Hint:
|
|
165
|
+
return "hint";
|
|
166
|
+
default:
|
|
167
|
+
return "unknown";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function formatDiagnostic(diag, filePath) {
|
|
171
|
+
let line = diag.range.start.line + 1, col = diag.range.start.character + 1, sev = severityToString(diag.severity), source = diag.source ? ` [${diag.source}]` : "", code = diag.code !== void 0 ? ` (${diag.code})` : "";
|
|
172
|
+
return `${filePath}:${line}:${col} ${sev}: ${diag.message}${code}${source}`;
|
|
173
|
+
}
|
|
174
|
+
var DiagnosticsParams = typebox_exports.Object({
|
|
175
|
+
path: typebox_exports.String({ description: 'File path to get diagnostics for. Pass "*" to get all workspace diagnostics from all running LSP servers.' })
|
|
176
|
+
});
|
|
177
|
+
function createDiagnosticsTool(manager, treeSitter) {
|
|
178
|
+
return {
|
|
179
|
+
name: "lsp_diagnostics",
|
|
180
|
+
label: "LSP Diagnostics",
|
|
181
|
+
description: 'Get compilation errors and warnings from the LSP server. Pass a file path to check a single file, or pass "*" to get all cached diagnostics across the workspace.',
|
|
182
|
+
promptSnippet: 'Get compiler errors, warnings, and hints for a source file via LSP. Pass path="*" to get all workspace diagnostics.',
|
|
183
|
+
promptGuidelines: [
|
|
184
|
+
"After making code changes with edit or write, use lsp_diagnostics to check for compilation errors before moving on.",
|
|
185
|
+
'To review all workspace diagnostics at once, call lsp_diagnostics with path="*" \u2014 this returns all cached diagnostics from running LSP servers without needing to check files individually.'
|
|
186
|
+
],
|
|
187
|
+
parameters: DiagnosticsParams,
|
|
188
|
+
async execute(_toolCallId, params) {
|
|
189
|
+
let filePath = params.path.replace(/^@/, "");
|
|
190
|
+
if (filePath === "*" || filePath === "")
|
|
191
|
+
return executeWorkspaceDiagnostics(manager);
|
|
192
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
193
|
+
if (client) {
|
|
194
|
+
let uri = manager.getFileUri(filePath), diagnostics = client.getDiagnostics(uri);
|
|
195
|
+
if (diagnostics.length === 0)
|
|
196
|
+
return { content: [{ type: "text", text: "No diagnostics (clean)." }], details: { count: 0 } };
|
|
197
|
+
let sorted = [...diagnostics].sort((a, b) => (a.severity ?? 99) - (b.severity ?? 99)), relPath = relative(manager.resolvePath("."), manager.resolvePath(filePath)), output = sorted.map((d) => formatDiagnostic(d, relPath)).join(`
|
|
198
|
+
`), errors = sorted.filter((d) => d.severity === import_vscode_languageserver_protocol.DiagnosticSeverity.Error).length, warnings = sorted.filter((d) => d.severity === import_vscode_languageserver_protocol.DiagnosticSeverity.Warning).length, other = sorted.length - errors - warnings, summary = [
|
|
199
|
+
errors > 0 ? `${errors} error(s)` : null,
|
|
200
|
+
warnings > 0 ? `${warnings} warning(s)` : null,
|
|
201
|
+
other > 0 ? `${other} other` : null
|
|
202
|
+
].filter(Boolean).join(", "), truncation = truncateHead(output, { maxLines: 2e3, maxBytes: 51200 }), resultText = `${summary}
|
|
203
|
+
|
|
204
|
+
${truncation.content}`;
|
|
205
|
+
return truncation.truncated && (resultText += `
|
|
206
|
+
|
|
207
|
+
[Output truncated: showing ${truncation.outputLines} of ${truncation.totalLines} diagnostics]`), {
|
|
208
|
+
content: [{ type: "text", text: resultText }],
|
|
209
|
+
details: { count: sorted.length, errors, warnings }
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (treeSitter && resolveProvider(filePath, manager, treeSitter).type === "tree-sitter")
|
|
213
|
+
try {
|
|
214
|
+
let absPath = manager.resolvePath(filePath), content = await readFile(absPath, "utf-8"), tree = await treeSitter.parse(absPath, content);
|
|
215
|
+
if (tree) {
|
|
216
|
+
let syntaxErrors = getSyntaxErrors(tree);
|
|
217
|
+
if (syntaxErrors.length === 0)
|
|
218
|
+
return { content: [{ type: "text", text: "No syntax errors detected. [tree-sitter \u2014 syntax only, no type checking]" }], details: { count: 0 } };
|
|
219
|
+
let relPath = relative(manager.resolvePath("."), absPath), output = syntaxErrors.map((e) => `${relPath}:${e.line + 1}:${e.character + 1} error: ${e.message}`).join(`
|
|
220
|
+
`), truncation = truncateHead(output, { maxLines: 2e3, maxBytes: 51200 }), resultText = `${syntaxErrors.length} syntax error(s) [tree-sitter \u2014 syntax only, no type checking]
|
|
221
|
+
|
|
222
|
+
${truncation.content}`;
|
|
223
|
+
return truncation.truncated && (resultText += `
|
|
224
|
+
|
|
225
|
+
[Output truncated: showing ${truncation.outputLines} of ${truncation.totalLines} diagnostics]`), {
|
|
226
|
+
content: [{ type: "text", text: resultText }],
|
|
227
|
+
details: { count: syntaxErrors.length, errors: syntaxErrors.length }
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: "text", text: manager.getUnavailableReason(filePath) }],
|
|
234
|
+
details: { count: 0 }
|
|
235
|
+
};
|
|
236
|
+
},
|
|
237
|
+
renderCall(args, theme) {
|
|
238
|
+
let text = theme.fg("toolTitle", theme.bold("lsp_diagnostics "));
|
|
239
|
+
return args.path && args.path !== "*" ? text += theme.fg("accent", args.path) : text += theme.fg("dim", "(workspace)"), new Text(text, 0, 0);
|
|
240
|
+
},
|
|
241
|
+
renderResult(result, { expanded, isPartial }, theme) {
|
|
242
|
+
if (isPartial)
|
|
243
|
+
return new Text(theme.fg("warning", "Checking..."), 0, 0);
|
|
244
|
+
let details = result.details;
|
|
245
|
+
if (!details || details.count === 0)
|
|
246
|
+
return new Text(theme.fg("success", "\u2713 No diagnostics"), 0, 0);
|
|
247
|
+
let text = "";
|
|
248
|
+
if (details.errors && details.errors > 0 && (text += theme.fg("error", `${details.errors} error(s)`)), details.warnings && details.warnings > 0 && (text && (text += " "), text += theme.fg("warning", `${details.warnings} warning(s)`)), details.files && details.files > 0 && (text += theme.fg("dim", ` in ${details.files} file(s)`)), expanded) {
|
|
249
|
+
let content = result.content[0];
|
|
250
|
+
if (content?.type === "text") {
|
|
251
|
+
let lines = content.text.split(`
|
|
252
|
+
`).slice(0, 30);
|
|
253
|
+
for (let line of lines)
|
|
254
|
+
text += `
|
|
255
|
+
${theme.fg("dim", line)}`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return new Text(text, 0, 0);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function executeWorkspaceDiagnostics(manager) {
|
|
263
|
+
let running = manager.getStatus().filter((s) => s.running);
|
|
264
|
+
if (running.length === 0)
|
|
265
|
+
return {
|
|
266
|
+
content: [{ type: "text", text: "No LSP servers are running. Use lsp_diagnostics with a file path to start a server and check that file." }],
|
|
267
|
+
details: { count: 0 }
|
|
268
|
+
};
|
|
269
|
+
let rootDir = manager.resolvePath("."), allDiagnostics = [];
|
|
270
|
+
for (let status of running) {
|
|
271
|
+
let client = manager.getRunningClient(status.languageId);
|
|
272
|
+
if (!client)
|
|
273
|
+
continue;
|
|
274
|
+
let diagMap = client.getAllDiagnostics();
|
|
275
|
+
for (let [uri, diagnostics] of diagMap) {
|
|
276
|
+
if (diagnostics.length === 0)
|
|
277
|
+
continue;
|
|
278
|
+
let absPath;
|
|
279
|
+
try {
|
|
280
|
+
absPath = fileURLToPath(uri);
|
|
281
|
+
} catch {
|
|
282
|
+
absPath = uri;
|
|
283
|
+
}
|
|
284
|
+
let relPath = relative(rootDir, absPath);
|
|
285
|
+
for (let diag of diagnostics)
|
|
286
|
+
allDiagnostics.push({ relPath, diag });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (allDiagnostics.length === 0) {
|
|
290
|
+
let langs = running.map((s) => s.languageId).join(", ");
|
|
291
|
+
return {
|
|
292
|
+
content: [{ type: "text", text: `No diagnostics across ${running.length} running server(s) (${langs}).` }],
|
|
293
|
+
details: { count: 0 }
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
allDiagnostics.sort((a, b) => {
|
|
297
|
+
let sevDiff = (a.diag.severity ?? 99) - (b.diag.severity ?? 99);
|
|
298
|
+
return sevDiff !== 0 ? sevDiff : a.relPath.localeCompare(b.relPath);
|
|
299
|
+
});
|
|
300
|
+
let errors = allDiagnostics.filter((d) => d.diag.severity === import_vscode_languageserver_protocol.DiagnosticSeverity.Error).length, warnings = allDiagnostics.filter((d) => d.diag.severity === import_vscode_languageserver_protocol.DiagnosticSeverity.Warning).length, other = allDiagnostics.length - errors - warnings, fileCount = new Set(allDiagnostics.map((d) => d.relPath)).size, summary = [
|
|
301
|
+
`${allDiagnostics.length} diagnostic(s) in ${fileCount} file(s)`,
|
|
302
|
+
errors > 0 ? `${errors} error(s)` : null,
|
|
303
|
+
warnings > 0 ? `${warnings} warning(s)` : null,
|
|
304
|
+
other > 0 ? `${other} other` : null
|
|
305
|
+
].filter(Boolean).join(", "), output = allDiagnostics.map((d) => formatDiagnostic(d.diag, d.relPath)).join(`
|
|
306
|
+
`), truncation = truncateHead(output, { maxLines: 2e3, maxBytes: 51200 }), resultText = `${summary}
|
|
307
|
+
|
|
308
|
+
${truncation.content}`;
|
|
309
|
+
return truncation.truncated && (resultText += `
|
|
310
|
+
|
|
311
|
+
[Output truncated: showing ${truncation.outputLines} of ${truncation.totalLines} diagnostics]`), {
|
|
312
|
+
content: [{ type: "text", text: resultText }],
|
|
313
|
+
details: { count: allDiagnostics.length, errors, warnings, files: fileCount }
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/hover.js
|
|
318
|
+
import { Text as Text2 } from "@ashx-j/lunr-tui";
|
|
319
|
+
|
|
320
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/shared/resolve-position.js
|
|
321
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
322
|
+
async function resolveSymbolPosition(filePath, query, manager, treeSitter) {
|
|
323
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
324
|
+
if (client) {
|
|
325
|
+
let uri = manager.getFileUri(filePath);
|
|
326
|
+
try {
|
|
327
|
+
let symbols = await client.sendRequest("textDocument/documentSymbol", { textDocument: { uri } });
|
|
328
|
+
if (symbols && symbols.length > 0) {
|
|
329
|
+
let match = findInDocumentSymbols(symbols, query);
|
|
330
|
+
if (match)
|
|
331
|
+
return match;
|
|
332
|
+
}
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (treeSitter)
|
|
337
|
+
try {
|
|
338
|
+
let absPath = manager.resolvePath(filePath), content = await readFile2(absPath, "utf-8"), languageId = getLanguageIdFromPath(filePath);
|
|
339
|
+
if (languageId) {
|
|
340
|
+
let tree = await treeSitter.parse(absPath, content);
|
|
341
|
+
if (tree) {
|
|
342
|
+
let symbols = extractSymbols(tree, languageId), match = findInSymbolInfos(symbols, query);
|
|
343
|
+
if (match)
|
|
344
|
+
return match;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
async function getSymbolNames(filePath, manager, treeSitter) {
|
|
352
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
353
|
+
if (client) {
|
|
354
|
+
let uri = manager.getFileUri(filePath);
|
|
355
|
+
try {
|
|
356
|
+
let symbols = await client.sendRequest("textDocument/documentSymbol", { textDocument: { uri } });
|
|
357
|
+
if (symbols && symbols.length > 0)
|
|
358
|
+
return "selectionRange" in symbols[0] ? symbols.map((s) => s.name) : symbols.map((s) => s.name);
|
|
359
|
+
} catch {
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (treeSitter)
|
|
363
|
+
try {
|
|
364
|
+
let absPath = manager.resolvePath(filePath), content = await readFile2(absPath, "utf-8"), languageId = getLanguageIdFromPath(filePath);
|
|
365
|
+
if (languageId) {
|
|
366
|
+
let tree = await treeSitter.parse(absPath, content);
|
|
367
|
+
if (tree)
|
|
368
|
+
return extractSymbols(tree, languageId).map((s) => s.name);
|
|
369
|
+
}
|
|
370
|
+
} catch {
|
|
371
|
+
}
|
|
372
|
+
return [];
|
|
373
|
+
}
|
|
374
|
+
function findInDocumentSymbols(symbols, query) {
|
|
375
|
+
return symbols.length === 0 ? null : "selectionRange" in symbols[0] ? findInHierarchicalSymbols(symbols, query) : findInFlatSymbols(symbols, query);
|
|
376
|
+
}
|
|
377
|
+
function findInHierarchicalSymbols(symbols, query) {
|
|
378
|
+
let candidates = flattenDocumentSymbols(symbols);
|
|
379
|
+
return matchCandidates(candidates, query, "lsp");
|
|
380
|
+
}
|
|
381
|
+
function flattenDocumentSymbols(symbols, parent) {
|
|
382
|
+
let result = [];
|
|
383
|
+
for (let sym of symbols)
|
|
384
|
+
result.push({
|
|
385
|
+
name: sym.name,
|
|
386
|
+
line: sym.selectionRange.start.line + 1,
|
|
387
|
+
character: sym.selectionRange.start.character + 1,
|
|
388
|
+
parent
|
|
389
|
+
}), sym.children && sym.children.length > 0 && result.push(...flattenDocumentSymbols(sym.children, sym.name));
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
function findInFlatSymbols(symbols, query) {
|
|
393
|
+
let candidates = symbols.map((sym) => ({
|
|
394
|
+
name: sym.name,
|
|
395
|
+
line: sym.location.range.start.line + 1,
|
|
396
|
+
character: sym.location.range.start.character + 1,
|
|
397
|
+
parent: sym.containerName ?? void 0
|
|
398
|
+
}));
|
|
399
|
+
return matchCandidates(candidates, query, "lsp");
|
|
400
|
+
}
|
|
401
|
+
function findInSymbolInfos(symbols, query) {
|
|
402
|
+
let candidates = flattenSymbolInfos(symbols);
|
|
403
|
+
return matchCandidates(candidates, query, "tree-sitter");
|
|
404
|
+
}
|
|
405
|
+
function flattenSymbolInfos(symbols, parent) {
|
|
406
|
+
let result = [];
|
|
407
|
+
for (let sym of symbols)
|
|
408
|
+
result.push({
|
|
409
|
+
name: sym.name,
|
|
410
|
+
line: sym.line,
|
|
411
|
+
character: 1,
|
|
412
|
+
// tree-sitter symbols don't have column precision for the name
|
|
413
|
+
parent
|
|
414
|
+
}), sym.children && sym.children.length > 0 && result.push(...flattenSymbolInfos(sym.children, sym.name));
|
|
415
|
+
return result;
|
|
416
|
+
}
|
|
417
|
+
function matchCandidates(candidates, query, source) {
|
|
418
|
+
let dotIndex = query.lastIndexOf("."), parentFilter, symbolQuery;
|
|
419
|
+
if (dotIndex > 0 ? (parentFilter = query.slice(0, dotIndex), symbolQuery = query.slice(dotIndex + 1)) : symbolQuery = query, parentFilter) {
|
|
420
|
+
let qualified = candidates.filter((c) => c.parent?.toLowerCase() === parentFilter.toLowerCase()), match = matchByPriority(qualified, symbolQuery, source);
|
|
421
|
+
if (match)
|
|
422
|
+
return match;
|
|
423
|
+
}
|
|
424
|
+
return matchByPriority(candidates, symbolQuery, source);
|
|
425
|
+
}
|
|
426
|
+
function matchByPriority(candidates, query, source) {
|
|
427
|
+
let queryLower = query.toLowerCase(), exact = candidates.find((c) => c.name === query);
|
|
428
|
+
if (exact)
|
|
429
|
+
return { line: exact.line, character: exact.character, symbolName: exact.name, source };
|
|
430
|
+
let caseInsensitive = candidates.find((c) => c.name.toLowerCase() === queryLower);
|
|
431
|
+
if (caseInsensitive)
|
|
432
|
+
return { line: caseInsensitive.line, character: caseInsensitive.character, symbolName: caseInsensitive.name, source };
|
|
433
|
+
let substring = candidates.find((c) => c.name.toLowerCase().includes(queryLower));
|
|
434
|
+
return substring ? { line: substring.line, character: substring.character, symbolName: substring.name, source } : null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/hover.js
|
|
438
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
439
|
+
function formatHoverContent(hover) {
|
|
440
|
+
let contents = hover.contents;
|
|
441
|
+
return typeof contents == "string" ? contents : "kind" in contents && "value" in contents ? contents.value : "language" in contents && "value" in contents ? `\`\`\`${contents.language}
|
|
442
|
+
${contents.value}
|
|
443
|
+
\`\`\`` : Array.isArray(contents) ? contents.map((c) => typeof c == "string" ? c : "language" in c && "value" in c ? `\`\`\`${c.language}
|
|
444
|
+
${c.value}
|
|
445
|
+
\`\`\`` : String(c)).join(`
|
|
446
|
+
|
|
447
|
+
`) : String(contents);
|
|
448
|
+
}
|
|
449
|
+
var HoverParams = typebox_exports.Object({
|
|
450
|
+
path: typebox_exports.String({ description: "File path" }),
|
|
451
|
+
line: typebox_exports.Optional(typebox_exports.Number({ description: "Line number (1-indexed). Required unless query is provided." })),
|
|
452
|
+
character: typebox_exports.Optional(typebox_exports.Number({ description: "Column number (1-indexed). Required unless query is provided." })),
|
|
453
|
+
query: typebox_exports.Optional(typebox_exports.String({ description: "Symbol name to find in the file. Alternative to line/character \u2014 resolves the symbol's position automatically." }))
|
|
454
|
+
});
|
|
455
|
+
function createHoverTool(manager, treeSitter) {
|
|
456
|
+
return {
|
|
457
|
+
name: "lsp_hover",
|
|
458
|
+
label: "LSP Hover",
|
|
459
|
+
description: "Get type information and documentation for a symbol at a specific position in a file. Line and character are 1-indexed.",
|
|
460
|
+
promptSnippet: "Get type info and docs for a symbol at a file position via LSP",
|
|
461
|
+
parameters: HoverParams,
|
|
462
|
+
async execute(_toolCallId, params) {
|
|
463
|
+
let filePath = params.path.replace(/^@/, ""), line = params.line, character = params.character, resolvedFrom;
|
|
464
|
+
if ((line === void 0 || character === void 0) && params.query) {
|
|
465
|
+
let resolved = await resolveSymbolPosition(filePath, params.query, manager, treeSitter);
|
|
466
|
+
if (resolved)
|
|
467
|
+
line = resolved.line, character = resolved.character, resolvedFrom = `Resolved "${params.query}" \u2192 ${resolved.symbolName} at ${line}:${character} [${resolved.source}]`;
|
|
468
|
+
else {
|
|
469
|
+
let names = await getSymbolNames(filePath, manager, treeSitter), hint = names.length > 0 ? `
|
|
470
|
+
Available symbols: ${names.slice(0, 20).join(", ")}` : "";
|
|
471
|
+
return { content: [{ type: "text", text: `Could not find symbol "${params.query}" in ${filePath}${hint}` }], details: { hasResult: !1 } };
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (line === void 0 || character === void 0)
|
|
475
|
+
return { content: [{ type: "text", text: "Either line/character or query is required." }], details: { hasResult: !1 } };
|
|
476
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
477
|
+
if (client) {
|
|
478
|
+
let uri = manager.getFileUri(filePath), position = { line: line - 1, character: character - 1 };
|
|
479
|
+
try {
|
|
480
|
+
let hover = await client.sendRequest("textDocument/hover", {
|
|
481
|
+
textDocument: { uri },
|
|
482
|
+
position
|
|
483
|
+
});
|
|
484
|
+
if (!hover)
|
|
485
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
486
|
+
|
|
487
|
+
No hover information available at this position.` : "No hover information available at this position." }], details: { hasResult: !1 } };
|
|
488
|
+
let hoverText = formatHoverContent(hover);
|
|
489
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
490
|
+
|
|
491
|
+
${hoverText}` : hoverText }], details: { hasResult: !0 } };
|
|
492
|
+
} catch (err) {
|
|
493
|
+
return { content: [{ type: "text", text: `LSP hover request failed: ${err.message}` }], details: { hasResult: !1 } };
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (treeSitter && resolveProvider(filePath, manager, treeSitter).type === "tree-sitter")
|
|
497
|
+
try {
|
|
498
|
+
let absPath = manager.resolvePath(filePath), content = await readFile3(absPath, "utf-8"), tree = await treeSitter.parse(absPath, content);
|
|
499
|
+
if (tree) {
|
|
500
|
+
let decl = getEnclosingDeclaration(tree, line - 1, character - 1);
|
|
501
|
+
if (decl) {
|
|
502
|
+
let sig = getSignatureText(decl), text2 = `${decl.type.replace(/_/g, " ")} [tree-sitter]
|
|
503
|
+
|
|
504
|
+
\`\`\`
|
|
505
|
+
${sig}
|
|
506
|
+
\`\`\``;
|
|
507
|
+
return resolvedFrom && (text2 = `${resolvedFrom}
|
|
508
|
+
|
|
509
|
+
${text2}`), { content: [{ type: "text", text: text2 }], details: { hasResult: !0 } };
|
|
510
|
+
}
|
|
511
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
512
|
+
|
|
513
|
+
No hover information available at this position. [tree-sitter]` : "No hover information available at this position. [tree-sitter]" }], details: { hasResult: !1 } };
|
|
514
|
+
}
|
|
515
|
+
} catch {
|
|
516
|
+
}
|
|
517
|
+
return { content: [{ type: "text", text: manager.getUnavailableReason(filePath) }], details: { hasResult: !1 } };
|
|
518
|
+
},
|
|
519
|
+
renderCall(args, theme) {
|
|
520
|
+
let text = theme.fg("toolTitle", theme.bold("lsp_hover "));
|
|
521
|
+
return args.query && !args.line ? (text += theme.fg("accent", `${args.path}`), text += theme.fg("muted", ` query="${args.query}"`)) : text += theme.fg("accent", `${args.path}:${args.line}:${args.character}`), new Text2(text, 0, 0);
|
|
522
|
+
},
|
|
523
|
+
renderResult(result, { isPartial }, theme) {
|
|
524
|
+
if (isPartial)
|
|
525
|
+
return new Text2(theme.fg("warning", "Looking up..."), 0, 0);
|
|
526
|
+
if (!result.details?.hasResult)
|
|
527
|
+
return new Text2(theme.fg("dim", "No info"), 0, 0);
|
|
528
|
+
let content = result.content[0];
|
|
529
|
+
if (content?.type === "text") {
|
|
530
|
+
let lines = content.text.split(`
|
|
531
|
+
`).slice(0, 5);
|
|
532
|
+
return new Text2(lines.map((l) => theme.fg("dim", l)).join(`
|
|
533
|
+
`), 0, 0);
|
|
534
|
+
}
|
|
535
|
+
return new Text2(theme.fg("dim", "No info"), 0, 0);
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/definition.js
|
|
541
|
+
import { Text as Text3 } from "@ashx-j/lunr-tui";
|
|
542
|
+
|
|
543
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/shared/format.js
|
|
544
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
545
|
+
import { relative as relative2 } from "node:path";
|
|
546
|
+
function formatLocation(loc, rootDir) {
|
|
547
|
+
try {
|
|
548
|
+
let absPath = fileURLToPath2(loc.uri);
|
|
549
|
+
return `${relative2(rootDir, absPath)}:${loc.range.start.line + 1}:${loc.range.start.character + 1}`;
|
|
550
|
+
} catch {
|
|
551
|
+
return `${loc.uri}:${loc.range.start.line + 1}:${loc.range.start.character + 1}`;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function formatLocationLink(link, rootDir) {
|
|
555
|
+
try {
|
|
556
|
+
let absPath = fileURLToPath2(link.targetUri);
|
|
557
|
+
return `${relative2(rootDir, absPath)}:${link.targetSelectionRange.start.line + 1}:${link.targetSelectionRange.start.character + 1}`;
|
|
558
|
+
} catch {
|
|
559
|
+
return `${link.targetUri}:${link.targetSelectionRange.start.line + 1}:${link.targetSelectionRange.start.character + 1}`;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/definition.js
|
|
564
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
565
|
+
import { relative as relative3 } from "node:path";
|
|
566
|
+
var DefinitionParams = typebox_exports.Object({
|
|
567
|
+
path: typebox_exports.String({ description: "File path" }),
|
|
568
|
+
line: typebox_exports.Optional(typebox_exports.Number({ description: "Line number (1-indexed). Required unless query is provided." })),
|
|
569
|
+
character: typebox_exports.Optional(typebox_exports.Number({ description: "Column number (1-indexed). Required unless query is provided." })),
|
|
570
|
+
query: typebox_exports.Optional(typebox_exports.String({ description: "Symbol name to find in the file. Alternative to line/character \u2014 resolves the symbol's position automatically." }))
|
|
571
|
+
});
|
|
572
|
+
function createDefinitionTool(manager, treeSitter, workspaceIndex) {
|
|
573
|
+
return {
|
|
574
|
+
name: "lsp_definition",
|
|
575
|
+
label: "LSP Definition",
|
|
576
|
+
description: "Go to the definition of a symbol at a specific position. Returns the file path and location of the definition. Line and character are 1-indexed.",
|
|
577
|
+
promptSnippet: "Jump to the definition of a symbol at a file position via LSP",
|
|
578
|
+
parameters: DefinitionParams,
|
|
579
|
+
async execute(_toolCallId, params) {
|
|
580
|
+
let filePath = params.path.replace(/^@/, ""), line = params.line, character = params.character, resolvedFrom;
|
|
581
|
+
if ((line === void 0 || character === void 0) && params.query) {
|
|
582
|
+
let resolved = await resolveSymbolPosition(filePath, params.query, manager, treeSitter);
|
|
583
|
+
if (resolved)
|
|
584
|
+
line = resolved.line, character = resolved.character, resolvedFrom = `Resolved "${params.query}" \u2192 ${resolved.symbolName} at ${line}:${character} [${resolved.source}]`;
|
|
585
|
+
else {
|
|
586
|
+
let names = await getSymbolNames(filePath, manager, treeSitter), hint = names.length > 0 ? `
|
|
587
|
+
Available symbols: ${names.slice(0, 20).join(", ")}` : "";
|
|
588
|
+
return { content: [{ type: "text", text: `Could not find symbol "${params.query}" in ${filePath}${hint}` }], details: { count: 0 } };
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (line === void 0 || character === void 0)
|
|
592
|
+
return { content: [{ type: "text", text: "Either line/character or query is required." }], details: { count: 0 } };
|
|
593
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
594
|
+
if (client) {
|
|
595
|
+
let uri = manager.getFileUri(filePath), position = { line: line - 1, character: character - 1 };
|
|
596
|
+
try {
|
|
597
|
+
let result = await client.sendRequest("textDocument/definition", {
|
|
598
|
+
textDocument: { uri },
|
|
599
|
+
position
|
|
600
|
+
});
|
|
601
|
+
if (!result)
|
|
602
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
603
|
+
|
|
604
|
+
No definition found.` : "No definition found." }], details: { count: 0 } };
|
|
605
|
+
let rootDir = manager.resolvePath("."), locations;
|
|
606
|
+
if (Array.isArray(result)) {
|
|
607
|
+
if (result.length === 0)
|
|
608
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
609
|
+
|
|
610
|
+
No definition found.` : "No definition found." }], details: { count: 0 } };
|
|
611
|
+
"targetUri" in result[0] ? locations = result.map((l) => formatLocationLink(l, rootDir)) : locations = result.map((l) => formatLocation(l, rootDir));
|
|
612
|
+
} else
|
|
613
|
+
locations = [formatLocation(result, rootDir)];
|
|
614
|
+
let text = locations.length === 1 ? `Definition: ${locations[0]}` : `Definitions:
|
|
615
|
+
${locations.map((l) => ` ${l}`).join(`
|
|
616
|
+
`)}`;
|
|
617
|
+
return resolvedFrom && (text = `${resolvedFrom}
|
|
618
|
+
|
|
619
|
+
${text}`), { content: [{ type: "text", text }], details: { count: locations.length } };
|
|
620
|
+
} catch (err) {
|
|
621
|
+
return { content: [{ type: "text", text: `LSP definition request failed: ${err.message}` }], details: { count: 0 } };
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (treeSitter) {
|
|
625
|
+
let provider = resolveProvider(filePath, manager, treeSitter);
|
|
626
|
+
if (provider.type === "tree-sitter")
|
|
627
|
+
try {
|
|
628
|
+
let absPath = manager.resolvePath(filePath), content = await readFile4(absPath, "utf-8"), tree = await treeSitter.parse(absPath, content);
|
|
629
|
+
if (tree) {
|
|
630
|
+
let node = getNodeAtPosition(tree, line - 1, character - 1);
|
|
631
|
+
if (node) {
|
|
632
|
+
let symbolName = node.text, rootDir = manager.resolvePath("."), relPath = relative3(rootDir, absPath), localDefs = findDefinition(tree, symbolName, provider.languageId);
|
|
633
|
+
if (localDefs.length > 0) {
|
|
634
|
+
let locs = localDefs.map((d) => `${relPath}:${d.line}:1`), text2 = locs.length === 1 ? `Definition [tree-sitter]: ${locs[0]}` : `Definitions [tree-sitter]:
|
|
635
|
+
${locs.map((l) => ` ${l}`).join(`
|
|
636
|
+
`)}`;
|
|
637
|
+
return resolvedFrom && (text2 = `${resolvedFrom}
|
|
638
|
+
|
|
639
|
+
${text2}`), { content: [{ type: "text", text: text2 }], details: { count: locs.length } };
|
|
640
|
+
}
|
|
641
|
+
if (workspaceIndex) {
|
|
642
|
+
await workspaceIndex.build();
|
|
643
|
+
let exact = workspaceIndex.search(symbolName).filter((e) => e.name === symbolName);
|
|
644
|
+
if (exact.length > 0) {
|
|
645
|
+
let rootDir2 = manager.resolvePath("."), locs = exact.slice(0, 10).map((e) => `${relative3(rootDir2, e.file)}:${e.line}:1`), text2 = locs.length === 1 ? `Definition [tree-sitter]: ${locs[0]}` : `Definitions [tree-sitter]:
|
|
646
|
+
${locs.map((l) => ` ${l}`).join(`
|
|
647
|
+
`)}`;
|
|
648
|
+
return resolvedFrom && (text2 = `${resolvedFrom}
|
|
649
|
+
|
|
650
|
+
${text2}`), { content: [{ type: "text", text: text2 }], details: { count: locs.length } };
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
let msg = `No definition found for "${symbolName}" [tree-sitter]`;
|
|
654
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
655
|
+
|
|
656
|
+
${msg}` : msg }], details: { count: 0 } };
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
} catch {
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return { content: [{ type: "text", text: manager.getUnavailableReason(filePath) }], details: { count: 0 } };
|
|
663
|
+
},
|
|
664
|
+
renderCall(args, theme) {
|
|
665
|
+
let text = theme.fg("toolTitle", theme.bold("lsp_definition "));
|
|
666
|
+
return args.query && !args.line ? (text += theme.fg("accent", `${args.path}`), text += theme.fg("muted", ` query="${args.query}"`)) : text += theme.fg("accent", `${args.path}:${args.line}:${args.character}`), new Text3(text, 0, 0);
|
|
667
|
+
},
|
|
668
|
+
renderResult(result, { isPartial }, theme) {
|
|
669
|
+
if (isPartial)
|
|
670
|
+
return new Text3(theme.fg("warning", "Resolving..."), 0, 0);
|
|
671
|
+
let content = result.content[0];
|
|
672
|
+
return content?.type === "text" ? new Text3(theme.fg("dim", content.text), 0, 0) : new Text3(theme.fg("dim", "No result"), 0, 0);
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/references.js
|
|
678
|
+
import { Text as Text4 } from "@ashx-j/lunr-tui";
|
|
679
|
+
var ReferencesParams = typebox_exports.Object({
|
|
680
|
+
path: typebox_exports.String({ description: "File path" }),
|
|
681
|
+
line: typebox_exports.Optional(typebox_exports.Number({ description: "Line number (1-indexed). Required unless query is provided." })),
|
|
682
|
+
character: typebox_exports.Optional(typebox_exports.Number({ description: "Column number (1-indexed). Required unless query is provided." })),
|
|
683
|
+
query: typebox_exports.Optional(typebox_exports.String({ description: "Symbol name to find in the file. Alternative to line/character \u2014 resolves the symbol's position automatically." })),
|
|
684
|
+
includeDeclaration: typebox_exports.Optional(typebox_exports.Boolean({ description: "Include the declaration in results (default: true)" }))
|
|
685
|
+
});
|
|
686
|
+
function createReferencesTool(manager, treeSitter) {
|
|
687
|
+
return {
|
|
688
|
+
name: "lsp_references",
|
|
689
|
+
label: "LSP References",
|
|
690
|
+
description: "Find all references to a symbol at a specific position. Returns a list of file locations. Line and character are 1-indexed.",
|
|
691
|
+
promptSnippet: "Find all references to a symbol at a file position via LSP",
|
|
692
|
+
parameters: ReferencesParams,
|
|
693
|
+
async execute(_toolCallId, params) {
|
|
694
|
+
let filePath = params.path.replace(/^@/, ""), line = params.line, character = params.character, resolvedFrom;
|
|
695
|
+
if ((line === void 0 || character === void 0) && params.query) {
|
|
696
|
+
let resolved = await resolveSymbolPosition(filePath, params.query, manager, treeSitter);
|
|
697
|
+
if (resolved)
|
|
698
|
+
line = resolved.line, character = resolved.character, resolvedFrom = `Resolved "${params.query}" \u2192 ${resolved.symbolName} at ${line}:${character} [${resolved.source}]`;
|
|
699
|
+
else {
|
|
700
|
+
let names = await getSymbolNames(filePath, manager, treeSitter), hint = names.length > 0 ? `
|
|
701
|
+
Available symbols: ${names.slice(0, 20).join(", ")}` : "";
|
|
702
|
+
return { content: [{ type: "text", text: `Could not find symbol "${params.query}" in ${filePath}${hint}` }], details: { count: 0 } };
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (line === void 0 || character === void 0)
|
|
706
|
+
return { content: [{ type: "text", text: "Either line/character or query is required." }], details: { count: 0 } };
|
|
707
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
708
|
+
if (!client)
|
|
709
|
+
return { content: [{ type: "text", text: manager.getUnavailableReason(filePath) }], details: { count: 0 } };
|
|
710
|
+
let uri = manager.getFileUri(filePath), position = { line: line - 1, character: character - 1 };
|
|
711
|
+
try {
|
|
712
|
+
let locations = await client.sendRequest("textDocument/references", {
|
|
713
|
+
textDocument: { uri },
|
|
714
|
+
position,
|
|
715
|
+
context: { includeDeclaration: params.includeDeclaration ?? !0 }
|
|
716
|
+
});
|
|
717
|
+
if (!locations || locations.length === 0)
|
|
718
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
719
|
+
|
|
720
|
+
No references found.` : "No references found." }], details: { count: 0 } };
|
|
721
|
+
let rootDir = manager.resolvePath("."), output = locations.map((l) => formatLocation(l, rootDir)).join(`
|
|
722
|
+
`), truncation = truncateHead(output, { maxLines: 2e3, maxBytes: 51200 }), resultText = `${locations.length} reference(s) found:
|
|
723
|
+
|
|
724
|
+
${truncation.content}`;
|
|
725
|
+
return truncation.truncated && (resultText += `
|
|
726
|
+
|
|
727
|
+
[Output truncated: showing ${truncation.outputLines} of ${truncation.totalLines} references]`), resolvedFrom && (resultText = `${resolvedFrom}
|
|
728
|
+
|
|
729
|
+
${resultText}`), { content: [{ type: "text", text: resultText }], details: { count: locations.length } };
|
|
730
|
+
} catch (err) {
|
|
731
|
+
return { content: [{ type: "text", text: `LSP references request failed: ${err.message}` }], details: { count: 0 } };
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
renderCall(args, theme) {
|
|
735
|
+
let text = theme.fg("toolTitle", theme.bold("lsp_references "));
|
|
736
|
+
return args.query && !args.line ? (text += theme.fg("accent", `${args.path}`), text += theme.fg("muted", ` query="${args.query}"`)) : text += theme.fg("accent", `${args.path}:${args.line}:${args.character}`), new Text4(text, 0, 0);
|
|
737
|
+
},
|
|
738
|
+
renderResult(result, { isPartial }, theme) {
|
|
739
|
+
if (isPartial)
|
|
740
|
+
return new Text4(theme.fg("warning", "Searching..."), 0, 0);
|
|
741
|
+
let details = result.details;
|
|
742
|
+
return !details || details.count === 0 ? new Text4(theme.fg("dim", "No references found"), 0, 0) : new Text4(theme.fg("success", `${details.count} reference(s)`), 0, 0);
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/symbols.js
|
|
748
|
+
var import_vscode_languageserver_protocol2 = __toESM(require_api(), 1);
|
|
749
|
+
import { Text as Text5 } from "@ashx-j/lunr-tui";
|
|
750
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
751
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
752
|
+
import { relative as relative4 } from "node:path";
|
|
753
|
+
var SYMBOL_KIND_NAMES = {
|
|
754
|
+
[import_vscode_languageserver_protocol2.SymbolKind.File]: "file",
|
|
755
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Module]: "module",
|
|
756
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Namespace]: "namespace",
|
|
757
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Package]: "package",
|
|
758
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Class]: "class",
|
|
759
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Method]: "method",
|
|
760
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Property]: "property",
|
|
761
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Field]: "field",
|
|
762
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Constructor]: "constructor",
|
|
763
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Enum]: "enum",
|
|
764
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Interface]: "interface",
|
|
765
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Function]: "function",
|
|
766
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Variable]: "variable",
|
|
767
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Constant]: "constant",
|
|
768
|
+
[import_vscode_languageserver_protocol2.SymbolKind.String]: "string",
|
|
769
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Number]: "number",
|
|
770
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Boolean]: "boolean",
|
|
771
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Array]: "array",
|
|
772
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Object]: "object",
|
|
773
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Key]: "key",
|
|
774
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Null]: "null",
|
|
775
|
+
[import_vscode_languageserver_protocol2.SymbolKind.EnumMember]: "enum-member",
|
|
776
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Struct]: "struct",
|
|
777
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Event]: "event",
|
|
778
|
+
[import_vscode_languageserver_protocol2.SymbolKind.Operator]: "operator",
|
|
779
|
+
[import_vscode_languageserver_protocol2.SymbolKind.TypeParameter]: "type-param"
|
|
780
|
+
};
|
|
781
|
+
function kindName(kind) {
|
|
782
|
+
return SYMBOL_KIND_NAMES[kind] ?? `kind(${kind})`;
|
|
783
|
+
}
|
|
784
|
+
function formatDocumentSymbol(sym, indent = 0) {
|
|
785
|
+
let prefix = " ".repeat(indent), line = sym.range.start.line + 1, result = [`${prefix}${kindName(sym.kind)} ${sym.name} (line ${line})`];
|
|
786
|
+
if (sym.children)
|
|
787
|
+
for (let child of sym.children)
|
|
788
|
+
result.push(...formatDocumentSymbol(child, indent + 1));
|
|
789
|
+
return result;
|
|
790
|
+
}
|
|
791
|
+
function formatSymbolInfo(sym, rootDir) {
|
|
792
|
+
let location = "";
|
|
793
|
+
if ("location" in sym && sym.location)
|
|
794
|
+
try {
|
|
795
|
+
let absPath = fileURLToPath3(sym.location.uri), relPath = relative4(rootDir, absPath), loc = sym.location, line = loc.range ? loc.range.start.line + 1 : "?";
|
|
796
|
+
location = ` ${relPath}:${line}`;
|
|
797
|
+
} catch {
|
|
798
|
+
location = ` ${sym.location.uri}`;
|
|
799
|
+
}
|
|
800
|
+
return `${kindName(sym.kind)} ${sym.name}${location}`;
|
|
801
|
+
}
|
|
802
|
+
var SymbolsParams = typebox_exports.Object({
|
|
803
|
+
path: typebox_exports.Optional(typebox_exports.String({ description: "File path for document symbols" })),
|
|
804
|
+
query: typebox_exports.Optional(typebox_exports.String({ description: "Search query for workspace symbols (searches across all files)" }))
|
|
805
|
+
});
|
|
806
|
+
function formatTreeSitterSymbol(sym, indent = 0) {
|
|
807
|
+
let prefix = " ".repeat(indent), kindStr = SYMBOL_KIND_NAMES[sym.kind] ?? `kind(${sym.kind})`, result = [`${prefix}${kindStr} ${sym.name} (line ${sym.line})`];
|
|
808
|
+
if (sym.children)
|
|
809
|
+
for (let child of sym.children)
|
|
810
|
+
result.push(...formatTreeSitterSymbol(child, indent + 1));
|
|
811
|
+
return result;
|
|
812
|
+
}
|
|
813
|
+
function createSymbolsTool(manager, treeSitter, workspaceIndex) {
|
|
814
|
+
return {
|
|
815
|
+
name: "lsp_symbols",
|
|
816
|
+
label: "LSP Symbols",
|
|
817
|
+
description: "List symbols in a file (document symbols) or search for symbols across the workspace. Provide 'path' for file symbols or 'query' for workspace search.",
|
|
818
|
+
promptSnippet: "List symbols in a file or search workspace symbols via LSP",
|
|
819
|
+
parameters: SymbolsParams,
|
|
820
|
+
async execute(_toolCallId, params) {
|
|
821
|
+
let filePath = params.path?.replace(/^@/, ""), query = params.query;
|
|
822
|
+
if (!filePath && query === void 0)
|
|
823
|
+
return {
|
|
824
|
+
content: [{ type: "text", text: "Please provide either 'path' for file symbols or 'query' for workspace symbol search." }],
|
|
825
|
+
details: { count: 0 }
|
|
826
|
+
};
|
|
827
|
+
if (filePath) {
|
|
828
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
829
|
+
if (client) {
|
|
830
|
+
let uri = manager.getFileUri(filePath);
|
|
831
|
+
try {
|
|
832
|
+
let result = await client.sendRequest("textDocument/documentSymbol", { textDocument: { uri } });
|
|
833
|
+
if (!result || result.length === 0)
|
|
834
|
+
return { content: [{ type: "text", text: "No symbols found in this file." }], details: { count: 0 } };
|
|
835
|
+
let lines;
|
|
836
|
+
if ("range" in result[0])
|
|
837
|
+
lines = result.flatMap((s) => formatDocumentSymbol(s));
|
|
838
|
+
else {
|
|
839
|
+
let rootDir = manager.resolvePath(".");
|
|
840
|
+
lines = result.map((s) => formatSymbolInfo(s, rootDir));
|
|
841
|
+
}
|
|
842
|
+
let output = lines.join(`
|
|
843
|
+
`), truncation = truncateHead(output, { maxLines: 2e3, maxBytes: 51200 }), text = `${lines.length} symbol(s):
|
|
844
|
+
|
|
845
|
+
${truncation.content}`;
|
|
846
|
+
return truncation.truncated && (text += `
|
|
847
|
+
|
|
848
|
+
[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines]`), { content: [{ type: "text", text }], details: { count: lines.length } };
|
|
849
|
+
} catch (err) {
|
|
850
|
+
return { content: [{ type: "text", text: `LSP document symbols request failed: ${err.message}` }], details: { count: 0 } };
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (treeSitter) {
|
|
854
|
+
let provider = resolveProvider(filePath, manager, treeSitter);
|
|
855
|
+
if (provider.type === "tree-sitter")
|
|
856
|
+
try {
|
|
857
|
+
let absPath = manager.resolvePath(filePath), content = await readFile5(absPath, "utf-8"), tree = await treeSitter.parse(absPath, content);
|
|
858
|
+
if (tree) {
|
|
859
|
+
let symbols = extractSymbols(tree, provider.languageId);
|
|
860
|
+
if (symbols.length === 0)
|
|
861
|
+
return { content: [{ type: "text", text: "No symbols found in this file." }], details: { count: 0 } };
|
|
862
|
+
let lines = symbols.flatMap((s) => formatTreeSitterSymbol(s)), output = lines.join(`
|
|
863
|
+
`), truncation = truncateHead(output, { maxLines: 2e3, maxBytes: 51200 }), text = `${lines.length} symbol(s) [tree-sitter]:
|
|
864
|
+
|
|
865
|
+
${truncation.content}`;
|
|
866
|
+
return truncation.truncated && (text += `
|
|
867
|
+
|
|
868
|
+
[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines]`), { content: [{ type: "text", text }], details: { count: lines.length } };
|
|
869
|
+
}
|
|
870
|
+
} catch {
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return { content: [{ type: "text", text: manager.getUnavailableReason(filePath) }], details: { count: 0 } };
|
|
874
|
+
}
|
|
875
|
+
let runningLang = manager.getStatus().find((s) => s.running)?.languageId;
|
|
876
|
+
if (runningLang) {
|
|
877
|
+
let client = await manager.getClientForLanguage(runningLang).catch(() => null);
|
|
878
|
+
if (client)
|
|
879
|
+
try {
|
|
880
|
+
let result = await client.sendRequest("workspace/symbol", { query: query ?? "" });
|
|
881
|
+
if (!result || result.length === 0)
|
|
882
|
+
return { content: [{ type: "text", text: `No workspace symbols found for query: "${query}"` }], details: { count: 0 } };
|
|
883
|
+
let rootDir = manager.resolvePath("."), output = result.map((s) => formatSymbolInfo(s, rootDir)).join(`
|
|
884
|
+
`), truncation = truncateHead(output, { maxLines: 2e3, maxBytes: 51200 }), text = `${result.length} symbol(s) found:
|
|
885
|
+
|
|
886
|
+
${truncation.content}`;
|
|
887
|
+
return truncation.truncated && (text += `
|
|
888
|
+
|
|
889
|
+
[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines]`), { content: [{ type: "text", text }], details: { count: result.length } };
|
|
890
|
+
} catch (err) {
|
|
891
|
+
return { content: [{ type: "text", text: `LSP workspace symbols request failed: ${err.message}` }], details: { count: 0 } };
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
if (workspaceIndex && query)
|
|
895
|
+
try {
|
|
896
|
+
await workspaceIndex.build();
|
|
897
|
+
let results = workspaceIndex.search(query);
|
|
898
|
+
if (results.length === 0)
|
|
899
|
+
return { content: [{ type: "text", text: `No workspace symbols found for query: "${query}" [tree-sitter]` }], details: { count: 0 } };
|
|
900
|
+
let rootDir = manager.resolvePath("."), output = results.map((e) => {
|
|
901
|
+
let relPath = relative4(rootDir, e.file);
|
|
902
|
+
return `${kindName(e.kind)} ${e.name} ${relPath}:${e.line}`;
|
|
903
|
+
}).join(`
|
|
904
|
+
`), truncation = truncateHead(output, { maxLines: 2e3, maxBytes: 51200 }), text = `${results.length} symbol(s) found [tree-sitter]:
|
|
905
|
+
|
|
906
|
+
${truncation.content}`;
|
|
907
|
+
return truncation.truncated && (text += `
|
|
908
|
+
|
|
909
|
+
[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines]`), { content: [{ type: "text", text }], details: { count: results.length } };
|
|
910
|
+
} catch {
|
|
911
|
+
}
|
|
912
|
+
return {
|
|
913
|
+
content: [{ type: "text", text: "No LSP servers are currently running and no workspace index is available. Use lsp_diagnostics or lsp_hover on a file first to start a server." }],
|
|
914
|
+
details: { count: 0 }
|
|
915
|
+
};
|
|
916
|
+
},
|
|
917
|
+
renderCall(args, theme) {
|
|
918
|
+
let text = theme.fg("toolTitle", theme.bold("lsp_symbols "));
|
|
919
|
+
return args.path && (text += theme.fg("accent", args.path)), args.query && (text += theme.fg("accent", `query="${args.query}"`)), new Text5(text, 0, 0);
|
|
920
|
+
},
|
|
921
|
+
renderResult(result, { isPartial }, theme) {
|
|
922
|
+
if (isPartial)
|
|
923
|
+
return new Text5(theme.fg("warning", "Loading symbols..."), 0, 0);
|
|
924
|
+
let details = result.details;
|
|
925
|
+
return !details || details.count === 0 ? new Text5(theme.fg("dim", "No symbols"), 0, 0) : new Text5(theme.fg("success", `${details.count} symbol(s)`), 0, 0);
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/rename.js
|
|
931
|
+
import { Text as Text6 } from "@ashx-j/lunr-tui";
|
|
932
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
933
|
+
import { relative as relative5 } from "node:path";
|
|
934
|
+
function formatWorkspaceEdit(edit, rootDir) {
|
|
935
|
+
let lines = [], totalEdits = 0, fileCount = 0;
|
|
936
|
+
if (edit.documentChanges) {
|
|
937
|
+
for (let change of edit.documentChanges)
|
|
938
|
+
if ("textDocument" in change && "edits" in change) {
|
|
939
|
+
fileCount++;
|
|
940
|
+
let relPath;
|
|
941
|
+
try {
|
|
942
|
+
relPath = relative5(rootDir, fileURLToPath4(change.textDocument.uri));
|
|
943
|
+
} catch {
|
|
944
|
+
relPath = change.textDocument.uri;
|
|
945
|
+
}
|
|
946
|
+
lines.push(`${relPath}:`);
|
|
947
|
+
for (let textEdit of change.edits) {
|
|
948
|
+
let line = textEdit.range.start.line + 1, col = textEdit.range.start.character + 1;
|
|
949
|
+
lines.push(` ${line}:${col} \u2192 "${textEdit.newText}"`), totalEdits++;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
let changes = edit.changes ?? {};
|
|
954
|
+
for (let [uri, edits] of Object.entries(changes)) {
|
|
955
|
+
fileCount++;
|
|
956
|
+
let relPath;
|
|
957
|
+
try {
|
|
958
|
+
relPath = relative5(rootDir, fileURLToPath4(uri));
|
|
959
|
+
} catch {
|
|
960
|
+
relPath = uri;
|
|
961
|
+
}
|
|
962
|
+
lines.push(`${relPath}:`);
|
|
963
|
+
for (let textEdit of edits) {
|
|
964
|
+
let line = textEdit.range.start.line + 1, col = textEdit.range.start.character + 1;
|
|
965
|
+
lines.push(` ${line}:${col} \u2192 "${textEdit.newText}"`), totalEdits++;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return { summary: lines.join(`
|
|
969
|
+
`), fileCount, editCount: totalEdits };
|
|
970
|
+
}
|
|
971
|
+
var RenameParams = typebox_exports.Object({
|
|
972
|
+
path: typebox_exports.String({ description: "File path" }),
|
|
973
|
+
line: typebox_exports.Optional(typebox_exports.Number({ description: "Line number (1-indexed). Required unless query is provided." })),
|
|
974
|
+
character: typebox_exports.Optional(typebox_exports.Number({ description: "Column number (1-indexed). Required unless query is provided." })),
|
|
975
|
+
query: typebox_exports.Optional(typebox_exports.String({ description: "Symbol name to find in the file. Alternative to line/character \u2014 resolves the symbol's position automatically." })),
|
|
976
|
+
newName: typebox_exports.String({ description: "New name for the symbol" })
|
|
977
|
+
});
|
|
978
|
+
function createRenameTool(manager, treeSitter) {
|
|
979
|
+
return {
|
|
980
|
+
name: "lsp_rename",
|
|
981
|
+
label: "LSP Rename",
|
|
982
|
+
description: "Preview a rename refactoring for a symbol at a position. Returns the list of changes that would be made across all files. Does NOT apply the changes \u2014 use edit/write tools to apply them. Line and character are 1-indexed.",
|
|
983
|
+
promptSnippet: "Preview rename refactoring for a symbol (returns planned edits, does not apply them)",
|
|
984
|
+
parameters: RenameParams,
|
|
985
|
+
async execute(_toolCallId, params) {
|
|
986
|
+
let filePath = params.path.replace(/^@/, ""), line = params.line, character = params.character, resolvedFrom;
|
|
987
|
+
if ((line === void 0 || character === void 0) && params.query) {
|
|
988
|
+
let resolved = await resolveSymbolPosition(filePath, params.query, manager, treeSitter);
|
|
989
|
+
if (resolved)
|
|
990
|
+
line = resolved.line, character = resolved.character, resolvedFrom = `Resolved "${params.query}" \u2192 ${resolved.symbolName} at ${line}:${character} [${resolved.source}]`;
|
|
991
|
+
else {
|
|
992
|
+
let names = await getSymbolNames(filePath, manager, treeSitter), hint = names.length > 0 ? `
|
|
993
|
+
Available symbols: ${names.slice(0, 20).join(", ")}` : "";
|
|
994
|
+
return { content: [{ type: "text", text: `Could not find symbol "${params.query}" in ${filePath}${hint}` }], details: { fileCount: 0, editCount: 0 } };
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (line === void 0 || character === void 0)
|
|
998
|
+
return { content: [{ type: "text", text: "Either line/character or query is required." }], details: { fileCount: 0, editCount: 0 } };
|
|
999
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
1000
|
+
if (!client)
|
|
1001
|
+
return { content: [{ type: "text", text: manager.getUnavailableReason(filePath) }], details: { fileCount: 0, editCount: 0 } };
|
|
1002
|
+
let uri = manager.getFileUri(filePath), position = { line: line - 1, character: character - 1 };
|
|
1003
|
+
try {
|
|
1004
|
+
let result = await client.sendRequest("textDocument/rename", {
|
|
1005
|
+
textDocument: { uri },
|
|
1006
|
+
position,
|
|
1007
|
+
newName: params.newName
|
|
1008
|
+
});
|
|
1009
|
+
if (!result)
|
|
1010
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
1011
|
+
|
|
1012
|
+
Rename not possible at this position.` : "Rename not possible at this position." }], details: { fileCount: 0, editCount: 0 } };
|
|
1013
|
+
let rootDir = manager.resolvePath("."), { summary, fileCount, editCount } = formatWorkspaceEdit(result, rootDir);
|
|
1014
|
+
if (editCount === 0)
|
|
1015
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
1016
|
+
|
|
1017
|
+
No edits needed for this rename.` : "No edits needed for this rename." }], details: { fileCount: 0, editCount: 0 } };
|
|
1018
|
+
let truncation = truncateHead(summary, { maxLines: 2e3, maxBytes: 51200 }), text = "";
|
|
1019
|
+
return resolvedFrom && (text += `${resolvedFrom}
|
|
1020
|
+
|
|
1021
|
+
`), text += `Rename "${params.newName}": ${editCount} edit(s) across ${fileCount} file(s)
|
|
1022
|
+
|
|
1023
|
+
`, text += `NOTE: These changes are NOT applied. Use edit/write tools to make the changes.
|
|
1024
|
+
|
|
1025
|
+
`, text += truncation.content, truncation.truncated && (text += `
|
|
1026
|
+
|
|
1027
|
+
[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines]`), { content: [{ type: "text", text }], details: { fileCount, editCount } };
|
|
1028
|
+
} catch (err) {
|
|
1029
|
+
return { content: [{ type: "text", text: `LSP rename request failed: ${err.message}` }], details: { fileCount: 0, editCount: 0 } };
|
|
1030
|
+
}
|
|
1031
|
+
},
|
|
1032
|
+
renderCall(args, theme) {
|
|
1033
|
+
let text = theme.fg("toolTitle", theme.bold("lsp_rename "));
|
|
1034
|
+
return args.query && !args.line ? (text += theme.fg("accent", `${args.path}`), text += theme.fg("muted", ` query="${args.query}" \u2192 ${args.newName}`)) : (text += theme.fg("accent", `${args.path}:${args.line}:${args.character}`), text += theme.fg("muted", ` \u2192 ${args.newName}`)), new Text6(text, 0, 0);
|
|
1035
|
+
},
|
|
1036
|
+
renderResult(result, { isPartial }, theme) {
|
|
1037
|
+
if (isPartial)
|
|
1038
|
+
return new Text6(theme.fg("warning", "Computing..."), 0, 0);
|
|
1039
|
+
let details = result.details;
|
|
1040
|
+
return !details || details.editCount === 0 ? new Text6(theme.fg("dim", "No edits"), 0, 0) : new Text6(theme.fg("success", `${details.editCount} edit(s) in ${details.fileCount} file(s) (preview only)`), 0, 0);
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/code-overview.js
|
|
1046
|
+
import { Text as Text7 } from "@ashx-j/lunr-tui";
|
|
1047
|
+
import { resolve, relative as relative6 } from "node:path";
|
|
1048
|
+
import { readdir, readFile as readFile6 } from "node:fs/promises";
|
|
1049
|
+
import { existsSync } from "node:fs";
|
|
1050
|
+
var MANIFESTS = [
|
|
1051
|
+
"package.json",
|
|
1052
|
+
"Cargo.toml",
|
|
1053
|
+
"go.mod",
|
|
1054
|
+
"go.sum",
|
|
1055
|
+
"pom.xml",
|
|
1056
|
+
"build.gradle",
|
|
1057
|
+
"build.gradle.kts",
|
|
1058
|
+
"requirements.txt",
|
|
1059
|
+
"pyproject.toml",
|
|
1060
|
+
"setup.py",
|
|
1061
|
+
"setup.cfg",
|
|
1062
|
+
"Gemfile",
|
|
1063
|
+
"Makefile",
|
|
1064
|
+
"CMakeLists.txt"
|
|
1065
|
+
], ENTRY_PATTERNS = [
|
|
1066
|
+
"index.ts",
|
|
1067
|
+
"index.js",
|
|
1068
|
+
"main.ts",
|
|
1069
|
+
"main.js",
|
|
1070
|
+
"app.ts",
|
|
1071
|
+
"app.js",
|
|
1072
|
+
"main.py",
|
|
1073
|
+
"app.py",
|
|
1074
|
+
"__init__.py",
|
|
1075
|
+
"main.rs",
|
|
1076
|
+
"lib.rs",
|
|
1077
|
+
"main.go",
|
|
1078
|
+
"Main.java",
|
|
1079
|
+
"Application.java"
|
|
1080
|
+
], MAX_TREE_DEPTH = 3, MAX_TREE_ENTRIES = 200, OverviewParams = typebox_exports.Object({
|
|
1081
|
+
path: typebox_exports.Optional(typebox_exports.String({ description: "Root directory to analyze (defaults to project root)" })),
|
|
1082
|
+
depth: typebox_exports.Optional(typebox_exports.Number({ description: "Maximum directory depth (default: 3)" }))
|
|
1083
|
+
});
|
|
1084
|
+
function createCodeOverviewTool(rootDirOrGetter, treeSitter, workspaceIndex) {
|
|
1085
|
+
let getRootDir = typeof rootDirOrGetter == "function" ? rootDirOrGetter : () => rootDirOrGetter;
|
|
1086
|
+
return {
|
|
1087
|
+
name: "code_overview",
|
|
1088
|
+
label: "Code Overview",
|
|
1089
|
+
description: "Summarize project structure: directory tree, top-level symbols per key file, dependency manifests. Uses tree-sitter for symbol extraction \u2014 no LSP required.",
|
|
1090
|
+
promptSnippet: "Get a structural overview of the project (directories, key files, symbols)",
|
|
1091
|
+
parameters: OverviewParams,
|
|
1092
|
+
async execute(_toolCallId, params) {
|
|
1093
|
+
let rootDir = getRootDir(), targetDir = resolve(rootDir, params.path ?? "."), maxDepth = params.depth ?? MAX_TREE_DEPTH, sections = [], totalFiles = 0, totalSymbols = 0, treeLines = [];
|
|
1094
|
+
await buildTree(targetDir, "", 0, maxDepth, treeLines), totalFiles = treeLines.filter((l) => !l.endsWith("/")).length, sections.push("## Directory Structure\n\n```\n" + treeLines.join(`
|
|
1095
|
+
`) + "\n```");
|
|
1096
|
+
let manifests = [];
|
|
1097
|
+
for (let m of MANIFESTS) {
|
|
1098
|
+
let path = resolve(targetDir, m);
|
|
1099
|
+
existsSync(path) && manifests.push(m);
|
|
1100
|
+
}
|
|
1101
|
+
manifests.length > 0 && sections.push(`## Dependency Manifests
|
|
1102
|
+
|
|
1103
|
+
` + manifests.map((m) => `- ${m}`).join(`
|
|
1104
|
+
`));
|
|
1105
|
+
let keyFiles = await findKeyFiles(targetDir);
|
|
1106
|
+
if (keyFiles.length > 0) {
|
|
1107
|
+
let symbolSections = [];
|
|
1108
|
+
for (let file of keyFiles.slice(0, 10))
|
|
1109
|
+
try {
|
|
1110
|
+
let content = await readFile6(file, "utf-8"), languageId = treeSitter.getLanguageId(file);
|
|
1111
|
+
if (!languageId)
|
|
1112
|
+
continue;
|
|
1113
|
+
let tree = await treeSitter.parse(file, content);
|
|
1114
|
+
if (!tree)
|
|
1115
|
+
continue;
|
|
1116
|
+
let symbols = extractSymbols(tree, languageId);
|
|
1117
|
+
if (symbols.length === 0)
|
|
1118
|
+
continue;
|
|
1119
|
+
let relPath = relative6(targetDir, file), symbolLines = symbols.slice(0, 20).map((s) => ` ${{
|
|
1120
|
+
5: "class",
|
|
1121
|
+
6: "method",
|
|
1122
|
+
10: "enum",
|
|
1123
|
+
11: "interface",
|
|
1124
|
+
12: "function",
|
|
1125
|
+
13: "variable",
|
|
1126
|
+
14: "constant",
|
|
1127
|
+
22: "struct"
|
|
1128
|
+
}[s.kind] ?? "symbol"} ${s.name} (line ${s.line})`);
|
|
1129
|
+
symbols.length > 20 && symbolLines.push(` ... and ${symbols.length - 20} more`), totalSymbols += symbols.length, symbolSections.push(`### ${relPath}
|
|
1130
|
+
${symbolLines.join(`
|
|
1131
|
+
`)}`);
|
|
1132
|
+
} catch {
|
|
1133
|
+
}
|
|
1134
|
+
symbolSections.length > 0 && sections.push(`## Key Files
|
|
1135
|
+
|
|
1136
|
+
` + symbolSections.join(`
|
|
1137
|
+
|
|
1138
|
+
`));
|
|
1139
|
+
}
|
|
1140
|
+
if (workspaceIndex.isBuilt) {
|
|
1141
|
+
let stats = workspaceIndex.getStats();
|
|
1142
|
+
sections.push(`## Index Stats
|
|
1143
|
+
|
|
1144
|
+
- ${stats.files} indexed files
|
|
1145
|
+
- ${stats.symbols} symbols`);
|
|
1146
|
+
}
|
|
1147
|
+
let output = sections.join(`
|
|
1148
|
+
|
|
1149
|
+
`), truncation = truncateHead(output, { maxLines: 2e3, maxBytes: 51200 }), text = truncation.content;
|
|
1150
|
+
return truncation.truncated && (text += `
|
|
1151
|
+
|
|
1152
|
+
[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines]`), {
|
|
1153
|
+
content: [{ type: "text", text }],
|
|
1154
|
+
details: { files: totalFiles, symbols: totalSymbols }
|
|
1155
|
+
};
|
|
1156
|
+
},
|
|
1157
|
+
renderCall(args, theme) {
|
|
1158
|
+
let text = theme.fg("toolTitle", theme.bold("code_overview "));
|
|
1159
|
+
return args.path ? text += theme.fg("accent", args.path) : text += theme.fg("dim", "(project root)"), new Text7(text, 0, 0);
|
|
1160
|
+
},
|
|
1161
|
+
renderResult(result, { isPartial }, theme) {
|
|
1162
|
+
if (isPartial)
|
|
1163
|
+
return new Text7(theme.fg("warning", "Analyzing..."), 0, 0);
|
|
1164
|
+
let d = result.details;
|
|
1165
|
+
return d ? new Text7(theme.fg("success", `${d.files} files, ${d.symbols} symbols`), 0, 0) : new Text7(theme.fg("dim", "No overview"), 0, 0);
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
async function buildTree(dir, prefix, depth, maxDepth, lines) {
|
|
1170
|
+
if (!(depth > maxDepth || lines.length > MAX_TREE_ENTRIES))
|
|
1171
|
+
try {
|
|
1172
|
+
let sorted = (await readdir(dir, { withFileTypes: !0 })).filter((e) => !e.name.startsWith(".") || e.name === ".github").sort((a, b) => a.isDirectory() && !b.isDirectory() ? -1 : !a.isDirectory() && b.isDirectory() ? 1 : a.name.localeCompare(b.name));
|
|
1173
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
1174
|
+
if (lines.length > MAX_TREE_ENTRIES) {
|
|
1175
|
+
lines.push(`${prefix}... (truncated)`);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
let entry = sorted[i], isLast = i === sorted.length - 1, connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ", childPrefix = isLast ? " " : "\u2502 ";
|
|
1179
|
+
if (entry.isDirectory()) {
|
|
1180
|
+
if (SKIP_DIRS.has(entry.name)) {
|
|
1181
|
+
lines.push(`${prefix}${connector}${entry.name}/ (skipped)`);
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
lines.push(`${prefix}${connector}${entry.name}/`), await buildTree(resolve(dir, entry.name), prefix + childPrefix, depth + 1, maxDepth, lines);
|
|
1185
|
+
} else
|
|
1186
|
+
lines.push(`${prefix}${connector}${entry.name}`);
|
|
1187
|
+
}
|
|
1188
|
+
} catch {
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
async function findKeyFiles(dir) {
|
|
1192
|
+
let found = [], searchDirs = [dir, resolve(dir, "src"), resolve(dir, "lib"), resolve(dir, "app")];
|
|
1193
|
+
for (let searchDir of searchDirs)
|
|
1194
|
+
for (let pattern of ENTRY_PATTERNS) {
|
|
1195
|
+
let fullPath = resolve(searchDir, pattern);
|
|
1196
|
+
existsSync(fullPath) && found.push(fullPath);
|
|
1197
|
+
}
|
|
1198
|
+
return [...new Set(found)];
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/completions.js
|
|
1202
|
+
import { Text as Text8 } from "@ashx-j/lunr-tui";
|
|
1203
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
1204
|
+
var KIND_LABELS = {
|
|
1205
|
+
1: "text",
|
|
1206
|
+
2: "method",
|
|
1207
|
+
3: "function",
|
|
1208
|
+
4: "constructor",
|
|
1209
|
+
5: "field",
|
|
1210
|
+
6: "variable",
|
|
1211
|
+
7: "class",
|
|
1212
|
+
8: "interface",
|
|
1213
|
+
9: "module",
|
|
1214
|
+
10: "property",
|
|
1215
|
+
11: "unit",
|
|
1216
|
+
12: "value",
|
|
1217
|
+
13: "enum",
|
|
1218
|
+
14: "keyword",
|
|
1219
|
+
15: "snippet",
|
|
1220
|
+
16: "color",
|
|
1221
|
+
17: "file",
|
|
1222
|
+
18: "reference",
|
|
1223
|
+
19: "folder",
|
|
1224
|
+
20: "enum member",
|
|
1225
|
+
21: "constant",
|
|
1226
|
+
22: "struct",
|
|
1227
|
+
23: "event",
|
|
1228
|
+
24: "operator",
|
|
1229
|
+
25: "type param"
|
|
1230
|
+
};
|
|
1231
|
+
function kindLabel(kind) {
|
|
1232
|
+
return kind ? KIND_LABELS[kind] ?? "unknown" : "unknown";
|
|
1233
|
+
}
|
|
1234
|
+
function docSummary(doc) {
|
|
1235
|
+
if (!doc)
|
|
1236
|
+
return;
|
|
1237
|
+
let text = typeof doc == "string" ? doc : doc.value;
|
|
1238
|
+
if (!text)
|
|
1239
|
+
return;
|
|
1240
|
+
let summary = text.replace(/```[\s\S]*?```/g, "").split(`
|
|
1241
|
+
`).map((l) => l.trim()).filter((l) => l.length > 0).slice(0, 2).join(" ");
|
|
1242
|
+
return summary.length > 120 ? summary.slice(0, 117) + "..." : summary;
|
|
1243
|
+
}
|
|
1244
|
+
function formatItem(item) {
|
|
1245
|
+
let kind = kindLabel(item.kind), label = item.label, detail = item.detail ? ` ${item.detail}` : "", labelDetail = item.labelDetails?.detail ? item.labelDetails.detail : "", labelDesc = item.labelDetails?.description ? ` \u2014 ${item.labelDetails.description}` : "", line = `${kind.padEnd(12)} ${label}${labelDetail}${detail}${labelDesc}`, doc = docSummary(item.documentation);
|
|
1246
|
+
return doc && (line += `
|
|
1247
|
+
${"".padEnd(13)}${doc}`), line;
|
|
1248
|
+
}
|
|
1249
|
+
var CompletionParams = typebox_exports.Object({
|
|
1250
|
+
path: typebox_exports.String({ description: "File path" }),
|
|
1251
|
+
line: typebox_exports.Optional(typebox_exports.Number({ description: "Line number (1-indexed). Required unless query is provided." })),
|
|
1252
|
+
character: typebox_exports.Optional(typebox_exports.Number({ description: "Column number (1-indexed). Required unless query is provided." })),
|
|
1253
|
+
query: typebox_exports.Optional(typebox_exports.String({ description: "Symbol name to find in the file. Alternative to line/character \u2014 resolves the symbol's position automatically." })),
|
|
1254
|
+
limit: typebox_exports.Optional(typebox_exports.Number({ description: "Max results to return (default: 20)" })),
|
|
1255
|
+
trigger: typebox_exports.Optional(typebox_exports.Union([typebox_exports.Literal("auto"), typebox_exports.Literal("none")], {
|
|
1256
|
+
description: 'Synthetic trigger mode (default: "auto"). When "auto", if the position is at the end of an identifier (no trailing dot), a dot is temporarily inserted to trigger member completions. Use "none" to skip this.'
|
|
1257
|
+
}))
|
|
1258
|
+
});
|
|
1259
|
+
function shouldSyntheticTrigger(content, line, character) {
|
|
1260
|
+
let lines = content.split(`
|
|
1261
|
+
`);
|
|
1262
|
+
if (line < 0 || line >= lines.length)
|
|
1263
|
+
return null;
|
|
1264
|
+
let lineText = lines[line];
|
|
1265
|
+
if (character < 0 || character > lineText.length || lineText[character] === ".")
|
|
1266
|
+
return null;
|
|
1267
|
+
let charBefore = character > 0 ? lineText[character - 1] : "";
|
|
1268
|
+
return charBefore && /[\w\d_\)\]]/.test(charBefore) ? { insertLine: line, insertChar: character } : null;
|
|
1269
|
+
}
|
|
1270
|
+
function insertDot(content, line, character) {
|
|
1271
|
+
let lines = content.split(`
|
|
1272
|
+
`), lineText = lines[line];
|
|
1273
|
+
return lines[line] = lineText.slice(0, character) + "." + lineText.slice(character), lines.join(`
|
|
1274
|
+
`);
|
|
1275
|
+
}
|
|
1276
|
+
var syntheticDotLocks = /* @__PURE__ */ new Set();
|
|
1277
|
+
function createCompletionsTool(manager, versionTracker, treeSitter) {
|
|
1278
|
+
return {
|
|
1279
|
+
name: "lsp_completions",
|
|
1280
|
+
label: "LSP Completions",
|
|
1281
|
+
description: 'Get completion suggestions at a specific position in a file. Returns methods, properties, and other symbols available at that point. Useful for discovering APIs and verifying method names. Line and character are 1-indexed. When trigger is "auto" (default), a dot is temporarily inserted if the position is at the end of an identifier, enabling member completion without editing the file.',
|
|
1282
|
+
promptSnippet: "Get code completion suggestions at a file position via LSP. Use to discover available methods, properties, and APIs on objects. Supports automatic dot insertion for exploring members on identifiers.",
|
|
1283
|
+
parameters: CompletionParams,
|
|
1284
|
+
async execute(_toolCallId, params) {
|
|
1285
|
+
let filePath = params.path.replace(/^@/, ""), limit = params.limit ?? 20, trigger = params.trigger ?? "auto", line = params.line, character = params.character, resolvedFrom;
|
|
1286
|
+
if ((line === void 0 || character === void 0) && params.query) {
|
|
1287
|
+
let resolved = await resolveSymbolPosition(filePath, params.query, manager, treeSitter);
|
|
1288
|
+
if (resolved)
|
|
1289
|
+
line = resolved.line + 1, character = resolved.character + 1, resolvedFrom = `Resolved "${params.query}" \u2192 ${resolved.symbolName} at ${line}:${character} [${resolved.source}]`;
|
|
1290
|
+
else
|
|
1291
|
+
return { content: [{ type: "text", text: `Could not find symbol "${params.query}" in ${filePath}` }], details: { count: 0, total: 0 } };
|
|
1292
|
+
}
|
|
1293
|
+
if (line === void 0 || character === void 0)
|
|
1294
|
+
return { content: [{ type: "text", text: "Either line/character or query is required." }], details: { count: 0, total: 0 } };
|
|
1295
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
1296
|
+
if (!client)
|
|
1297
|
+
return {
|
|
1298
|
+
content: [
|
|
1299
|
+
{ type: "text", text: manager.getUnavailableReason(filePath) }
|
|
1300
|
+
],
|
|
1301
|
+
details: { count: 0, total: 0 }
|
|
1302
|
+
};
|
|
1303
|
+
let caps = client.serverCapabilities;
|
|
1304
|
+
if (caps && !caps.completionProvider)
|
|
1305
|
+
return {
|
|
1306
|
+
content: [
|
|
1307
|
+
{
|
|
1308
|
+
type: "text",
|
|
1309
|
+
text: "LSP server for this file does not support completions."
|
|
1310
|
+
}
|
|
1311
|
+
],
|
|
1312
|
+
details: { count: 0, total: 0 }
|
|
1313
|
+
};
|
|
1314
|
+
let uri = manager.getFileUri(filePath), position = {
|
|
1315
|
+
line: line - 1,
|
|
1316
|
+
character: character - 1
|
|
1317
|
+
};
|
|
1318
|
+
try {
|
|
1319
|
+
let syntheticDot = !1, originalContent = null, revertVersion = 99991, completionPosition = position;
|
|
1320
|
+
if (trigger === "auto" && !syntheticDotLocks.has(uri))
|
|
1321
|
+
try {
|
|
1322
|
+
syntheticDotLocks.add(uri);
|
|
1323
|
+
let absPath = manager.resolvePath(filePath), fileContent = await readFile7(absPath, "utf-8"), triggerPos = shouldSyntheticTrigger(fileContent, position.line, position.character);
|
|
1324
|
+
if (triggerPos) {
|
|
1325
|
+
originalContent = fileContent;
|
|
1326
|
+
let modifiedContent = insertDot(fileContent, triggerPos.insertLine, triggerPos.insertChar), currentVersion = versionTracker?.getTrackedVersion(uri), insertVersion = currentVersion != null ? currentVersion + 1 : 99990;
|
|
1327
|
+
revertVersion = currentVersion != null ? currentVersion + 2 : 99991, client.didChange(uri, insertVersion, modifiedContent), versionTracker && currentVersion != null && versionTracker.setTrackedVersion(uri, insertVersion), completionPosition = {
|
|
1328
|
+
line: triggerPos.insertLine,
|
|
1329
|
+
character: triggerPos.insertChar + 1
|
|
1330
|
+
}, syntheticDot = !0, await new Promise((r) => setTimeout(r, 100));
|
|
1331
|
+
}
|
|
1332
|
+
} catch {
|
|
1333
|
+
} finally {
|
|
1334
|
+
syntheticDotLocks.delete(uri);
|
|
1335
|
+
}
|
|
1336
|
+
let response;
|
|
1337
|
+
try {
|
|
1338
|
+
response = await client.sendRequest("textDocument/completion", { textDocument: { uri }, position: completionPosition });
|
|
1339
|
+
} finally {
|
|
1340
|
+
if (syntheticDot && originalContent !== null)
|
|
1341
|
+
try {
|
|
1342
|
+
client.didChange(uri, revertVersion, originalContent), versionTracker && versionTracker.setTrackedVersion(uri, revertVersion);
|
|
1343
|
+
} catch {
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
if (!response)
|
|
1347
|
+
return {
|
|
1348
|
+
content: [
|
|
1349
|
+
{ type: "text", text: "No completions available at this position." }
|
|
1350
|
+
],
|
|
1351
|
+
details: { count: 0, total: 0 }
|
|
1352
|
+
};
|
|
1353
|
+
let allItems = Array.isArray(response) ? response : response.items;
|
|
1354
|
+
if (allItems.length === 0)
|
|
1355
|
+
return {
|
|
1356
|
+
content: [
|
|
1357
|
+
{ type: "text", text: "No completions available at this position." }
|
|
1358
|
+
],
|
|
1359
|
+
details: { count: 0, total: 0 }
|
|
1360
|
+
};
|
|
1361
|
+
let total = allItems.length, topItems = [...allItems].sort((a, b) => {
|
|
1362
|
+
let sa = a.sortText ?? a.label, sb = b.sortText ?? b.label;
|
|
1363
|
+
return sa.localeCompare(sb);
|
|
1364
|
+
}).slice(0, limit), resolveSupported = caps?.completionProvider?.resolveProvider, resolvedItems;
|
|
1365
|
+
resolveSupported ? resolvedItems = (await Promise.allSettled(topItems.map((item) => Promise.race([
|
|
1366
|
+
client.sendRequest("completionItem/resolve", item),
|
|
1367
|
+
// Timeout per item: 2 seconds
|
|
1368
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("resolve timeout")), 2e3))
|
|
1369
|
+
])))).map((result, i) => result.status === "fulfilled" ? result.value : topItems[i]) : resolvedItems = topItems;
|
|
1370
|
+
let triggerNote = syntheticDot ? " (synthetic dot trigger)" : "", header = `${resolvedItems.length} of ${total} completions at ${filePath}:${line}:${character}${triggerNote}
|
|
1371
|
+
`;
|
|
1372
|
+
resolvedFrom && (header = `${resolvedFrom}
|
|
1373
|
+
|
|
1374
|
+
${header}`);
|
|
1375
|
+
let lines = resolvedItems.map(formatItem);
|
|
1376
|
+
return {
|
|
1377
|
+
content: [{ type: "text", text: header + lines.join(`
|
|
1378
|
+
`) }],
|
|
1379
|
+
details: { count: resolvedItems.length, total }
|
|
1380
|
+
};
|
|
1381
|
+
} catch (err) {
|
|
1382
|
+
return {
|
|
1383
|
+
content: [
|
|
1384
|
+
{
|
|
1385
|
+
type: "text",
|
|
1386
|
+
text: `LSP completion request failed: ${err.message}`
|
|
1387
|
+
}
|
|
1388
|
+
],
|
|
1389
|
+
details: { count: 0, total: 0 }
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
},
|
|
1393
|
+
renderCall(args, theme) {
|
|
1394
|
+
let text = theme.fg("toolTitle", theme.bold("lsp_completions "));
|
|
1395
|
+
args.query && !args.line ? (text += theme.fg("accent", `${args.path}`), text += theme.fg("muted", ` query="${args.query}"`)) : text += theme.fg("accent", `${args.path}:${args.line}:${args.character}`);
|
|
1396
|
+
let extras = [];
|
|
1397
|
+
return args.limit && extras.push(`limit: ${args.limit}`), args.trigger === "none" && extras.push("trigger: none"), extras.length > 0 && (text += theme.fg("dim", ` (${extras.join(", ")})`)), new Text8(text, 0, 0);
|
|
1398
|
+
},
|
|
1399
|
+
renderResult(result, { isPartial }, theme) {
|
|
1400
|
+
if (isPartial)
|
|
1401
|
+
return new Text8(theme.fg("warning", "Loading completions..."), 0, 0);
|
|
1402
|
+
if (!result.details || result.details.count === 0) {
|
|
1403
|
+
let content = result.content[0];
|
|
1404
|
+
return content?.type === "text" ? new Text8(theme.fg("dim", content.text), 0, 0) : new Text8(theme.fg("dim", "No completions"), 0, 0);
|
|
1405
|
+
}
|
|
1406
|
+
let { count, total } = result.details, summary = `${count} of ${total} completions`;
|
|
1407
|
+
return new Text8(theme.fg("dim", summary), 0, 0);
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/code-search.js
|
|
1413
|
+
import { relative as relative7 } from "node:path";
|
|
1414
|
+
|
|
1415
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/pattern-compiler.js
|
|
1416
|
+
var VARIADIC_RE = /^\$\$\$([A-Z_][A-Z0-9_]*)?$/, METAVAR_RE = /^\$([A-Z_][A-Z0-9_]*)$/;
|
|
1417
|
+
function isVariadic(text) {
|
|
1418
|
+
let m = VARIADIC_RE.exec(text);
|
|
1419
|
+
return m ? m[1] ?? "" : null;
|
|
1420
|
+
}
|
|
1421
|
+
function isMetavar(text) {
|
|
1422
|
+
let m = METAVAR_RE.exec(text);
|
|
1423
|
+
return m ? m[1] : null;
|
|
1424
|
+
}
|
|
1425
|
+
var META_PREFIX = "__META_", VMETA_PREFIX = "__VMETA_", META_SUFFIX = "__";
|
|
1426
|
+
function preprocessMetavars(source) {
|
|
1427
|
+
let placeholders = /* @__PURE__ */ new Map(), preprocessed = source.replace(/\$\$\$([A-Z_][A-Z0-9_]*)?/g, (_match, name) => {
|
|
1428
|
+
let n = name ?? "", placeholder = `${VMETA_PREFIX}${n || "ANON"}${META_SUFFIX}`;
|
|
1429
|
+
return placeholders.set(placeholder, { kind: "variadic", name: n }), placeholder;
|
|
1430
|
+
});
|
|
1431
|
+
return preprocessed = preprocessed.replace(/\$([A-Z_][A-Z0-9_]*)/g, (_match, name) => {
|
|
1432
|
+
let placeholder = `${META_PREFIX}${name}${META_SUFFIX}`;
|
|
1433
|
+
return placeholders.set(placeholder, { kind: "metavar", name }), placeholder;
|
|
1434
|
+
}), { preprocessed, placeholders };
|
|
1435
|
+
}
|
|
1436
|
+
function decodePlaceholder(text, placeholders) {
|
|
1437
|
+
return placeholders.get(text) ?? null;
|
|
1438
|
+
}
|
|
1439
|
+
async function compilePattern(source, languageId, treeSitter) {
|
|
1440
|
+
await treeSitter.init();
|
|
1441
|
+
let { preprocessed, placeholders } = preprocessMetavars(source), wrappers = getWrappersForLanguage(languageId);
|
|
1442
|
+
for (let { wrap, unwrap } of wrappers) {
|
|
1443
|
+
let wrapped = wrap(preprocessed), tree = await treeSitter.parseWithLanguage(`__pattern__${Date.now()}`, wrapped, languageId);
|
|
1444
|
+
if (!tree)
|
|
1445
|
+
continue;
|
|
1446
|
+
let root = tree.rootNode;
|
|
1447
|
+
if (hasErrors(root))
|
|
1448
|
+
continue;
|
|
1449
|
+
let unwrapped = unwrap(root);
|
|
1450
|
+
if (!unwrapped)
|
|
1451
|
+
continue;
|
|
1452
|
+
let metavars = [];
|
|
1453
|
+
return { root: buildPatternNode(unwrapped, metavars, void 0, placeholders), metavars: [...new Set(metavars)], languageId };
|
|
1454
|
+
}
|
|
1455
|
+
throw new Error(`Failed to parse pattern as ${languageId}. The pattern may have syntax errors or may not be a valid code fragment in this language.`);
|
|
1456
|
+
}
|
|
1457
|
+
function getWrappersForLanguage(languageId) {
|
|
1458
|
+
let common = [
|
|
1459
|
+
{ wrap: (s) => s, unwrap: unwrapProgram },
|
|
1460
|
+
{ wrap: (s) => `${s};`, unwrap: unwrapProgram },
|
|
1461
|
+
{ wrap: (s) => `(${s})`, unwrap: unwrapExpression }
|
|
1462
|
+
];
|
|
1463
|
+
return languageId === "python" ? [
|
|
1464
|
+
{ wrap: (s) => s, unwrap: unwrapModule },
|
|
1465
|
+
{ wrap: (s) => `(${s})`, unwrap: unwrapExpression },
|
|
1466
|
+
{ wrap: (s) => `def _():
|
|
1467
|
+
${s}`, unwrap: unwrapPythonFunctionBody }
|
|
1468
|
+
] : [
|
|
1469
|
+
...common,
|
|
1470
|
+
{ wrap: (s) => `function _() { ${s} }`, unwrap: unwrapFunctionBody }
|
|
1471
|
+
];
|
|
1472
|
+
}
|
|
1473
|
+
function buildPatternNode(node, metavars, fieldName, placeholders) {
|
|
1474
|
+
let text = node.text;
|
|
1475
|
+
if (node.namedChildCount === 0) {
|
|
1476
|
+
let decoded = decodePlaceholder(text, placeholders);
|
|
1477
|
+
if (decoded)
|
|
1478
|
+
return decoded.name && metavars.push(decoded.name), decoded.kind === "variadic" ? { kind: "variadic", name: decoded.name, fieldName } : { kind: "metavar", name: decoded.name, fieldName };
|
|
1479
|
+
}
|
|
1480
|
+
if (node.namedChildCount === 0) {
|
|
1481
|
+
let variadicName = isVariadic(text);
|
|
1482
|
+
if (variadicName !== null)
|
|
1483
|
+
return variadicName && metavars.push(variadicName), { kind: "variadic", name: variadicName, fieldName };
|
|
1484
|
+
let metavarName = isMetavar(text);
|
|
1485
|
+
if (metavarName !== null)
|
|
1486
|
+
return metavars.push(metavarName), { kind: "metavar", name: metavarName, fieldName };
|
|
1487
|
+
}
|
|
1488
|
+
if (node.namedChildCount === 1) {
|
|
1489
|
+
let onlyChild = node.namedChildren[0];
|
|
1490
|
+
if (onlyChild.namedChildCount === 0) {
|
|
1491
|
+
let decoded = decodePlaceholder(onlyChild.text, placeholders);
|
|
1492
|
+
if (decoded)
|
|
1493
|
+
return decoded.name && metavars.push(decoded.name), decoded.kind === "variadic" ? { kind: "variadic", name: decoded.name, fieldName } : { kind: "metavar", name: decoded.name, fieldName };
|
|
1494
|
+
let childMeta = isMetavar(onlyChild.text);
|
|
1495
|
+
if (childMeta !== null)
|
|
1496
|
+
return metavars.push(childMeta), { kind: "metavar", name: childMeta, fieldName };
|
|
1497
|
+
let childVariadic = isVariadic(onlyChild.text);
|
|
1498
|
+
if (childVariadic !== null)
|
|
1499
|
+
return childVariadic && metavars.push(childVariadic), { kind: "variadic", name: childVariadic, fieldName };
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
if (node.namedChildCount === 0)
|
|
1503
|
+
return { kind: "literal", nodeType: node.type, text, children: [], fieldName };
|
|
1504
|
+
let children = [];
|
|
1505
|
+
for (let child of node.namedChildren) {
|
|
1506
|
+
let childFieldName = getFieldName(node, child);
|
|
1507
|
+
children.push(buildPatternNode(child, metavars, childFieldName, placeholders));
|
|
1508
|
+
}
|
|
1509
|
+
return { kind: "literal", nodeType: node.type, children, fieldName };
|
|
1510
|
+
}
|
|
1511
|
+
function getFieldName(parent, child) {
|
|
1512
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
1513
|
+
let c = parent.child(i);
|
|
1514
|
+
if (c && c.id === child.id)
|
|
1515
|
+
return parent.fieldNameForChild(i) ?? void 0;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
function unwrapProgram(root) {
|
|
1519
|
+
if (root.type !== "program" || root.namedChildCount === 0)
|
|
1520
|
+
return null;
|
|
1521
|
+
let child = root.namedChildren[0];
|
|
1522
|
+
return child.type === "expression_statement" && child.namedChildCount === 1 ? child.namedChildren[0] : child;
|
|
1523
|
+
}
|
|
1524
|
+
function unwrapModule(root) {
|
|
1525
|
+
if (root.type !== "module" || root.namedChildCount === 0)
|
|
1526
|
+
return null;
|
|
1527
|
+
let child = root.namedChildren[0];
|
|
1528
|
+
return child.type === "expression_statement" && child.namedChildCount === 1 ? child.namedChildren[0] : child;
|
|
1529
|
+
}
|
|
1530
|
+
function unwrapExpression(root) {
|
|
1531
|
+
if (root.type !== "program" || root.namedChildCount === 0)
|
|
1532
|
+
return null;
|
|
1533
|
+
let node = root.namedChildren[0];
|
|
1534
|
+
return node.type === "expression_statement" && (node = node.namedChildren[0]), node.type === "parenthesized_expression" && node.namedChildCount === 1 ? node.namedChildren[0] : node;
|
|
1535
|
+
}
|
|
1536
|
+
function unwrapFunctionBody(root) {
|
|
1537
|
+
if (root.type !== "program" || root.namedChildCount === 0)
|
|
1538
|
+
return null;
|
|
1539
|
+
let fn = root.namedChildren[0];
|
|
1540
|
+
if (!fn.type.includes("function"))
|
|
1541
|
+
return null;
|
|
1542
|
+
let body = fn.childForFieldName("body");
|
|
1543
|
+
if (!body || body.namedChildCount === 0)
|
|
1544
|
+
return null;
|
|
1545
|
+
let stmt = body.namedChildren[0];
|
|
1546
|
+
return stmt.type === "expression_statement" && stmt.namedChildCount === 1 ? stmt.namedChildren[0] : stmt;
|
|
1547
|
+
}
|
|
1548
|
+
function unwrapPythonFunctionBody(root) {
|
|
1549
|
+
if (root.type !== "module" || root.namedChildCount === 0)
|
|
1550
|
+
return null;
|
|
1551
|
+
let fn = root.namedChildren[0];
|
|
1552
|
+
if (fn.type !== "function_definition")
|
|
1553
|
+
return null;
|
|
1554
|
+
let body = fn.childForFieldName("body");
|
|
1555
|
+
if (!body || body.namedChildCount === 0)
|
|
1556
|
+
return null;
|
|
1557
|
+
let stmt = body.namedChildren[0];
|
|
1558
|
+
return stmt.type === "expression_statement" && stmt.namedChildCount === 1 ? stmt.namedChildren[0] : stmt;
|
|
1559
|
+
}
|
|
1560
|
+
function hasErrors(node) {
|
|
1561
|
+
if (node.type === "ERROR" || node.isMissing)
|
|
1562
|
+
return !0;
|
|
1563
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1564
|
+
let child = node.child(i);
|
|
1565
|
+
if (child && hasErrors(child))
|
|
1566
|
+
return !0;
|
|
1567
|
+
}
|
|
1568
|
+
return !1;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js
|
|
1572
|
+
import { resolve as resolve2 } from "node:path";
|
|
1573
|
+
import { readdir as readdir2, readFile as readFile8, stat } from "node:fs/promises";
|
|
1574
|
+
async function searchFiles(pattern, rootDir, treeSitter, options = {}) {
|
|
1575
|
+
let searchRoot = options.path ? resolve2(rootDir, options.path) : rootDir, maxResults = options.maxResults ?? 50, stats = await stat(searchRoot), files;
|
|
1576
|
+
stats.isFile() ? files = [searchRoot] : files = await collectFilesByLanguage(searchRoot, pattern.languageId, treeSitter);
|
|
1577
|
+
let matches = [];
|
|
1578
|
+
for (let file of files) {
|
|
1579
|
+
if (matches.length >= maxResults)
|
|
1580
|
+
break;
|
|
1581
|
+
try {
|
|
1582
|
+
let content = await readFile8(file, "utf-8"), tree = await treeSitter.parseWithLanguage(file, content, pattern.languageId);
|
|
1583
|
+
if (!tree)
|
|
1584
|
+
continue;
|
|
1585
|
+
let fileMatches = findMatches(tree.rootNode, pattern.root);
|
|
1586
|
+
for (let m of fileMatches) {
|
|
1587
|
+
if (matches.length >= maxResults)
|
|
1588
|
+
break;
|
|
1589
|
+
matches.push({
|
|
1590
|
+
file,
|
|
1591
|
+
line: m.node.startPosition.row + 1,
|
|
1592
|
+
column: m.node.startPosition.column + 1,
|
|
1593
|
+
matchedText: m.node.text,
|
|
1594
|
+
startIndex: m.node.startIndex,
|
|
1595
|
+
endIndex: m.node.endIndex,
|
|
1596
|
+
captures: m.captures
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
} catch {
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
return matches;
|
|
1603
|
+
}
|
|
1604
|
+
async function collectFilesByLanguage(dir, languageId, treeSitter, collected = [], maxFiles = MAX_INDEX_FILES) {
|
|
1605
|
+
if (collected.length >= maxFiles)
|
|
1606
|
+
return collected;
|
|
1607
|
+
try {
|
|
1608
|
+
let entries = await readdir2(dir, { withFileTypes: !0 });
|
|
1609
|
+
for (let entry of entries) {
|
|
1610
|
+
if (collected.length >= maxFiles)
|
|
1611
|
+
break;
|
|
1612
|
+
if (entry.isDirectory()) {
|
|
1613
|
+
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith("."))
|
|
1614
|
+
continue;
|
|
1615
|
+
await collectFilesByLanguage(resolve2(dir, entry.name), languageId, treeSitter, collected, maxFiles);
|
|
1616
|
+
} else if (entry.isFile() && treeSitter.getLanguageId(entry.name) === languageId) {
|
|
1617
|
+
let filePath = resolve2(dir, entry.name);
|
|
1618
|
+
try {
|
|
1619
|
+
(await stat(filePath)).size <= MAX_FILE_SIZE && collected.push(filePath);
|
|
1620
|
+
} catch {
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
} catch {
|
|
1625
|
+
}
|
|
1626
|
+
return collected;
|
|
1627
|
+
}
|
|
1628
|
+
function findMatches(root, pattern) {
|
|
1629
|
+
let matches = [], visited = /* @__PURE__ */ new Set();
|
|
1630
|
+
function walk(node) {
|
|
1631
|
+
if (visited.has(node.id))
|
|
1632
|
+
return;
|
|
1633
|
+
let captures = {};
|
|
1634
|
+
if (matchNode(node, pattern, captures)) {
|
|
1635
|
+
matches.push({ node, captures }), markDescendants(node, visited);
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
for (let child of node.namedChildren)
|
|
1639
|
+
walk(child);
|
|
1640
|
+
}
|
|
1641
|
+
return walk(root), matches;
|
|
1642
|
+
}
|
|
1643
|
+
function markDescendants(node, visited) {
|
|
1644
|
+
visited.add(node.id);
|
|
1645
|
+
for (let child of node.namedChildren)
|
|
1646
|
+
markDescendants(child, visited);
|
|
1647
|
+
}
|
|
1648
|
+
function matchNode(target, pattern, captures) {
|
|
1649
|
+
switch (pattern.kind) {
|
|
1650
|
+
case "metavar": {
|
|
1651
|
+
let name = pattern.name;
|
|
1652
|
+
return name in captures ? captures[name] === target.text : (captures[name] = target.text, !0);
|
|
1653
|
+
}
|
|
1654
|
+
case "variadic": {
|
|
1655
|
+
let name = pattern.name;
|
|
1656
|
+
return name && name in captures ? captures[name] === target.text : (name && (captures[name] = target.text), !0);
|
|
1657
|
+
}
|
|
1658
|
+
case "literal":
|
|
1659
|
+
return target.type !== pattern.nodeType ? !1 : pattern.text !== void 0 ? target.text === pattern.text : matchChildrenFieldAware(target, pattern.children, captures);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
function matchChildrenFieldAware(targetNode, patternChildren, captures) {
|
|
1663
|
+
let fieldPatterns = [], positionalPatterns = [];
|
|
1664
|
+
for (let pc of patternChildren)
|
|
1665
|
+
pc.fieldName ? fieldPatterns.push(pc) : positionalPatterns.push(pc);
|
|
1666
|
+
for (let fp of fieldPatterns) {
|
|
1667
|
+
let targetChild = targetNode.childForFieldName(fp.fieldName);
|
|
1668
|
+
if (!targetChild || !matchNode(targetChild, fp, captures))
|
|
1669
|
+
return !1;
|
|
1670
|
+
}
|
|
1671
|
+
if (positionalPatterns.length > 0) {
|
|
1672
|
+
let matchedFieldNames = new Set(fieldPatterns.map((fp) => fp.fieldName)), remainingTargets = [];
|
|
1673
|
+
for (let child of targetNode.namedChildren) {
|
|
1674
|
+
let childField = getChildFieldName(targetNode, child);
|
|
1675
|
+
(!childField || !matchedFieldNames.has(childField)) && remainingTargets.push(child);
|
|
1676
|
+
}
|
|
1677
|
+
return matchChildrenPositional(remainingTargets, 0, positionalPatterns, 0, captures);
|
|
1678
|
+
}
|
|
1679
|
+
return !0;
|
|
1680
|
+
}
|
|
1681
|
+
function getChildFieldName(parent, child) {
|
|
1682
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
1683
|
+
let c = parent.child(i);
|
|
1684
|
+
if (c && c.id === child.id)
|
|
1685
|
+
return parent.fieldNameForChild(i);
|
|
1686
|
+
}
|
|
1687
|
+
return null;
|
|
1688
|
+
}
|
|
1689
|
+
function matchChildrenPositional(targets, ti, patterns, pi, captures) {
|
|
1690
|
+
if (pi >= patterns.length)
|
|
1691
|
+
return !0;
|
|
1692
|
+
let pat = patterns[pi];
|
|
1693
|
+
if (pat.kind === "variadic") {
|
|
1694
|
+
if (pi === patterns.length - 1) {
|
|
1695
|
+
let remainingText = targets.slice(ti).map((t) => t.text).join(", ");
|
|
1696
|
+
if (pat.name) {
|
|
1697
|
+
if (pat.name in captures && captures[pat.name] !== remainingText)
|
|
1698
|
+
return !1;
|
|
1699
|
+
captures[pat.name] = remainingText;
|
|
1700
|
+
}
|
|
1701
|
+
return !0;
|
|
1702
|
+
}
|
|
1703
|
+
for (let take = 0; take <= targets.length - ti; take++) {
|
|
1704
|
+
let captureSnapshot2 = { ...captures }, consumedText = targets.slice(ti, ti + take).map((t) => t.text).join(", ");
|
|
1705
|
+
if (pat.name) {
|
|
1706
|
+
if (pat.name in captureSnapshot2 && captureSnapshot2[pat.name] !== consumedText)
|
|
1707
|
+
continue;
|
|
1708
|
+
captureSnapshot2[pat.name] = consumedText;
|
|
1709
|
+
}
|
|
1710
|
+
if (matchChildrenPositional(targets, ti + take, patterns, pi + 1, captureSnapshot2))
|
|
1711
|
+
return Object.assign(captures, captureSnapshot2), !0;
|
|
1712
|
+
}
|
|
1713
|
+
return !1;
|
|
1714
|
+
}
|
|
1715
|
+
if (ti >= targets.length)
|
|
1716
|
+
return !1;
|
|
1717
|
+
let captureSnapshot = { ...captures };
|
|
1718
|
+
return matchNode(targets[ti], pat, captureSnapshot) && matchChildrenPositional(targets, ti + 1, patterns, pi + 1, captureSnapshot) ? (Object.assign(captures, captureSnapshot), !0) : !1;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/code-search.js
|
|
1722
|
+
var SearchParams = typebox_exports.Object({
|
|
1723
|
+
pattern: typebox_exports.String({ description: "Structural pattern with metavariables ($NAME for single node, $$$NAME for variadic)" }),
|
|
1724
|
+
language: typebox_exports.String({ description: "Target language (typescript, python, rust, java, etc.)" }),
|
|
1725
|
+
path: typebox_exports.Optional(typebox_exports.String({ description: "File or directory to search (default: workspace root)" })),
|
|
1726
|
+
max_results: typebox_exports.Optional(typebox_exports.Number({ description: "Maximum results to return (default: 50)" }))
|
|
1727
|
+
});
|
|
1728
|
+
function createCodeSearchTool(rootDirOrGetter, treeSitter) {
|
|
1729
|
+
let getRootDir = typeof rootDirOrGetter == "function" ? rootDirOrGetter : () => rootDirOrGetter;
|
|
1730
|
+
return {
|
|
1731
|
+
name: "ast_search",
|
|
1732
|
+
label: "Code Search",
|
|
1733
|
+
description: "Find code matching a structural pattern using AST matching. Use $NAME to match any single node, $$$NAME to match zero-or-more nodes. More precise than grep \u2014 matches code structure, not text.",
|
|
1734
|
+
parameters: SearchParams,
|
|
1735
|
+
async execute(_toolCallId, params) {
|
|
1736
|
+
let rootDir = getRootDir(), { pattern: patternStr, language, path, max_results } = params, compiled;
|
|
1737
|
+
try {
|
|
1738
|
+
compiled = await compilePattern(patternStr, language, treeSitter);
|
|
1739
|
+
} catch (e) {
|
|
1740
|
+
return {
|
|
1741
|
+
content: [{ type: "text", text: `Error: ${e.message}` }],
|
|
1742
|
+
details: { matchCount: 0, filesSearched: 0 }
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
let matches = await searchFiles(compiled, rootDir, treeSitter, {
|
|
1746
|
+
path,
|
|
1747
|
+
maxResults: max_results ?? 50
|
|
1748
|
+
});
|
|
1749
|
+
if (matches.length === 0)
|
|
1750
|
+
return {
|
|
1751
|
+
content: [{ type: "text", text: "No matches found." }],
|
|
1752
|
+
details: { matchCount: 0, filesSearched: 0 }
|
|
1753
|
+
};
|
|
1754
|
+
let lines = [];
|
|
1755
|
+
lines.push(`Found ${matches.length} match${matches.length !== 1 ? "es" : ""}:
|
|
1756
|
+
`);
|
|
1757
|
+
for (let m of matches) {
|
|
1758
|
+
let relPath = relative7(rootDir, m.file), matchText = m.matchedText.length > 200 ? m.matchedText.slice(0, 200) + "..." : m.matchedText;
|
|
1759
|
+
lines.push(`${relPath}:${m.line}:${m.column}`), lines.push(` ${matchText.replace(/\n/g, `
|
|
1760
|
+
`)}`);
|
|
1761
|
+
let captureEntries = Object.entries(m.captures);
|
|
1762
|
+
if (captureEntries.length > 0)
|
|
1763
|
+
for (let [name, value] of captureEntries) {
|
|
1764
|
+
let displayValue = value.length > 100 ? value.slice(0, 100) + "..." : value;
|
|
1765
|
+
lines.push(` $${name} = ${displayValue}`);
|
|
1766
|
+
}
|
|
1767
|
+
lines.push("");
|
|
1768
|
+
}
|
|
1769
|
+
let text = lines.join(`
|
|
1770
|
+
`), uniqueFiles = new Set(matches.map((m) => m.file)), truncation = truncateHead(text, { maxLines: 2e3, maxBytes: 51200 }), output = truncation.content;
|
|
1771
|
+
return truncation.truncated && (output += `
|
|
1772
|
+
|
|
1773
|
+
[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines]`), {
|
|
1774
|
+
content: [{ type: "text", text: output }],
|
|
1775
|
+
details: { matchCount: matches.length, filesSearched: uniqueFiles.size }
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js
|
|
1782
|
+
import { relative as relative8 } from "node:path";
|
|
1783
|
+
|
|
1784
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js
|
|
1785
|
+
import { readFile as readFile9, writeFile } from "node:fs/promises";
|
|
1786
|
+
var METAVAR_REF_RE = /\$\$\$([A-Z_][A-Z0-9_]*)|\$([A-Z_][A-Z0-9_]*)/g;
|
|
1787
|
+
function substituteCaptures(template, captures) {
|
|
1788
|
+
return template.replace(METAVAR_REF_RE, (match, variadicName, singleName) => {
|
|
1789
|
+
let name = variadicName ?? singleName;
|
|
1790
|
+
return name in captures ? captures[name] : match;
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
function preserveTrailingSemicolon(original, replacement) {
|
|
1794
|
+
let trailingMatch = original.match(/(\s*;)\s*$/);
|
|
1795
|
+
return trailingMatch && !replacement.trimEnd().endsWith(";") ? replacement + trailingMatch[1] : replacement;
|
|
1796
|
+
}
|
|
1797
|
+
function computeRewrites(matches, replacementTemplate) {
|
|
1798
|
+
return matches.map((m) => {
|
|
1799
|
+
let raw = substituteCaptures(replacementTemplate, m.captures), after = preserveTrailingSemicolon(m.matchedText, raw);
|
|
1800
|
+
return {
|
|
1801
|
+
file: m.file,
|
|
1802
|
+
line: m.line,
|
|
1803
|
+
column: m.column,
|
|
1804
|
+
before: m.matchedText,
|
|
1805
|
+
after
|
|
1806
|
+
};
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
async function applyRewrites(matches, replacementTemplate) {
|
|
1810
|
+
let byFile = /* @__PURE__ */ new Map();
|
|
1811
|
+
for (let m of matches) {
|
|
1812
|
+
let existing = byFile.get(m.file);
|
|
1813
|
+
existing ? existing.push(m) : byFile.set(m.file, [m]);
|
|
1814
|
+
}
|
|
1815
|
+
let changes = [], filesModified = 0;
|
|
1816
|
+
for (let [file, fileMatches] of byFile) {
|
|
1817
|
+
let sorted = [...fileMatches].sort((a, b) => b.startIndex - a.startIndex), content = await readFile9(file, "utf-8"), modified = !1;
|
|
1818
|
+
for (let m of sorted) {
|
|
1819
|
+
let raw = substituteCaptures(replacementTemplate, m.captures), replacement = preserveTrailingSemicolon(m.matchedText, raw);
|
|
1820
|
+
replacement !== m.matchedText && (content = content.slice(0, m.startIndex) + replacement + content.slice(m.endIndex), modified = !0), changes.push({
|
|
1821
|
+
file,
|
|
1822
|
+
line: m.line,
|
|
1823
|
+
column: m.column,
|
|
1824
|
+
before: m.matchedText,
|
|
1825
|
+
after: replacement
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
modified && (await writeFile(file, content, "utf-8"), filesModified++);
|
|
1829
|
+
}
|
|
1830
|
+
return changes.reverse(), { changes, filesModified };
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js
|
|
1834
|
+
var RewriteParams = typebox_exports.Object({
|
|
1835
|
+
pattern: typebox_exports.String({ description: "Structural pattern to match (with $NAME / $$$NAME metavariables)" }),
|
|
1836
|
+
replacement: typebox_exports.String({ description: "Replacement template using the same metavariables" }),
|
|
1837
|
+
language: typebox_exports.String({ description: "Target language (typescript, python, rust, java, etc.)" }),
|
|
1838
|
+
path: typebox_exports.Optional(typebox_exports.String({ description: "File or directory scope (default: workspace root)" })),
|
|
1839
|
+
dry_run: typebox_exports.Optional(typebox_exports.Boolean({ description: "Preview changes without applying (default: true)" }))
|
|
1840
|
+
});
|
|
1841
|
+
function createCodeRewriteTool(rootDirOrGetter, treeSitter, fileChangeCallback) {
|
|
1842
|
+
let getRootDir = typeof rootDirOrGetter == "function" ? rootDirOrGetter : () => rootDirOrGetter;
|
|
1843
|
+
return {
|
|
1844
|
+
name: "code_rewrite",
|
|
1845
|
+
label: "Code Rewrite",
|
|
1846
|
+
description: "Transform code matching a structural pattern into a replacement. Use $NAME to capture and reuse single nodes, $$$NAME for sequences. Defaults to dry-run mode (preview only). Set dry_run=false to apply changes. For symbol renames, prefer lsp_rename instead (semantically correct).",
|
|
1847
|
+
parameters: RewriteParams,
|
|
1848
|
+
async execute(_toolCallId, params) {
|
|
1849
|
+
let rootDir = getRootDir(), { pattern: patternStr, replacement, language, path, dry_run } = params, isDryRun = dry_run !== !1, compiled;
|
|
1850
|
+
try {
|
|
1851
|
+
compiled = await compilePattern(patternStr, language, treeSitter);
|
|
1852
|
+
} catch (e) {
|
|
1853
|
+
return {
|
|
1854
|
+
content: [{ type: "text", text: `Error: ${e.message}` }],
|
|
1855
|
+
details: { matchCount: 0, filesModified: 0, dryRun: isDryRun }
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
let knownVars = new Set(compiled.metavars), refRe = /\$\$\$([A-Z_][A-Z0-9_]*)|\$([A-Z_][A-Z0-9_]*)/g, refMatch;
|
|
1859
|
+
for (; (refMatch = refRe.exec(replacement)) !== null; ) {
|
|
1860
|
+
let name = refMatch[1] ?? refMatch[2];
|
|
1861
|
+
if (!knownVars.has(name))
|
|
1862
|
+
return {
|
|
1863
|
+
content: [{ type: "text", text: `Error: Replacement references $${name} but pattern doesn't capture it. Pattern captures: ${compiled.metavars.join(", ") || "(none)"}` }],
|
|
1864
|
+
details: { matchCount: 0, filesModified: 0, dryRun: isDryRun }
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
let matches = await searchFiles(compiled, rootDir, treeSitter, {
|
|
1868
|
+
path,
|
|
1869
|
+
maxResults: 500
|
|
1870
|
+
});
|
|
1871
|
+
if (matches.length === 0)
|
|
1872
|
+
return {
|
|
1873
|
+
content: [{ type: "text", text: "No matches found. No changes to make." }],
|
|
1874
|
+
details: { matchCount: 0, filesModified: 0, dryRun: isDryRun }
|
|
1875
|
+
};
|
|
1876
|
+
if (isDryRun) {
|
|
1877
|
+
let changes = computeRewrites(matches, replacement), lines2 = [];
|
|
1878
|
+
lines2.push(`Dry run: ${changes.length} change${changes.length !== 1 ? "s" : ""} would be made:
|
|
1879
|
+
`);
|
|
1880
|
+
for (let c of changes) {
|
|
1881
|
+
let relPath = relative8(rootDir, c.file);
|
|
1882
|
+
lines2.push(`${relPath}:${c.line}:${c.column}`);
|
|
1883
|
+
let beforeLines = c.before.split(`
|
|
1884
|
+
`), afterLines = c.after.split(`
|
|
1885
|
+
`);
|
|
1886
|
+
for (let l of beforeLines)
|
|
1887
|
+
lines2.push(` - ${l}`);
|
|
1888
|
+
for (let l of afterLines)
|
|
1889
|
+
lines2.push(` + ${l}`);
|
|
1890
|
+
lines2.push("");
|
|
1891
|
+
}
|
|
1892
|
+
lines2.push("Run with dry_run=false to apply these changes.");
|
|
1893
|
+
let text2 = lines2.join(`
|
|
1894
|
+
`), truncation2 = truncateHead(text2, { maxLines: 2e3, maxBytes: 51200 }), output2 = truncation2.content;
|
|
1895
|
+
truncation2.truncated && (output2 += `
|
|
1896
|
+
|
|
1897
|
+
[Truncated: showing ${truncation2.outputLines} of ${truncation2.totalLines} lines]`);
|
|
1898
|
+
let uniqueFiles = new Set(changes.map((c) => c.file));
|
|
1899
|
+
return {
|
|
1900
|
+
content: [{ type: "text", text: output2 }],
|
|
1901
|
+
details: { matchCount: matches.length, filesModified: uniqueFiles.size, dryRun: !0 }
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
let result = await applyRewrites(matches, replacement);
|
|
1905
|
+
if (fileChangeCallback && result.filesModified > 0) {
|
|
1906
|
+
let modifiedFiles = new Set(result.changes.map((c) => c.file));
|
|
1907
|
+
for (let file of modifiedFiles)
|
|
1908
|
+
fileChangeCallback.onFileModified(file);
|
|
1909
|
+
}
|
|
1910
|
+
let lines = [];
|
|
1911
|
+
lines.push(`Applied ${result.changes.length} change${result.changes.length !== 1 ? "s" : ""} across ${result.filesModified} file${result.filesModified !== 1 ? "s" : ""}:
|
|
1912
|
+
`);
|
|
1913
|
+
for (let c of result.changes) {
|
|
1914
|
+
let relPath = relative8(rootDir, c.file), beforeShort = c.before.length > 80 ? c.before.slice(0, 80) + "..." : c.before, afterShort = c.after.length > 80 ? c.after.slice(0, 80) + "..." : c.after;
|
|
1915
|
+
lines.push(`${relPath}:${c.line} \u2014 ${beforeShort} \u2192 ${afterShort}`);
|
|
1916
|
+
}
|
|
1917
|
+
let text = lines.join(`
|
|
1918
|
+
`), truncation = truncateHead(text, { maxLines: 2e3, maxBytes: 51200 }), output = truncation.content;
|
|
1919
|
+
return truncation.truncated && (output += `
|
|
1920
|
+
|
|
1921
|
+
[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines]`), {
|
|
1922
|
+
content: [{ type: "text", text: output }],
|
|
1923
|
+
details: { matchCount: matches.length, filesModified: result.filesModified, dryRun: !1 }
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tools/code-actions.js
|
|
1930
|
+
import { Text as Text9 } from "@ashx-j/lunr-tui";
|
|
1931
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
1932
|
+
import { relative as relative9 } from "node:path";
|
|
1933
|
+
var CodeActionsParams = typebox_exports.Object({
|
|
1934
|
+
path: typebox_exports.String({ description: "File path" }),
|
|
1935
|
+
line: typebox_exports.Optional(typebox_exports.Number({ description: "Line number (1-indexed). Required unless query is provided." })),
|
|
1936
|
+
character: typebox_exports.Optional(typebox_exports.Number({ description: "Column number (1-indexed). Required unless query is provided." })),
|
|
1937
|
+
query: typebox_exports.Optional(typebox_exports.String({ description: "Symbol name to find in the file. Alternative to line/character \u2014 resolves the symbol's position automatically." })),
|
|
1938
|
+
endLine: typebox_exports.Optional(typebox_exports.Number({ description: "End line for range selection (1-indexed). Defaults to line." })),
|
|
1939
|
+
endCharacter: typebox_exports.Optional(typebox_exports.Number({ description: "End column for range selection (1-indexed). Defaults to character." })),
|
|
1940
|
+
kind: typebox_exports.Optional(typebox_exports.String({ description: 'Filter by action kind (e.g., "quickfix", "refactor", "source")' }))
|
|
1941
|
+
});
|
|
1942
|
+
function rangeContainsPosition(range, line, character) {
|
|
1943
|
+
return !(line < range.start.line || line > range.end.line || line === range.start.line && character < range.start.character || line === range.end.line && character > range.end.character);
|
|
1944
|
+
}
|
|
1945
|
+
function formatEditSummary(edit, rootDir) {
|
|
1946
|
+
let lines = [];
|
|
1947
|
+
if (edit.documentChanges) {
|
|
1948
|
+
for (let change of edit.documentChanges)
|
|
1949
|
+
if ("textDocument" in change && "edits" in change) {
|
|
1950
|
+
let relPath;
|
|
1951
|
+
try {
|
|
1952
|
+
relPath = relative9(rootDir, fileURLToPath5(change.textDocument.uri));
|
|
1953
|
+
} catch {
|
|
1954
|
+
relPath = change.textDocument.uri;
|
|
1955
|
+
}
|
|
1956
|
+
for (let textEdit of change.edits.slice(0, 5)) {
|
|
1957
|
+
let ln = textEdit.range.start.line + 1, col = textEdit.range.start.character + 1, endLn = textEdit.range.end.line + 1, endCol = textEdit.range.end.character + 1, newText = textEdit.newText.length > 60 ? textEdit.newText.slice(0, 57) + "..." : textEdit.newText;
|
|
1958
|
+
textEdit.range.start.line === textEdit.range.end.line && textEdit.range.start.character === textEdit.range.end.character ? lines.push(` ${relPath}:${ln}:${col} insert "${newText.replace(/\n/g, "\\n")}"`) : lines.push(` ${relPath}:${ln}:${col}-${endLn}:${endCol} \u2192 "${newText.replace(/\n/g, "\\n")}"`);
|
|
1959
|
+
}
|
|
1960
|
+
let remaining = change.edits.length - 5;
|
|
1961
|
+
remaining > 0 && lines.push(` ... and ${remaining} more edits in ${relPath}`);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
let changes = edit.changes ?? {};
|
|
1965
|
+
for (let [uri, edits] of Object.entries(changes)) {
|
|
1966
|
+
let relPath;
|
|
1967
|
+
try {
|
|
1968
|
+
relPath = relative9(rootDir, fileURLToPath5(uri));
|
|
1969
|
+
} catch {
|
|
1970
|
+
relPath = uri;
|
|
1971
|
+
}
|
|
1972
|
+
for (let textEdit of edits.slice(0, 5)) {
|
|
1973
|
+
let ln = textEdit.range.start.line + 1, col = textEdit.range.start.character + 1, endLn = textEdit.range.end.line + 1, endCol = textEdit.range.end.character + 1, newText = textEdit.newText.length > 60 ? textEdit.newText.slice(0, 57) + "..." : textEdit.newText;
|
|
1974
|
+
textEdit.range.start.line === textEdit.range.end.line && textEdit.range.start.character === textEdit.range.end.character ? lines.push(` ${relPath}:${ln}:${col} insert "${newText.replace(/\n/g, "\\n")}"`) : lines.push(` ${relPath}:${ln}:${col}-${endLn}:${endCol} \u2192 "${newText.replace(/\n/g, "\\n")}"`);
|
|
1975
|
+
}
|
|
1976
|
+
let remaining = edits.length - 5;
|
|
1977
|
+
remaining > 0 && lines.push(` ... and ${remaining} more edits in ${relPath}`);
|
|
1978
|
+
}
|
|
1979
|
+
return lines;
|
|
1980
|
+
}
|
|
1981
|
+
function isCodeAction(item) {
|
|
1982
|
+
return "kind" in item || "edit" in item || "diagnostics" in item || "isPreferred" in item;
|
|
1983
|
+
}
|
|
1984
|
+
function createCodeActionsTool(manager, treeSitter) {
|
|
1985
|
+
return {
|
|
1986
|
+
name: "lsp_code_actions",
|
|
1987
|
+
label: "LSP Code Actions",
|
|
1988
|
+
description: "Get available code actions (quick fixes, refactorings, source actions) at a position or range. Returns actionable fixes the LSP server can suggest \u2014 auto-imports, remove unused, extract method, etc.",
|
|
1989
|
+
promptSnippet: "Get available code actions (quick fixes, refactorings) at a file position via LSP. Use after lsp_diagnostics shows errors to find auto-fixes.",
|
|
1990
|
+
parameters: CodeActionsParams,
|
|
1991
|
+
async execute(_toolCallId, params) {
|
|
1992
|
+
let filePath = params.path.replace(/^@/, ""), line = params.line, character = params.character, resolvedFrom;
|
|
1993
|
+
if ((line === void 0 || character === void 0) && params.query) {
|
|
1994
|
+
let resolved = await resolveSymbolPosition(filePath, params.query, manager, treeSitter);
|
|
1995
|
+
if (resolved)
|
|
1996
|
+
line = resolved.line, character = resolved.character, resolvedFrom = `Resolved "${params.query}" \u2192 ${resolved.symbolName} at ${line}:${character} [${resolved.source}]`;
|
|
1997
|
+
else {
|
|
1998
|
+
let names = await getSymbolNames(filePath, manager, treeSitter), hint = names.length > 0 ? `
|
|
1999
|
+
Available symbols: ${names.slice(0, 20).join(", ")}` : "";
|
|
2000
|
+
return { content: [{ type: "text", text: `Could not find symbol "${params.query}" in ${filePath}${hint}` }], details: { count: 0, preferredCount: 0 } };
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
if (line === void 0 || character === void 0)
|
|
2004
|
+
return { content: [{ type: "text", text: "Either line/character or query is required." }], details: { count: 0, preferredCount: 0 } };
|
|
2005
|
+
let client = await manager.getClientForFile(filePath).catch(() => null);
|
|
2006
|
+
if (!client)
|
|
2007
|
+
return { content: [{ type: "text", text: manager.getUnavailableReason(filePath) }], details: { count: 0, preferredCount: 0 } };
|
|
2008
|
+
let caps = client.serverCapabilities;
|
|
2009
|
+
if (caps && !caps.codeActionProvider)
|
|
2010
|
+
return {
|
|
2011
|
+
content: [{ type: "text", text: "LSP server for this file does not support code actions." }],
|
|
2012
|
+
details: { count: 0, preferredCount: 0 }
|
|
2013
|
+
};
|
|
2014
|
+
let uri = manager.getFileUri(filePath), startLine = line - 1, startChar = character - 1, endLine = (params.endLine ?? line) - 1, endChar = (params.endCharacter ?? character) - 1, rangeDiags = (client.getDiagnostics(uri) ?? []).filter((d) => rangeContainsPosition(d.range, startLine, startChar) || rangeContainsPosition({ start: { line: startLine, character: startChar }, end: { line: endLine, character: endChar } }, d.range.start.line, d.range.start.character));
|
|
2015
|
+
try {
|
|
2016
|
+
let response = await client.sendRequest("textDocument/codeAction", {
|
|
2017
|
+
textDocument: { uri },
|
|
2018
|
+
range: {
|
|
2019
|
+
start: { line: startLine, character: startChar },
|
|
2020
|
+
end: { line: endLine, character: endChar }
|
|
2021
|
+
},
|
|
2022
|
+
context: {
|
|
2023
|
+
diagnostics: rangeDiags,
|
|
2024
|
+
only: params.kind ? [params.kind] : void 0
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
if (!response || response.length === 0)
|
|
2028
|
+
return { content: [{ type: "text", text: resolvedFrom ? `${resolvedFrom}
|
|
2029
|
+
|
|
2030
|
+
No code actions available at this position.` : "No code actions available at this position." }], details: { count: 0, preferredCount: 0 } };
|
|
2031
|
+
let actions = [], commands = [];
|
|
2032
|
+
for (let item of response)
|
|
2033
|
+
isCodeAction(item) ? actions.push(item) : commands.push(item);
|
|
2034
|
+
let kindOrder = { quickfix: 0, refactor: 1, source: 2 };
|
|
2035
|
+
actions.sort((a, b) => {
|
|
2036
|
+
if (a.isPreferred && !b.isPreferred)
|
|
2037
|
+
return -1;
|
|
2038
|
+
if (!a.isPreferred && b.isPreferred)
|
|
2039
|
+
return 1;
|
|
2040
|
+
let aKind = a.kind?.split(".")[0] ?? "zzz", bKind = b.kind?.split(".")[0] ?? "zzz";
|
|
2041
|
+
return (kindOrder[aKind] ?? 3) - (kindOrder[bKind] ?? 3);
|
|
2042
|
+
});
|
|
2043
|
+
let rootDir = manager.resolvePath("."), outputLines = [], preferredCount = 0;
|
|
2044
|
+
for (let i = 0; i < actions.length; i++) {
|
|
2045
|
+
let action = actions[i], preferred = action.isPreferred ? "\u2605 " : "";
|
|
2046
|
+
action.isPreferred && preferredCount++;
|
|
2047
|
+
let kindStr = action.kind ? ` [${action.kind}]` : "";
|
|
2048
|
+
if (outputLines.push(` ${i + 1}. ${preferred}${action.title}${kindStr}`), action.edit) {
|
|
2049
|
+
let editLines = formatEditSummary(action.edit, rootDir);
|
|
2050
|
+
outputLines.push(...editLines);
|
|
2051
|
+
} else action.command && !action.edit ? outputLines.push(` (command: ${action.command.title || action.command.command})`) : outputLines.push(" (resolve required)");
|
|
2052
|
+
}
|
|
2053
|
+
for (let cmd of commands)
|
|
2054
|
+
outputLines.push(` \u2022 ${cmd.title} (command-only, requires IDE execution)`);
|
|
2055
|
+
let totalCount = actions.length + commands.length, header = `${totalCount} code action(s) at ${filePath}:${line}:${character}`, preferredNote = preferredCount > 0 ? ` (${preferredCount} preferred)` : "", text = `${header}${preferredNote}
|
|
2056
|
+
|
|
2057
|
+
${outputLines.join(`
|
|
2058
|
+
`)}`;
|
|
2059
|
+
return resolvedFrom && (text = `${resolvedFrom}
|
|
2060
|
+
|
|
2061
|
+
${text}`), {
|
|
2062
|
+
content: [{ type: "text", text }],
|
|
2063
|
+
details: { count: totalCount, preferredCount }
|
|
2064
|
+
};
|
|
2065
|
+
} catch (err) {
|
|
2066
|
+
return {
|
|
2067
|
+
content: [{ type: "text", text: `LSP code action request failed: ${err.message}` }],
|
|
2068
|
+
details: { count: 0, preferredCount: 0 }
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
},
|
|
2072
|
+
renderCall(args, theme) {
|
|
2073
|
+
let text = theme.fg("toolTitle", theme.bold("lsp_code_actions "));
|
|
2074
|
+
return args.query && !args.line ? (text += theme.fg("accent", args.path), text += theme.fg("muted", ` query="${args.query}"`)) : args.endLine ? text += theme.fg("accent", `${args.path}:${args.line}:${args.character}-${args.endLine}:${args.endCharacter}`) : text += theme.fg("accent", `${args.path}:${args.line}:${args.character}`), args.kind && (text += theme.fg("dim", ` [${args.kind}]`)), new Text9(text, 0, 0);
|
|
2075
|
+
},
|
|
2076
|
+
renderResult(result, { isPartial }, theme) {
|
|
2077
|
+
if (isPartial)
|
|
2078
|
+
return new Text9(theme.fg("warning", "Loading..."), 0, 0);
|
|
2079
|
+
let details = result.details;
|
|
2080
|
+
if (!details || details.count === 0)
|
|
2081
|
+
return new Text9(theme.fg("dim", "No code actions available"), 0, 0);
|
|
2082
|
+
let preferred = details.preferredCount > 0 ? ` (${details.preferredCount} preferred)` : "";
|
|
2083
|
+
return new Text9(theme.fg("success", `${details.count} action(s)${preferred}`), 0, 0);
|
|
2084
|
+
}
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/index.js
|
|
2089
|
+
import { relative as relative10 } from "node:path";
|
|
2090
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
2091
|
+
import { join, resolve as resolve3 } from "node:path";
|
|
2092
|
+
function loadProjectConfig(dir) {
|
|
2093
|
+
let configPath = join(dir, ".pi-lsp.json");
|
|
2094
|
+
try {
|
|
2095
|
+
if (!existsSync2(configPath))
|
|
2096
|
+
return null;
|
|
2097
|
+
let raw = readFileSync(configPath, "utf-8"), parsed = JSON.parse(raw);
|
|
2098
|
+
return typeof parsed != "object" || parsed === null ? null : parsed;
|
|
2099
|
+
} catch {
|
|
2100
|
+
return null;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
function createServiceProxy(getTarget, label) {
|
|
2104
|
+
return new Proxy({}, {
|
|
2105
|
+
get(_target, prop, receiver) {
|
|
2106
|
+
let service = getTarget();
|
|
2107
|
+
if (!service)
|
|
2108
|
+
throw new Error(`${label} is not initialized`);
|
|
2109
|
+
let value = Reflect.get(service, prop, service);
|
|
2110
|
+
return typeof value == "function" ? value.bind(service) : value;
|
|
2111
|
+
}
|
|
2112
|
+
});
|
|
2113
|
+
}
|
|
2114
|
+
function registerToolWithRuntime(pi, tool, ensure, host) {
|
|
2115
|
+
pi.registerTool({
|
|
2116
|
+
...tool,
|
|
2117
|
+
async execute(id, params, signal, onUpdate, ctx) {
|
|
2118
|
+
let generation = host.getGeneration();
|
|
2119
|
+
if (signal?.throwIfAborted(), await awaitWithAbort(ensure(), signal), signal?.throwIfAborted(), host.getGeneration() !== generation)
|
|
2120
|
+
throw new Error("LSP session changed before execution");
|
|
2121
|
+
return tool.execute(id, params, signal, onUpdate, ctx);
|
|
2122
|
+
}
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
function lspExtension(pi) {
|
|
2126
|
+
let origListeners = process.listeners("uncaughtException");
|
|
2127
|
+
process.on("uncaughtException", (err) => {
|
|
2128
|
+
if (err?.code !== "EPIPE") {
|
|
2129
|
+
for (let listener of origListeners)
|
|
2130
|
+
listener(err);
|
|
2131
|
+
origListeners.length === 0 && console.error("[LSP] Uncaught exception:", err);
|
|
2132
|
+
}
|
|
2133
|
+
});
|
|
2134
|
+
let host = new LspRuntimeHost(), pendingProvider = null, latestCtx = null, projectConfig = null, sessionCwd = process.cwd(), withLatestCtx = (fn) => {
|
|
2135
|
+
let ctx = latestCtx;
|
|
2136
|
+
if (ctx)
|
|
2137
|
+
try {
|
|
2138
|
+
fn(ctx);
|
|
2139
|
+
} catch (err) {
|
|
2140
|
+
if (typeof err?.message == "string" && err.message.includes("stale after session")) {
|
|
2141
|
+
latestCtx = null;
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
throw err;
|
|
2145
|
+
}
|
|
2146
|
+
}, applyProvider = (data) => {
|
|
2147
|
+
let provider = data;
|
|
2148
|
+
pendingProvider = provider, host.setPendingProvider(provider);
|
|
2149
|
+
let statusText = provider.getStatusText();
|
|
2150
|
+
statusText && withLatestCtx((ctx) => {
|
|
2151
|
+
ctx.ui?.theme && ctx.ui.setStatus("lsp", ctx.ui.theme.fg("accent", `LSP: ${statusText}`));
|
|
2152
|
+
});
|
|
2153
|
+
};
|
|
2154
|
+
pi.events.on("lsp:register-workspace-provider", applyProvider);
|
|
2155
|
+
let existing = pi.events["lsp:workspace-provider"];
|
|
2156
|
+
existing && applyProvider(existing);
|
|
2157
|
+
let setLspStatus = (color, text) => {
|
|
2158
|
+
withLatestCtx((ctx) => {
|
|
2159
|
+
ctx.ui?.theme && ctx.ui.setStatus("lsp", ctx.ui.theme.fg(color, text));
|
|
2160
|
+
});
|
|
2161
|
+
}, makeCallbacks = () => ({
|
|
2162
|
+
onWorkspaceSetupStart: () => {
|
|
2163
|
+
setLspStatus("warning", "LSP: workspace setup...");
|
|
2164
|
+
},
|
|
2165
|
+
onWorkspaceSetupEnd: (success, duration) => {
|
|
2166
|
+
let secs = (duration / 1e3).toFixed(1);
|
|
2167
|
+
success ? setLspStatus("accent", `LSP: workspace ready (${secs}s)`) : setLspStatus("warning", `LSP: workspace setup failed (${secs}s)`);
|
|
2168
|
+
},
|
|
2169
|
+
onServerStart: (languageId, command) => {
|
|
2170
|
+
setLspStatus("warning", `LSP: starting ${languageId} (${command})...`);
|
|
2171
|
+
},
|
|
2172
|
+
onServerReady: (languageId) => {
|
|
2173
|
+
setLspStatus("accent", `LSP: ${languageId} ready`);
|
|
2174
|
+
},
|
|
2175
|
+
onServerError: (languageId, _error) => {
|
|
2176
|
+
setLspStatus("error", `LSP: ${languageId} failed`);
|
|
2177
|
+
},
|
|
2178
|
+
onServerCrash: (languageId, restarting, attempt) => {
|
|
2179
|
+
restarting ? setLspStatus("warning", `LSP: restarting ${languageId}... (attempt ${attempt}/3)`) : setLspStatus("error", `LSP: ${languageId} crashed \u2014 auto-restart exhausted`);
|
|
2180
|
+
}
|
|
2181
|
+
}), applyProjectConfigToManager = (manager, config) => {
|
|
2182
|
+
if (config) {
|
|
2183
|
+
if (config.servers)
|
|
2184
|
+
for (let [lang, serverConf] of Object.entries(config.servers))
|
|
2185
|
+
manager.setServerConfig(lang, {
|
|
2186
|
+
command: serverConf.command,
|
|
2187
|
+
args: serverConf.args ?? [],
|
|
2188
|
+
env: serverConf.env,
|
|
2189
|
+
initializationOptions: serverConf.initializationOptions,
|
|
2190
|
+
settings: serverConf.settings
|
|
2191
|
+
});
|
|
2192
|
+
config.lombokJar && config.lombokJar !== "auto" && manager.setLombokJar(config.lombokJar);
|
|
2193
|
+
}
|
|
2194
|
+
}, buildBindOptions = (cwd) => ({
|
|
2195
|
+
cwd,
|
|
2196
|
+
callbacks: makeCallbacks(),
|
|
2197
|
+
pendingProvider,
|
|
2198
|
+
syntheticDotChecker: (uri) => syntheticDotLocks.has(uri),
|
|
2199
|
+
configureManager: (manager) => {
|
|
2200
|
+
applyProjectConfigToManager(manager, projectConfig);
|
|
2201
|
+
}
|
|
2202
|
+
}), ensureRuntime = async () => host.ensureServices(buildBindOptions(sessionCwd || process.cwd()));
|
|
2203
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
2204
|
+
latestCtx = ctx, sessionCwd = ctx.cwd, projectConfig = loadProjectConfig(ctx.cwd), host.bindSession(buildBindOptions(ctx.cwd));
|
|
2205
|
+
let statusText = pendingProvider?.getStatusText?.() ?? "";
|
|
2206
|
+
if (statusText ? ctx.ui.setStatus("lsp", ctx.ui.theme.fg("accent", `LSP: ${statusText}`)) : ctx.ui.setStatus("lsp", ctx.ui.theme.fg("dim", "LSP: idle")), projectConfig?.autoStart && projectConfig.autoStart.length > 0) {
|
|
2207
|
+
let langs = projectConfig.autoStart, generation = host.getGeneration();
|
|
2208
|
+
ensureRuntime().then((services) => {
|
|
2209
|
+
if (host.getGeneration() !== generation)
|
|
2210
|
+
return;
|
|
2211
|
+
let lombokJar = services.manager.getLombokJar?.() ?? null, lombokNote = langs.includes("java") && lombokJar ? ` (lombok: ${String(lombokJar).split(/[/\\]/).pop()})` : "";
|
|
2212
|
+
setLspStatus("warning", `LSP: auto-starting ${langs.join(", ")}${lombokNote}...`), services.manager.startEagerly(langs);
|
|
2213
|
+
}).catch((error) => {
|
|
2214
|
+
host.getGeneration() === generation && setLspStatus("error", `LSP startup failed: ${String(error)}`);
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2217
|
+
});
|
|
2218
|
+
let managerProxy = createServiceProxy(() => host.getServicesIfReady()?.manager ?? null, "LSP manager"), treeSitterProxy = createServiceProxy(() => host.getServicesIfReady()?.treeSitter ?? null, "Tree-sitter manager"), workspaceIndexProxy = createServiceProxy(() => host.getServicesIfReady()?.workspaceIndex ?? null, "Workspace index");
|
|
2219
|
+
registerToolWithRuntime(pi, createDiagnosticsTool(managerProxy, treeSitterProxy), ensureRuntime, host), registerToolWithRuntime(pi, createHoverTool(managerProxy, treeSitterProxy), ensureRuntime, host), registerToolWithRuntime(pi, createDefinitionTool(managerProxy, treeSitterProxy, workspaceIndexProxy), ensureRuntime, host), registerToolWithRuntime(pi, createReferencesTool(managerProxy, treeSitterProxy), ensureRuntime, host), registerToolWithRuntime(pi, createSymbolsTool(managerProxy, treeSitterProxy, workspaceIndexProxy), ensureRuntime, host), registerToolWithRuntime(pi, createRenameTool(managerProxy, treeSitterProxy), ensureRuntime, host), registerToolWithRuntime(pi, createCodeActionsTool(managerProxy, treeSitterProxy), ensureRuntime, host), registerToolWithRuntime(pi, createCompletionsTool(managerProxy, {
|
|
2220
|
+
getTrackedVersion: (uri) => {
|
|
2221
|
+
let services = host.getServicesIfReady();
|
|
2222
|
+
return services ? services.fileSync.getTrackedVersion(uri) : null;
|
|
2223
|
+
},
|
|
2224
|
+
setTrackedVersion: (uri, v) => {
|
|
2225
|
+
host.getServicesIfReady()?.fileSync.setTrackedVersion(uri, v);
|
|
2226
|
+
},
|
|
2227
|
+
isSyntheticDotActive: (uri) => syntheticDotLocks.has(uri)
|
|
2228
|
+
}, treeSitterProxy), ensureRuntime, host);
|
|
2229
|
+
let getRootDir = () => {
|
|
2230
|
+
let manager = host.getServicesIfReady()?.manager;
|
|
2231
|
+
return manager ? manager.resolvePath(".") : sessionCwd || process.cwd();
|
|
2232
|
+
};
|
|
2233
|
+
registerToolWithRuntime(pi, createCodeOverviewTool(getRootDir, treeSitterProxy, workspaceIndexProxy), ensureRuntime, host), registerToolWithRuntime(pi, createCodeSearchTool(getRootDir, treeSitterProxy), ensureRuntime, host), registerToolWithRuntime(pi, createCodeRewriteTool(getRootDir, treeSitterProxy, {
|
|
2234
|
+
onFileModified: (filePath) => {
|
|
2235
|
+
let services = host.getServicesIfReady();
|
|
2236
|
+
services && services.fileSync.handleFileWrite(filePath).catch(() => {
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2239
|
+
}), ensureRuntime, host), pi.on("tool_result", async (event) => {
|
|
2240
|
+
let generation = host.getGeneration();
|
|
2241
|
+
try {
|
|
2242
|
+
if (isReadToolResult(event) && !event.isError) {
|
|
2243
|
+
let services2 = host.getServicesIfReady();
|
|
2244
|
+
if (services2) {
|
|
2245
|
+
let path = event.input?.path;
|
|
2246
|
+
if (path && await services2.fileSync.handleFileRead(path), host.getGeneration() !== generation)
|
|
2247
|
+
return;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
if ((isWriteToolResult(event) || isEditToolResult(event)) && !event.isError) {
|
|
2251
|
+
let path = event.input?.path;
|
|
2252
|
+
if (path) {
|
|
2253
|
+
let services2 = await ensureRuntime();
|
|
2254
|
+
if (host.getGeneration() !== generation)
|
|
2255
|
+
return;
|
|
2256
|
+
await services2.fileSync.handleFileWrite(path);
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
} catch {
|
|
2260
|
+
}
|
|
2261
|
+
if (host.getGeneration() !== generation)
|
|
2262
|
+
return;
|
|
2263
|
+
let services = host.getServicesIfReady();
|
|
2264
|
+
if ((isWriteToolResult(event) || isEditToolResult(event)) && !event.isError && services) {
|
|
2265
|
+
let path = event.input?.path;
|
|
2266
|
+
if (!path)
|
|
2267
|
+
return;
|
|
2268
|
+
let manager = services.manager, languageId = manager.getLanguageId(path);
|
|
2269
|
+
if (!languageId)
|
|
2270
|
+
return;
|
|
2271
|
+
let inject = projectConfig?.autoInjectDiagnostics;
|
|
2272
|
+
if (inject === !1 || Array.isArray(inject) && !inject.includes(languageId))
|
|
2273
|
+
return;
|
|
2274
|
+
let client = manager.getRunningClient(languageId);
|
|
2275
|
+
if (!client || (await new Promise((r) => setTimeout(r, 1500)), host.getGeneration() !== generation))
|
|
2276
|
+
return;
|
|
2277
|
+
let uri = manager.getFileUri(path), errors = client.getDiagnostics(uri).filter((d) => d.severity === import_vscode_languageserver_protocol3.DiagnosticSeverity.Error);
|
|
2278
|
+
if (errors.length === 0)
|
|
2279
|
+
return;
|
|
2280
|
+
let relPath = relative10(manager.resolvePath("."), manager.resolvePath(path)), lines = errors.slice(0, 10).map((d) => {
|
|
2281
|
+
let line = d.range.start.line + 1, col = d.range.start.character + 1, source = d.source ? ` [${d.source}]` : "";
|
|
2282
|
+
return `${relPath}:${line}:${col} error: ${d.message}${source}`;
|
|
2283
|
+
});
|
|
2284
|
+
errors.length > 10 && lines.push(`... and ${errors.length - 10} more error(s)`);
|
|
2285
|
+
let summary = `
|
|
2286
|
+
|
|
2287
|
+
\u26A0 LSP: ${errors.length} error(s) in ${relPath}:
|
|
2288
|
+
${lines.join(`
|
|
2289
|
+
`)}`;
|
|
2290
|
+
return {
|
|
2291
|
+
content: [
|
|
2292
|
+
...event.content,
|
|
2293
|
+
{ type: "text", text: summary }
|
|
2294
|
+
]
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
}), pi.on("tool_execution_end", async (_event, ctx) => {
|
|
2298
|
+
latestCtx = ctx;
|
|
2299
|
+
let services = host.getServicesIfReady();
|
|
2300
|
+
if (!services)
|
|
2301
|
+
return;
|
|
2302
|
+
let running = services.manager.getStatus().filter((s) => s.running);
|
|
2303
|
+
if (running.length === 0)
|
|
2304
|
+
ctx.ui.setStatus("lsp", ctx.ui.theme.fg("dim", "LSP: idle"));
|
|
2305
|
+
else {
|
|
2306
|
+
let totalDiags = running.reduce((n, s) => n + s.diagnosticsCount, 0), status = `LSP: ${running.map((s) => s.languageId).join(", ")}`;
|
|
2307
|
+
totalDiags > 0 && (status += ` (${totalDiags} diagnostic${totalDiags !== 1 ? "s" : ""})`), ctx.ui.setStatus("lsp", ctx.ui.theme.fg("accent", status));
|
|
2308
|
+
}
|
|
2309
|
+
}), pi.registerCommand("lsp", {
|
|
2310
|
+
description: "Show LSP server status",
|
|
2311
|
+
handler: async (_args, ctx) => {
|
|
2312
|
+
let services = host.getServicesIfReady();
|
|
2313
|
+
if (!services) {
|
|
2314
|
+
ctx.ui.notify("LSP manager not initialized", "warning");
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
let statuses = services.manager.getStatus();
|
|
2318
|
+
if (statuses.length === 0) {
|
|
2319
|
+
ctx.ui.notify("No LSP servers configured", "info");
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
2322
|
+
let lines = statuses.map((s) => {
|
|
2323
|
+
let icon = s.running ? "\u{1F7E2}" : "\u26AA", diags = s.diagnosticsCount > 0 ? ` (${s.diagnosticsCount} diagnostics)` : "", shared = s.shared ? " [shared]" : "";
|
|
2324
|
+
return `${icon} ${s.languageId}: ${s.command}${diags}${shared}`;
|
|
2325
|
+
});
|
|
2326
|
+
ctx.ui.notify(lines.join(`
|
|
2327
|
+
`), "info");
|
|
2328
|
+
}
|
|
2329
|
+
}), pi.registerCommand("lsp-restart", {
|
|
2330
|
+
description: "Restart an LSP server: /lsp-restart <language> (e.g. java, typescript)",
|
|
2331
|
+
handler: async (args, ctx) => {
|
|
2332
|
+
let services = host.getServicesIfReady();
|
|
2333
|
+
if (!services) {
|
|
2334
|
+
ctx.ui.notify("LSP manager not initialized", "warning");
|
|
2335
|
+
return;
|
|
2336
|
+
}
|
|
2337
|
+
let languageId = args?.trim().toLowerCase();
|
|
2338
|
+
if (!languageId) {
|
|
2339
|
+
let statuses = services.manager.getStatus().filter((s) => s.running);
|
|
2340
|
+
if (statuses.length === 0)
|
|
2341
|
+
ctx.ui.notify(`No LSP servers are running.
|
|
2342
|
+
|
|
2343
|
+
Usage: /lsp-restart <language>`, "info");
|
|
2344
|
+
else {
|
|
2345
|
+
let langs = statuses.map((s) => s.languageId).join(", ");
|
|
2346
|
+
ctx.ui.notify(`Running servers: ${langs}
|
|
2347
|
+
|
|
2348
|
+
Usage: /lsp-restart <language>
|
|
2349
|
+
Example: /lsp-restart java`, "info");
|
|
2350
|
+
}
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
ctx.ui.setStatus("lsp", ctx.ui.theme.fg("warning", `LSP: restarting ${languageId}...`)), ctx.ui.notify(`Restarting ${languageId} server (kills daemon if shared)...`, "info");
|
|
2354
|
+
try {
|
|
2355
|
+
await services.manager.restartServer(languageId);
|
|
2356
|
+
let lombokJar = languageId === "java" ? services.manager.getLombokJar() : null, lombokNote = lombokJar ? `
|
|
2357
|
+
Lombok: ${lombokJar}` : "";
|
|
2358
|
+
ctx.ui.notify(`${languageId} server restarted successfully.${lombokNote}`, "info"), ctx.ui.setStatus("lsp", ctx.ui.theme.fg("accent", `LSP: ${languageId} ready`));
|
|
2359
|
+
} catch (err) {
|
|
2360
|
+
ctx.ui.notify(`Failed to restart ${languageId}: ${err.message}`, "error"), ctx.ui.setStatus("lsp", ctx.ui.theme.fg("error", `LSP: ${languageId} restart failed`));
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
}), pi.registerCommand("lsp-config", {
|
|
2364
|
+
description: "Configure an LSP server: /lsp-config <language> <command> [args...]",
|
|
2365
|
+
handler: async (args, ctx) => {
|
|
2366
|
+
if (!args?.trim()) {
|
|
2367
|
+
ctx.ui.notify(`Usage: /lsp-config <language> <command> [args...]
|
|
2368
|
+
Example: /lsp-config python pylsp`, "info");
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
let parts = args.trim().split(/\s+/);
|
|
2372
|
+
if (parts.length < 2) {
|
|
2373
|
+
ctx.ui.notify("Usage: /lsp-config <language> <command> [args...]", "warning");
|
|
2374
|
+
return;
|
|
2375
|
+
}
|
|
2376
|
+
let [languageId, command, ...serverArgs] = parts, config = { command, args: serverArgs };
|
|
2377
|
+
(await ensureRuntime()).manager.setServerConfig(languageId, config), ctx.ui.notify(`Configured LSP for ${languageId}: ${command} ${serverArgs.join(" ")}`, "info");
|
|
2378
|
+
}
|
|
2379
|
+
}), pi.registerCommand("lsp-lombok", {
|
|
2380
|
+
description: "Set Lombok jar path for Java: /lsp-lombok <path-to-lombok.jar>",
|
|
2381
|
+
handler: async (args, ctx) => {
|
|
2382
|
+
let mgr = (await ensureRuntime()).manager;
|
|
2383
|
+
if (!args?.trim()) {
|
|
2384
|
+
let current = mgr.getLombokJar();
|
|
2385
|
+
current ? ctx.ui.notify(`Lombok jar: ${current}`, "info") : ctx.ui.notify(`No Lombok jar configured or detected.
|
|
2386
|
+
|
|
2387
|
+
Usage: /lsp-lombok <path-to-lombok.jar>
|
|
2388
|
+
Or set LOMBOK_JAR environment variable.
|
|
2389
|
+
|
|
2390
|
+
Download from: https://projectlombok.org/download`, "info");
|
|
2391
|
+
return;
|
|
2392
|
+
}
|
|
2393
|
+
let jarPath = args.trim(), resolved = resolve3(ctx.cwd, jarPath);
|
|
2394
|
+
if (!existsSync2(resolved)) {
|
|
2395
|
+
ctx.ui.notify(`File not found: ${resolved}`, "error");
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
resolved.endsWith(".jar") || ctx.ui.notify(`Warning: ${resolved} doesn't end in .jar \u2014 setting anyway`, "warning"), mgr.setLombokJar(resolved), ctx.ui.notify(`Lombok jar set: ${resolved}`, "info");
|
|
2399
|
+
}
|
|
2400
|
+
}), pi.on("session_shutdown", async () => {
|
|
2401
|
+
latestCtx = null, await host.shutdown();
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
export {
|
|
2405
|
+
LspRuntimeHost,
|
|
2406
|
+
lspExtension as default
|
|
2407
|
+
};
|