@oh-my-pi/pi-coding-agent 8.0.20 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +125 -0
- package/docs/session.md +111 -46
- package/examples/custom-tools/hello/index.ts +1 -1
- package/examples/custom-tools/todo/index.ts +3 -4
- package/examples/extensions/api-demo.ts +0 -1
- package/examples/extensions/chalk-logger.ts +2 -3
- package/examples/extensions/hello.ts +0 -1
- package/examples/extensions/pirate.ts +0 -1
- package/examples/extensions/plan-mode.ts +15 -16
- package/examples/extensions/todo.ts +3 -4
- package/examples/extensions/tools.ts +1 -2
- package/examples/extensions/with-deps/index.ts +0 -1
- package/examples/hooks/auto-commit-on-exit.ts +1 -2
- package/examples/hooks/confirm-destructive.ts +0 -1
- package/examples/hooks/custom-compaction.ts +1 -2
- package/examples/hooks/dirty-repo-guard.ts +0 -1
- package/examples/hooks/file-trigger.ts +3 -4
- package/examples/hooks/git-checkpoint.ts +0 -1
- package/examples/hooks/handoff.ts +3 -4
- package/examples/hooks/permission-gate.ts +1 -2
- package/examples/hooks/protected-paths.ts +1 -2
- package/examples/hooks/qna.ts +2 -3
- package/examples/hooks/snake.ts +4 -5
- package/examples/hooks/status-line.ts +0 -1
- package/examples/sdk/01-minimal.ts +2 -3
- package/examples/sdk/02-custom-model.ts +2 -3
- package/examples/sdk/03-custom-prompt.ts +3 -4
- package/examples/sdk/04-skills.ts +2 -3
- package/examples/sdk/06-extensions.ts +1 -2
- package/examples/sdk/06-hooks.ts +6 -7
- package/examples/sdk/07-context-files.ts +0 -1
- package/examples/sdk/08-prompt-templates.ts +0 -1
- package/examples/sdk/08-slash-commands.ts +0 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +0 -1
- package/examples/sdk/10-settings.ts +0 -1
- package/examples/sdk/11-sessions.ts +0 -1
- package/package.json +54 -23
- package/scripts/format-prompts.ts +0 -1
- package/src/capability/context-file.ts +3 -4
- package/src/capability/extension-module.ts +3 -4
- package/src/capability/extension.ts +3 -4
- package/src/capability/fs.ts +20 -21
- package/src/capability/hook.ts +3 -4
- package/src/capability/index.ts +15 -16
- package/src/capability/instruction.ts +3 -4
- package/src/capability/mcp.ts +3 -4
- package/src/capability/prompt.ts +3 -4
- package/src/capability/rule.ts +3 -4
- package/src/capability/settings.ts +2 -3
- package/src/capability/skill.ts +3 -4
- package/src/capability/slash-command.ts +3 -4
- package/src/capability/ssh.ts +3 -4
- package/src/capability/system-prompt.ts +3 -4
- package/src/capability/tool.ts +3 -4
- package/src/cli/args.ts +5 -6
- package/src/cli/config-cli.ts +6 -7
- package/src/cli/file-processor.ts +19 -17
- package/src/cli/jupyter-cli.ts +105 -0
- package/src/cli/list-models.ts +10 -11
- package/src/cli/plugin-cli.ts +20 -25
- package/src/cli/session-picker.ts +2 -3
- package/src/cli/setup-cli.ts +2 -3
- package/src/cli/stats-cli.ts +2 -3
- package/src/cli/update-cli.ts +25 -22
- package/src/commit/agentic/agent.ts +307 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +351 -0
- package/src/commit/agentic/prompts/analyze-file.md +22 -0
- package/src/commit/agentic/prompts/session-user.md +26 -0
- package/src/commit/agentic/prompts/split-confirm.md +1 -0
- package/src/commit/agentic/prompts/system.md +40 -0
- package/src/commit/agentic/state.ts +69 -0
- package/src/commit/agentic/tools/analyze-file.ts +131 -0
- package/src/commit/agentic/tools/git-file-diff.ts +194 -0
- package/src/commit/agentic/tools/git-hunk.ts +50 -0
- package/src/commit/agentic/tools/git-overview.ts +84 -0
- package/src/commit/agentic/tools/index.ts +56 -0
- package/src/commit/agentic/tools/propose-changelog.ts +128 -0
- package/src/commit/agentic/tools/propose-commit.ts +154 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/split-commit.ts +280 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +200 -0
- package/src/commit/analysis/conventional.ts +165 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +112 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +36 -0
- package/src/commit/changelog/generate.ts +110 -0
- package/src/commit/changelog/index.ts +233 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +93 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/git/errors.ts +11 -0
- package/src/commit/git/index.ts +212 -0
- package/src/commit/git/operations.ts +53 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/index.ts +63 -0
- package/src/commit/map-reduce/map-phase.ts +178 -0
- package/src/commit/map-reduce/reduce-phase.ts +145 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +80 -0
- package/src/commit/pipeline.ts +240 -0
- package/src/commit/prompts/analysis-system.md +155 -0
- package/src/commit/prompts/analysis-user.md +41 -0
- package/src/commit/prompts/changelog-system.md +56 -0
- package/src/commit/prompts/changelog-user.md +19 -0
- package/src/commit/prompts/file-observer-system.md +26 -0
- package/src/commit/prompts/file-observer-user.md +9 -0
- package/src/commit/prompts/reduce-system.md +60 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +4 -0
- package/src/commit/prompts/summary-system.md +52 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/types.ts +109 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/config/file-lock.ts +121 -0
- package/src/config/keybindings.ts +6 -8
- package/src/config/model-registry.ts +65 -38
- package/src/config/model-resolver.ts +18 -19
- package/src/config/prompt-templates.ts +11 -11
- package/src/config/settings-manager.ts +141 -50
- package/src/config.ts +64 -66
- package/src/cursor.ts +11 -9
- package/src/discovery/agents-md.ts +11 -12
- package/src/discovery/builtin.ts +68 -73
- package/src/discovery/claude.ts +41 -42
- package/src/discovery/cline.ts +11 -12
- package/src/discovery/codex.ts +52 -53
- package/src/discovery/cursor.ts +9 -10
- package/src/discovery/gemini.ts +17 -22
- package/src/discovery/github.ts +13 -14
- package/src/discovery/helpers.ts +35 -34
- package/src/discovery/index.ts +22 -24
- package/src/discovery/mcp-json.ts +8 -9
- package/src/discovery/ssh.ts +8 -9
- package/src/discovery/vscode.ts +4 -5
- package/src/discovery/windsurf.ts +6 -7
- package/src/exa/company.ts +1 -2
- package/src/exa/index.ts +2 -3
- package/src/exa/linkedin.ts +1 -2
- package/src/exa/mcp-client.ts +14 -16
- package/src/exa/render.ts +10 -11
- package/src/exa/researcher.ts +1 -2
- package/src/exa/search.ts +1 -2
- package/src/exa/types.ts +0 -1
- package/src/exa/websets.ts +1 -2
- package/src/exec/bash-executor.ts +3 -4
- package/src/exec/exec.ts +0 -1
- package/src/export/custom-share.ts +5 -6
- package/src/export/html/index.ts +24 -21
- package/src/export/ttsr.ts +2 -3
- package/src/extensibility/custom-commands/bundled/review/index.ts +7 -8
- package/src/extensibility/custom-commands/loader.ts +18 -15
- package/src/extensibility/custom-commands/types.ts +2 -3
- package/src/extensibility/custom-tools/loader.ts +11 -12
- package/src/extensibility/custom-tools/types.ts +7 -8
- package/src/extensibility/custom-tools/wrapper.ts +2 -3
- package/src/extensibility/extensions/loader.ts +76 -54
- package/src/extensibility/extensions/runner.ts +11 -12
- package/src/extensibility/extensions/types.ts +20 -27
- package/src/extensibility/extensions/wrapper.ts +3 -4
- package/src/extensibility/hooks/index.ts +1 -1
- package/src/extensibility/hooks/loader.ts +9 -10
- package/src/extensibility/hooks/runner.ts +7 -8
- package/src/extensibility/hooks/tool-wrapper.ts +0 -1
- package/src/extensibility/hooks/types.ts +11 -18
- package/src/extensibility/plugins/doctor.ts +3 -3
- package/src/extensibility/plugins/installer.ts +27 -27
- package/src/extensibility/plugins/loader.ts +59 -56
- package/src/extensibility/plugins/manager.ts +211 -171
- package/src/extensibility/plugins/parser.ts +1 -1
- package/src/extensibility/plugins/paths.ts +8 -8
- package/src/extensibility/skills.ts +63 -60
- package/src/extensibility/slash-commands.ts +10 -10
- package/src/index.ts +54 -54
- package/src/internal-urls/agent-protocol.ts +21 -11
- package/src/internal-urls/artifact-protocol.ts +17 -13
- package/src/internal-urls/router.ts +1 -2
- package/src/internal-urls/rule-protocol.ts +3 -4
- package/src/internal-urls/skill-protocol.ts +3 -4
- package/src/ipy/executor.ts +109 -9
- package/src/ipy/gateway-coordinator.ts +79 -90
- package/src/ipy/kernel.ts +32 -30
- package/src/ipy/modules.ts +13 -13
- package/src/lsp/client.ts +21 -10
- package/src/lsp/clients/biome-client.ts +1 -2
- package/src/lsp/clients/index.ts +3 -3
- package/src/lsp/clients/lsp-linter-client.ts +4 -5
- package/src/lsp/config.ts +15 -15
- package/src/lsp/edits.ts +4 -5
- package/src/lsp/index.ts +43 -44
- package/src/lsp/lspmux.ts +8 -8
- package/src/lsp/render.ts +99 -61
- package/src/lsp/utils.ts +3 -3
- package/src/main.ts +71 -37
- package/src/mcp/client.ts +2 -3
- package/src/mcp/config.ts +5 -6
- package/src/mcp/json-rpc.ts +0 -1
- package/src/mcp/loader.ts +6 -7
- package/src/mcp/manager.ts +17 -18
- package/src/mcp/tool-bridge.ts +4 -9
- package/src/mcp/tool-cache.ts +2 -3
- package/src/mcp/transports/http.ts +2 -4
- package/src/mcp/transports/stdio.ts +1 -2
- package/src/migrations.ts +63 -52
- package/src/modes/components/armin.ts +4 -5
- package/src/modes/components/assistant-message.ts +33 -5
- package/src/modes/components/bash-execution.ts +7 -8
- package/src/modes/components/bordered-loader.ts +3 -3
- package/src/modes/components/branch-summary-message.ts +3 -3
- package/src/modes/components/compaction-summary-message.ts +3 -3
- package/src/modes/components/countdown-timer.ts +0 -1
- package/src/modes/components/custom-message.ts +5 -5
- package/src/modes/components/diff.ts +1 -1
- package/src/modes/components/dynamic-border.ts +2 -2
- package/src/modes/components/extensions/extension-dashboard.ts +6 -7
- package/src/modes/components/extensions/extension-list.ts +2 -3
- package/src/modes/components/extensions/inspector-panel.ts +3 -4
- package/src/modes/components/extensions/state-manager.ts +25 -26
- package/src/modes/components/extensions/types.ts +1 -2
- package/src/modes/components/footer.ts +47 -43
- package/src/modes/components/history-search.ts +2 -2
- package/src/modes/components/hook-editor.ts +3 -4
- package/src/modes/components/hook-input.ts +2 -3
- package/src/modes/components/hook-message.ts +5 -5
- package/src/modes/components/hook-selector.ts +2 -3
- package/src/modes/components/keybinding-hints.ts +2 -3
- package/src/modes/components/login-dialog.ts +2 -2
- package/src/modes/components/model-selector.ts +12 -12
- package/src/modes/components/oauth-selector.ts +2 -2
- package/src/modes/components/plugin-settings.ts +20 -20
- package/src/modes/components/python-execution.ts +7 -8
- package/src/modes/components/queue-mode-selector.ts +3 -3
- package/src/modes/components/read-tool-group.ts +2 -2
- package/src/modes/components/session-selector.ts +4 -4
- package/src/modes/components/settings-defs.ts +77 -69
- package/src/modes/components/settings-selector.ts +16 -16
- package/src/modes/components/show-images-selector.ts +2 -2
- package/src/modes/components/status-line/segments.ts +4 -4
- package/src/modes/components/status-line/separators.ts +1 -1
- package/src/modes/components/status-line/types.ts +2 -2
- package/src/modes/components/status-line-segment-editor.ts +7 -8
- package/src/modes/components/status-line.ts +12 -12
- package/src/modes/components/theme-selector.ts +8 -7
- package/src/modes/components/thinking-selector.ts +4 -4
- package/src/modes/components/todo-display.ts +2 -2
- package/src/modes/components/todo-reminder.ts +4 -4
- package/src/modes/components/tool-execution.ts +16 -19
- package/src/modes/components/tree-selector.ts +12 -12
- package/src/modes/components/ttsr-notification.ts +5 -5
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +1 -1
- package/src/modes/components/visual-truncate.ts +0 -1
- package/src/modes/components/welcome.ts +4 -4
- package/src/modes/controllers/command-controller.ts +46 -47
- package/src/modes/controllers/event-controller.ts +16 -20
- package/src/modes/controllers/extension-ui-controller.ts +40 -46
- package/src/modes/controllers/input-controller.ts +17 -18
- package/src/modes/controllers/selector-controller.ts +103 -91
- package/src/modes/index.ts +3 -3
- package/src/modes/interactive-mode.ts +31 -31
- package/src/modes/print-mode.ts +12 -13
- package/src/modes/rpc/rpc-client.ts +7 -8
- package/src/modes/rpc/rpc-mode.ts +24 -28
- package/src/modes/rpc/rpc-types.ts +3 -4
- package/src/modes/theme/mermaid-cache.ts +89 -0
- package/src/modes/theme/theme.ts +130 -53
- package/src/modes/types.ts +10 -10
- package/src/modes/utils/ui-helpers.ts +17 -17
- package/src/patch/applicator.ts +18 -19
- package/src/patch/diff.ts +1 -2
- package/src/patch/fuzzy.ts +1 -2
- package/src/patch/index.ts +11 -18
- package/src/patch/normalize.ts +4 -4
- package/src/patch/normative.ts +1 -2
- package/src/patch/parser.ts +8 -9
- package/src/patch/shared.ts +43 -16
- package/src/prompts/tools/task.md +2 -0
- package/src/sdk.ts +100 -65
- package/src/session/agent-session.ts +84 -85
- package/src/session/agent-storage.ts +43 -39
- package/src/session/artifacts.ts +32 -10
- package/src/session/auth-storage.ts +50 -39
- package/src/session/compaction/branch-summarization.ts +7 -10
- package/src/session/compaction/compaction.ts +8 -19
- package/src/session/compaction/utils.ts +6 -9
- package/src/session/history-storage.ts +10 -10
- package/src/session/messages.ts +4 -5
- package/src/session/session-manager.ts +76 -65
- package/src/session/session-storage.ts +57 -69
- package/src/session/storage-migration.ts +14 -56
- package/src/session/streaming-output.ts +2 -2
- package/src/ssh/connection-manager.ts +43 -50
- package/src/ssh/ssh-executor.ts +2 -2
- package/src/ssh/sshfs-mount.ts +11 -18
- package/src/system-prompt.ts +28 -35
- package/src/task/agents.ts +45 -30
- package/src/task/commands.ts +6 -7
- package/src/task/discovery.ts +39 -76
- package/src/task/executor.ts +14 -15
- package/src/task/index.ts +40 -34
- package/src/task/output-manager.ts +93 -0
- package/src/task/parallel.ts +0 -1
- package/src/task/render.ts +24 -30
- package/src/task/subprocess-tool-registry.ts +1 -2
- package/src/task/worker-protocol.ts +3 -3
- package/src/task/worker.ts +33 -39
- package/src/task/worktree.ts +19 -19
- package/src/tools/ask.ts +41 -20
- package/src/tools/bash-interceptor.ts +1 -5
- package/src/tools/bash.ts +91 -97
- package/src/tools/calculator.ts +49 -47
- package/src/tools/complete.ts +4 -5
- package/src/tools/context.ts +2 -2
- package/src/tools/fetch.ts +84 -124
- package/src/tools/find.ts +94 -98
- package/src/tools/gemini-image.ts +14 -14
- package/src/tools/grep.ts +100 -116
- package/src/tools/index.ts +80 -55
- package/src/tools/list-limit.ts +1 -1
- package/src/tools/ls.ts +44 -70
- package/src/tools/notebook.ts +51 -67
- package/src/tools/output-meta.ts +3 -4
- package/src/tools/output-utils.ts +2 -2
- package/src/tools/path-utils.ts +5 -5
- package/src/tools/python.ts +104 -217
- package/src/tools/read.ts +92 -33
- package/src/tools/render-utils.ts +8 -23
- package/src/tools/renderers.ts +6 -7
- package/src/tools/review.ts +8 -11
- package/src/tools/ssh.ts +69 -49
- package/src/tools/todo-write.ts +37 -25
- package/src/tools/tool-errors.ts +3 -3
- package/src/tools/tool-result.ts +3 -8
- package/src/tools/write.ts +99 -75
- package/src/tui/code-cell.ts +109 -0
- package/src/tui/file-list.ts +47 -0
- package/src/tui/index.ts +11 -0
- package/src/tui/output-block.ts +72 -0
- package/src/tui/status-line.ts +39 -0
- package/src/tui/tree-list.ts +55 -0
- package/src/tui/types.ts +16 -0
- package/src/tui/utils.ts +48 -0
- package/src/utils/changelog.ts +9 -10
- package/src/utils/clipboard.ts +11 -11
- package/src/utils/file-mentions.ts +4 -10
- package/src/utils/frontmatter.ts +6 -3
- package/src/utils/fuzzy.ts +2 -2
- package/src/utils/image-convert.ts +1 -1
- package/src/utils/image-resize.ts +1 -1
- package/src/utils/mime.ts +2 -2
- package/src/utils/shell-snapshot.ts +11 -13
- package/src/utils/shell.ts +4 -5
- package/src/utils/title-generator.ts +8 -9
- package/src/utils/tools-manager.ts +23 -23
- package/src/vendor/photon/index.js +1099 -1059
- package/src/vendor/photon/photon_rs_bg.wasm +0 -0
- package/src/web/scrapers/artifacthub.ts +1 -1
- package/src/web/scrapers/arxiv.ts +2 -2
- package/src/web/scrapers/bluesky.ts +2 -2
- package/src/web/scrapers/cheatsh.ts +1 -1
- package/src/web/scrapers/chocolatey.ts +2 -2
- package/src/web/scrapers/choosealicense.ts +5 -5
- package/src/web/scrapers/cisa-kev.ts +1 -1
- package/src/web/scrapers/crossref.ts +2 -2
- package/src/web/scrapers/devto.ts +3 -3
- package/src/web/scrapers/discogs.ts +3 -4
- package/src/web/scrapers/discourse.ts +1 -1
- package/src/web/scrapers/dockerhub.ts +1 -1
- package/src/web/scrapers/fdroid.ts +2 -2
- package/src/web/scrapers/firefox-addons.ts +3 -3
- package/src/web/scrapers/flathub.ts +1 -1
- package/src/web/scrapers/github.ts +3 -3
- package/src/web/scrapers/gitlab.ts +4 -4
- package/src/web/scrapers/hackernews.ts +2 -2
- package/src/web/scrapers/huggingface.ts +1 -1
- package/src/web/scrapers/iacr.ts +2 -2
- package/src/web/scrapers/index.ts +0 -1
- package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
- package/src/web/scrapers/lemmy.ts +2 -2
- package/src/web/scrapers/maven.ts +2 -2
- package/src/web/scrapers/mdn.ts +2 -4
- package/src/web/scrapers/metacpan.ts +2 -2
- package/src/web/scrapers/musicbrainz.ts +1 -2
- package/src/web/scrapers/npm.ts +1 -1
- package/src/web/scrapers/nuget.ts +2 -2
- package/src/web/scrapers/nvd.ts +3 -3
- package/src/web/scrapers/ollama.ts +7 -9
- package/src/web/scrapers/opencorporates.ts +2 -2
- package/src/web/scrapers/openlibrary.ts +6 -6
- package/src/web/scrapers/orcid.ts +0 -1
- package/src/web/scrapers/osv.ts +2 -2
- package/src/web/scrapers/packagist.ts +1 -1
- package/src/web/scrapers/pubmed.ts +1 -2
- package/src/web/scrapers/rawg.ts +2 -2
- package/src/web/scrapers/readthedocs.ts +1 -2
- package/src/web/scrapers/repology.ts +2 -2
- package/src/web/scrapers/rfc.ts +1 -1
- package/src/web/scrapers/searchcode.ts +2 -2
- package/src/web/scrapers/semantic-scholar.ts +1 -1
- package/src/web/scrapers/snapcraft.ts +2 -2
- package/src/web/scrapers/sourcegraph.ts +1 -1
- package/src/web/scrapers/spdx.ts +3 -3
- package/src/web/scrapers/spotify.ts +0 -1
- package/src/web/scrapers/twitter.ts +1 -1
- package/src/web/scrapers/types.ts +1 -2
- package/src/web/scrapers/utils.ts +5 -5
- package/src/web/scrapers/wikidata.ts +3 -3
- package/src/web/scrapers/youtube.ts +9 -14
- package/src/web/search/auth.ts +5 -10
- package/src/web/search/index.ts +11 -21
- package/src/web/search/providers/anthropic.ts +3 -9
- package/src/web/search/providers/exa.ts +6 -10
- package/src/web/search/providers/perplexity.ts +5 -5
- package/src/web/search/render.ts +129 -175
- package/tsconfig.json +0 -42
package/src/lsp/index.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { existsSync, statSync } from "node:fs";
|
|
1
|
+
import * as fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
5
|
-
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
6
|
-
import { type Theme, theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
7
|
-
import lspDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/lsp.md" with { type: "text" };
|
|
8
|
-
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools/index";
|
|
9
|
-
import { resolveToCwd } from "@oh-my-pi/pi-coding-agent/tools/path-utils";
|
|
10
|
-
import { throwIfAborted } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
|
11
4
|
import { logger, once, untilAborted } from "@oh-my-pi/pi-utils";
|
|
12
5
|
import type { BunFile } from "bun";
|
|
6
|
+
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
7
|
+
import { type Theme, theme } from "../modes/theme/theme";
|
|
8
|
+
import lspDescription from "../prompts/tools/lsp.md" with { type: "text" };
|
|
9
|
+
import type { ToolSession } from "../tools";
|
|
10
|
+
import { resolveToCwd } from "../tools/path-utils";
|
|
11
|
+
import { throwIfAborted } from "../tools/tool-errors";
|
|
13
12
|
import {
|
|
14
13
|
ensureFileOpen,
|
|
15
14
|
getActiveClients,
|
|
@@ -254,21 +253,21 @@ function limitDiagnosticMessages(messages: string[]): string[] {
|
|
|
254
253
|
}
|
|
255
254
|
|
|
256
255
|
function findFileByExtensions(baseDir: string, extensions: string[], maxDepth: number): string | null {
|
|
257
|
-
const normalized = extensions.map(
|
|
256
|
+
const normalized = extensions.map(ext => ext.toLowerCase());
|
|
258
257
|
const search = (dir: string, depth: number): string | null => {
|
|
259
258
|
if (depth > maxDepth) return null;
|
|
260
|
-
const entries: Dirent[] = [];
|
|
259
|
+
const entries: fs.Dirent[] = [];
|
|
261
260
|
try {
|
|
262
261
|
const names = Array.from(new Bun.Glob("*").scanSync({ cwd: dir, onlyFiles: false }));
|
|
263
262
|
for (const name of names) {
|
|
264
263
|
const fullPath = path.join(dir, name);
|
|
265
264
|
let isDir = false;
|
|
266
265
|
try {
|
|
267
|
-
isDir = statSync(fullPath).isDirectory();
|
|
266
|
+
isDir = fs.statSync(fullPath).isDirectory();
|
|
268
267
|
} catch {
|
|
269
268
|
continue;
|
|
270
269
|
}
|
|
271
|
-
entries.push({ name, isFile: () => !isDir, isDirectory: () => isDir } as Dirent);
|
|
270
|
+
entries.push({ name, isFile: () => !isDir, isDirectory: () => isDir } as fs.Dirent);
|
|
272
271
|
}
|
|
273
272
|
} catch {
|
|
274
273
|
return null;
|
|
@@ -281,7 +280,7 @@ function findFileByExtensions(baseDir: string, extensions: string[], maxDepth: n
|
|
|
281
280
|
|
|
282
281
|
if (entry.isFile()) {
|
|
283
282
|
const lowerName = entry.name.toLowerCase();
|
|
284
|
-
if (normalized.some(
|
|
283
|
+
if (normalized.some(ext => lowerName.endsWith(ext))) {
|
|
285
284
|
return fullPath;
|
|
286
285
|
}
|
|
287
286
|
} else if (entry.isDirectory()) {
|
|
@@ -362,22 +361,22 @@ interface ProjectType {
|
|
|
362
361
|
/** Detect project type from root markers */
|
|
363
362
|
function detectProjectType(cwd: string): ProjectType {
|
|
364
363
|
// Check for Rust (Cargo.toml)
|
|
365
|
-
if (existsSync(path.join(cwd, "Cargo.toml"))) {
|
|
364
|
+
if (fs.existsSync(path.join(cwd, "Cargo.toml"))) {
|
|
366
365
|
return { type: "rust", command: ["cargo", "check", "--message-format=short"], description: "Rust (cargo check)" };
|
|
367
366
|
}
|
|
368
367
|
|
|
369
368
|
// Check for TypeScript (tsconfig.json)
|
|
370
|
-
if (existsSync(path.join(cwd, "tsconfig.json"))) {
|
|
369
|
+
if (fs.existsSync(path.join(cwd, "tsconfig.json"))) {
|
|
371
370
|
return { type: "typescript", command: ["npx", "tsc", "--noEmit"], description: "TypeScript (tsc --noEmit)" };
|
|
372
371
|
}
|
|
373
372
|
|
|
374
373
|
// Check for Go (go.mod)
|
|
375
|
-
if (existsSync(path.join(cwd, "go.mod"))) {
|
|
374
|
+
if (fs.existsSync(path.join(cwd, "go.mod"))) {
|
|
376
375
|
return { type: "go", command: ["go", "build", "./..."], description: "Go (go build)" };
|
|
377
376
|
}
|
|
378
377
|
|
|
379
378
|
// Check for Python (pyproject.toml or pyrightconfig.json)
|
|
380
|
-
if (existsSync(path.join(cwd, "pyproject.toml")) || existsSync(path.join(cwd, "pyrightconfig.json"))) {
|
|
379
|
+
if (fs.existsSync(path.join(cwd, "pyproject.toml")) || fs.existsSync(path.join(cwd, "pyrightconfig.json"))) {
|
|
381
380
|
return { type: "python", command: ["pyright"], description: "Python (pyright)" };
|
|
382
381
|
}
|
|
383
382
|
|
|
@@ -412,10 +411,10 @@ async function runWorkspaceDiagnostics(
|
|
|
412
411
|
return { output: "No issues found", projectType };
|
|
413
412
|
}
|
|
414
413
|
|
|
415
|
-
const summary = formatDiagnosticsSummary(collected.map(
|
|
416
|
-
const formatted = collected.slice(0, 50).map(
|
|
414
|
+
const summary = formatDiagnosticsSummary(collected.map(d => d.diagnostic));
|
|
415
|
+
const formatted = collected.slice(0, 50).map(d => formatDiagnostic(d.diagnostic, d.filePath));
|
|
417
416
|
const more = collected.length > 50 ? `\n ... and ${collected.length - 50} more` : "";
|
|
418
|
-
return { output: `${summary}:\n${formatted.map(
|
|
417
|
+
return { output: `${summary}:\n${formatted.map(f => ` ${f}`).join("\n")}${more}`, projectType };
|
|
419
418
|
} catch (err) {
|
|
420
419
|
logger.debug("LSP diagnostics failed, falling back to shell", { error: String(err) });
|
|
421
420
|
// Fall through to shell command
|
|
@@ -571,10 +570,10 @@ async function getDiagnosticsForFile(
|
|
|
571
570
|
}
|
|
572
571
|
}
|
|
573
572
|
|
|
574
|
-
const formatted = uniqueDiagnostics.map(
|
|
573
|
+
const formatted = uniqueDiagnostics.map(d => formatDiagnostic(d, relPath));
|
|
575
574
|
const limited = limitDiagnosticMessages(formatted);
|
|
576
575
|
const summary = formatDiagnosticsSummary(uniqueDiagnostics);
|
|
577
|
-
const hasErrors = uniqueDiagnostics.some(
|
|
576
|
+
const hasErrors = uniqueDiagnostics.some(d => d.severity === 1);
|
|
578
577
|
|
|
579
578
|
return {
|
|
580
579
|
server: serverNames.join(", "),
|
|
@@ -1096,8 +1095,8 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1096
1095
|
}
|
|
1097
1096
|
|
|
1098
1097
|
const summary = formatDiagnosticsSummary(uniqueDiagnostics);
|
|
1099
|
-
const formatted = uniqueDiagnostics.map(
|
|
1100
|
-
const output = `${summary}:\n${formatted.map(
|
|
1098
|
+
const formatted = uniqueDiagnostics.map(d => formatDiagnostic(d, relPath));
|
|
1099
|
+
const output = `${summary}:\n${formatted.map(f => ` ${f}`).join("\n")}`;
|
|
1101
1100
|
return {
|
|
1102
1101
|
content: [{ type: "text", text: output }],
|
|
1103
1102
|
details: { action, serverName: Array.from(allServerNames).join(", "), success: true },
|
|
@@ -1187,7 +1186,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1187
1186
|
output = "No definition found";
|
|
1188
1187
|
} else {
|
|
1189
1188
|
const raw = Array.isArray(result) ? result : [result];
|
|
1190
|
-
const locations = raw.flatMap(
|
|
1189
|
+
const locations = raw.flatMap(loc => {
|
|
1191
1190
|
if ("uri" in loc) {
|
|
1192
1191
|
return [loc as Location];
|
|
1193
1192
|
}
|
|
@@ -1203,7 +1202,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1203
1202
|
output = "No definition found";
|
|
1204
1203
|
} else {
|
|
1205
1204
|
output = `Found ${locations.length} definition(s):\n${locations
|
|
1206
|
-
.map(
|
|
1205
|
+
.map(loc => ` ${formatLocation(loc, this.session.cwd)}`)
|
|
1207
1206
|
.join("\n")}`;
|
|
1208
1207
|
}
|
|
1209
1208
|
}
|
|
@@ -1220,7 +1219,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1220
1219
|
if (!result || result.length === 0) {
|
|
1221
1220
|
output = "No references found";
|
|
1222
1221
|
} else {
|
|
1223
|
-
const lines = result.map(
|
|
1222
|
+
const lines = result.map(loc => ` ${formatLocation(loc, this.session.cwd)}`);
|
|
1224
1223
|
output = `Found ${result.length} reference(s):\n${lines.join("\n")}`;
|
|
1225
1224
|
}
|
|
1226
1225
|
break;
|
|
@@ -1257,11 +1256,11 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1257
1256
|
// Check if hierarchical (DocumentSymbol) or flat (SymbolInformation)
|
|
1258
1257
|
if ("selectionRange" in result[0]) {
|
|
1259
1258
|
// Hierarchical
|
|
1260
|
-
const lines = (result as DocumentSymbol[]).flatMap(
|
|
1259
|
+
const lines = (result as DocumentSymbol[]).flatMap(s => formatDocumentSymbol(s));
|
|
1261
1260
|
output = `Symbols in ${relPath}:\n${lines.join("\n")}`;
|
|
1262
1261
|
} else {
|
|
1263
1262
|
// Flat
|
|
1264
|
-
const lines = (result as SymbolInformation[]).map(
|
|
1263
|
+
const lines = (result as SymbolInformation[]).map(s => {
|
|
1265
1264
|
const line = s.location.range.start.line + 1;
|
|
1266
1265
|
const icon = symbolKindToIcon(s.kind);
|
|
1267
1266
|
return `${icon} ${s.name} @ line ${line}`;
|
|
@@ -1285,8 +1284,8 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1285
1284
|
if (!result || result.length === 0) {
|
|
1286
1285
|
output = `No symbols matching "${query}"`;
|
|
1287
1286
|
} else {
|
|
1288
|
-
const lines = result.map(
|
|
1289
|
-
output = `Found ${result.length} symbol(s) matching "${query}":\n${lines.map(
|
|
1287
|
+
const lines = result.map(s => formatSymbolInformation(s, this.session.cwd));
|
|
1288
|
+
output = `Found ${result.length} symbol(s) matching "${query}":\n${lines.map(l => ` ${l}`).join("\n")}`;
|
|
1290
1289
|
}
|
|
1291
1290
|
break;
|
|
1292
1291
|
}
|
|
@@ -1311,10 +1310,10 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1311
1310
|
const shouldApply = apply !== false;
|
|
1312
1311
|
if (shouldApply) {
|
|
1313
1312
|
const applied = await applyWorkspaceEdit(result, this.session.cwd);
|
|
1314
|
-
output = `Applied rename:\n${applied.map(
|
|
1313
|
+
output = `Applied rename:\n${applied.map(a => ` ${a}`).join("\n")}`;
|
|
1315
1314
|
} else {
|
|
1316
1315
|
const preview = formatWorkspaceEdit(result, this.session.cwd);
|
|
1317
|
-
output = `Rename preview:\n${preview.map(
|
|
1316
|
+
output = `Rename preview:\n${preview.map(p => ` ${p}`).join("\n")}`;
|
|
1318
1317
|
}
|
|
1319
1318
|
}
|
|
1320
1319
|
break;
|
|
@@ -1335,7 +1334,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1335
1334
|
const endCharacter = (end_character ?? column ?? 1) - 1;
|
|
1336
1335
|
const range = { start: position, end: { line: endLine, character: endCharacter } };
|
|
1337
1336
|
const relevantDiagnostics = diagnostics.filter(
|
|
1338
|
-
|
|
1337
|
+
d => d.range.start.line <= range.end.line && d.range.end.line >= range.start.line,
|
|
1339
1338
|
);
|
|
1340
1339
|
|
|
1341
1340
|
const codeActionContext: { diagnostics: Diagnostic[]; only?: string[] } = {
|
|
@@ -1401,7 +1400,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1401
1400
|
|
|
1402
1401
|
if (isCodeAction(resolvedAction) && resolvedAction.edit) {
|
|
1403
1402
|
const applied = await applyWorkspaceEdit(resolvedAction.edit, this.session.cwd);
|
|
1404
|
-
output = `Applied "${codeAction.title}":\n${applied.map(
|
|
1403
|
+
output = `Applied "${codeAction.title}":\n${applied.map(a => ` ${a}`).join("\n")}`;
|
|
1405
1404
|
} else {
|
|
1406
1405
|
const commandPayload = getCommandPayload(resolvedAction);
|
|
1407
1406
|
if (commandPayload) {
|
|
@@ -1450,7 +1449,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1450
1449
|
if (!calls || calls.length === 0) {
|
|
1451
1450
|
output = `No callers found for "${item.name}"`;
|
|
1452
1451
|
} else {
|
|
1453
|
-
const lines = calls.map(
|
|
1452
|
+
const lines = calls.map(call => {
|
|
1454
1453
|
const loc = { uri: call.from.uri, range: call.from.selectionRange };
|
|
1455
1454
|
const detail = call.from.detail ? ` (${call.from.detail})` : "";
|
|
1456
1455
|
return ` ${call.from.name}${detail} @ ${formatLocation(loc, this.session.cwd)}`;
|
|
@@ -1465,7 +1464,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1465
1464
|
if (!calls || calls.length === 0) {
|
|
1466
1465
|
output = `"${item.name}" doesn't call any functions`;
|
|
1467
1466
|
} else {
|
|
1468
|
-
const lines = calls.map(
|
|
1467
|
+
const lines = calls.map(call => {
|
|
1469
1468
|
const loc = { uri: call.to.uri, range: call.to.selectionRange };
|
|
1470
1469
|
const detail = call.to.detail ? ` (${call.to.detail})` : "";
|
|
1471
1470
|
return ` ${call.to.name}${detail} @ ${formatLocation(loc, this.session.cwd)}`;
|
|
@@ -1500,10 +1499,10 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1500
1499
|
if (collected.length === 0) {
|
|
1501
1500
|
output = "Flycheck: no issues found";
|
|
1502
1501
|
} else {
|
|
1503
|
-
const summary = formatDiagnosticsSummary(collected.map(
|
|
1504
|
-
const formatted = collected.slice(0, 20).map(
|
|
1502
|
+
const summary = formatDiagnosticsSummary(collected.map(d => d.diagnostic));
|
|
1503
|
+
const formatted = collected.slice(0, 20).map(d => formatDiagnostic(d.diagnostic, d.filePath));
|
|
1505
1504
|
const more = collected.length > 20 ? `\n ... and ${collected.length - 20} more` : "";
|
|
1506
|
-
output = `Flycheck ${summary}:\n${formatted.map(
|
|
1505
|
+
output = `Flycheck ${summary}:\n${formatted.map(f => ` ${f}`).join("\n")}${more}`;
|
|
1507
1506
|
}
|
|
1508
1507
|
break;
|
|
1509
1508
|
}
|
|
@@ -1561,13 +1560,13 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1561
1560
|
const applied = await applyWorkspaceEdit(result, this.session.cwd);
|
|
1562
1561
|
output =
|
|
1563
1562
|
applied.length > 0
|
|
1564
|
-
? `Applied SSR:\n${applied.map(
|
|
1563
|
+
? `Applied SSR:\n${applied.map(a => ` ${a}`).join("\n")}`
|
|
1565
1564
|
: "SSR: no matches found";
|
|
1566
1565
|
} else {
|
|
1567
1566
|
const preview = formatWorkspaceEdit(result, this.session.cwd);
|
|
1568
1567
|
output =
|
|
1569
1568
|
preview.length > 0
|
|
1570
|
-
? `SSR preview:\n${preview.map(
|
|
1569
|
+
? `SSR preview:\n${preview.map(p => ` ${p}`).join("\n")}`
|
|
1571
1570
|
: "SSR: no matches found";
|
|
1572
1571
|
}
|
|
1573
1572
|
break;
|
|
@@ -1592,7 +1591,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1592
1591
|
if (result.length === 0) {
|
|
1593
1592
|
output = "No runnables found";
|
|
1594
1593
|
} else {
|
|
1595
|
-
const lines = result.map(
|
|
1594
|
+
const lines = result.map(r => {
|
|
1596
1595
|
const args = r.args?.cargoArgs?.join(" ") || "";
|
|
1597
1596
|
return ` [${r.kind}] ${r.label}${args ? ` (cargo ${args})` : ""}`;
|
|
1598
1597
|
});
|
|
@@ -1620,7 +1619,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1620
1619
|
if (result.length === 0) {
|
|
1621
1620
|
output = "No related tests found";
|
|
1622
1621
|
} else {
|
|
1623
|
-
output = `Found ${result.length} related test(s):\n${result.map(
|
|
1622
|
+
output = `Found ${result.length} related test(s):\n${result.map(t => ` ${t}`).join("\n")}`;
|
|
1624
1623
|
}
|
|
1625
1624
|
break;
|
|
1626
1625
|
}
|
package/src/lsp/lspmux.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import * as path from "node:path";
|
|
3
3
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
4
4
|
import { TOML } from "bun";
|
|
5
5
|
|
|
@@ -63,14 +63,14 @@ const STATE_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
|
63
63
|
* Matches Rust's `dirs::config_dir()` behavior.
|
|
64
64
|
*/
|
|
65
65
|
function getConfigPath(): string {
|
|
66
|
-
const home = homedir();
|
|
67
|
-
switch (platform()) {
|
|
66
|
+
const home = os.homedir();
|
|
67
|
+
switch (os.platform()) {
|
|
68
68
|
case "win32":
|
|
69
|
-
return join(process.env.APPDATA ?? join(home, "AppData", "Roaming"), "lspmux", "config.toml");
|
|
69
|
+
return path.join(process.env.APPDATA ?? path.join(home, "AppData", "Roaming"), "lspmux", "config.toml");
|
|
70
70
|
case "darwin":
|
|
71
|
-
return join(home, "Library", "Application Support", "lspmux", "config.toml");
|
|
71
|
+
return path.join(home, "Library", "Application Support", "lspmux", "config.toml");
|
|
72
72
|
default:
|
|
73
|
-
return join(process.env.XDG_CONFIG_HOME ?? join(home, ".config"), "lspmux", "config.toml");
|
|
73
|
+
return path.join(process.env.XDG_CONFIG_HOME ?? path.join(home, ".config"), "lspmux", "config.toml");
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -108,7 +108,7 @@ async function checkServerRunning(binaryPath: string): Promise<boolean> {
|
|
|
108
108
|
|
|
109
109
|
const exited = await Promise.race([
|
|
110
110
|
proc.exited,
|
|
111
|
-
new Promise<null>(
|
|
111
|
+
new Promise<null>(resolve => setTimeout(() => resolve(null), LIVENESS_TIMEOUT_MS)),
|
|
112
112
|
]);
|
|
113
113
|
|
|
114
114
|
if (exited === null) {
|
package/src/lsp/render.ts
CHANGED
|
@@ -7,17 +7,12 @@
|
|
|
7
7
|
* - Grouped references and symbols
|
|
8
8
|
* - Collapsible/expandable views
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
10
|
import type { AgentToolResult, RenderResultOptions } from "@oh-my-pi/pi-agent-core";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
formatExpandHint,
|
|
15
|
-
formatMoreItems,
|
|
16
|
-
TRUNCATE_LENGTHS,
|
|
17
|
-
truncate,
|
|
18
|
-
} from "@oh-my-pi/pi-coding-agent/tools/render-utils";
|
|
19
|
-
import { Text } from "@oh-my-pi/pi-tui";
|
|
11
|
+
import { type Component, Text } from "@oh-my-pi/pi-tui";
|
|
20
12
|
import { highlight, supportsLanguage } from "cli-highlight";
|
|
13
|
+
import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
|
|
14
|
+
import { formatExpandHint, formatMoreItems, TRUNCATE_LENGTHS, truncate } from "../tools/render-utils";
|
|
15
|
+
import { renderOutputBlock, renderStatusLine } from "../tui";
|
|
21
16
|
import type { LspParams, LspToolDetails } from "./types";
|
|
22
17
|
|
|
23
18
|
// =============================================================================
|
|
@@ -30,16 +25,13 @@ import type { LspParams, LspToolDetails } from "./types";
|
|
|
30
25
|
*/
|
|
31
26
|
export function renderCall(args: unknown, theme: Theme): Text {
|
|
32
27
|
const p = args as LspParams & { file?: string; files?: string[] };
|
|
33
|
-
|
|
34
|
-
let text = theme.fg("toolTitle", theme.bold("LSP"));
|
|
35
|
-
text += ` ${theme.fg("accent", p.action || "?")}`;
|
|
36
|
-
|
|
28
|
+
const meta: string[] = [];
|
|
37
29
|
if (p.file) {
|
|
38
|
-
|
|
30
|
+
meta.push(p.file);
|
|
39
31
|
} else if (p.files?.length) {
|
|
40
|
-
|
|
32
|
+
meta.push(`${p.files.length} file(s)`);
|
|
41
33
|
}
|
|
42
|
-
|
|
34
|
+
const text = renderStatusLine({ icon: "pending", title: "LSP", description: p.action || "?", meta }, theme);
|
|
43
35
|
return new Text(text, 0, 0);
|
|
44
36
|
}
|
|
45
37
|
|
|
@@ -55,40 +47,74 @@ export function renderResult(
|
|
|
55
47
|
result: AgentToolResult<LspToolDetails>,
|
|
56
48
|
options: RenderResultOptions,
|
|
57
49
|
theme: Theme,
|
|
58
|
-
|
|
50
|
+
args?: LspParams & { file?: string; files?: string[] },
|
|
51
|
+
): Component {
|
|
59
52
|
const content = result.content?.[0];
|
|
60
53
|
if (!content || content.type !== "text" || !("text" in content) || !content.text) {
|
|
61
|
-
|
|
54
|
+
const header = renderStatusLine({ icon: "warning", title: "LSP", description: "No result" }, theme);
|
|
55
|
+
return new Text([header, theme.fg("dim", "No result")].join("\n"), 0, 0);
|
|
62
56
|
}
|
|
63
57
|
|
|
64
58
|
const text = content.text;
|
|
65
|
-
const lines = text.split("\n")
|
|
59
|
+
const lines = text.split("\n");
|
|
66
60
|
const expanded = options.expanded;
|
|
67
61
|
|
|
68
|
-
|
|
62
|
+
let label = "Result";
|
|
63
|
+
let state: "success" | "warning" | "error" = "success";
|
|
64
|
+
let bodyLines: string[] = [];
|
|
65
|
+
|
|
69
66
|
const codeBlockMatch = text.match(/```(\w*)\n([\s\S]*?)```/);
|
|
70
67
|
if (codeBlockMatch) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
68
|
+
label = "Hover";
|
|
69
|
+
bodyLines = renderHover(codeBlockMatch, text, lines, expanded, theme);
|
|
70
|
+
} else {
|
|
71
|
+
const errorMatch = text.match(/(\d+)\s+error\(s\)/);
|
|
72
|
+
const warningMatch = text.match(/(\d+)\s+warning\(s\)/);
|
|
73
|
+
if (errorMatch || warningMatch || text.includes(theme.status.error)) {
|
|
74
|
+
label = "Diagnostics";
|
|
75
|
+
const errorCount = errorMatch ? Number.parseInt(errorMatch[1], 10) : 0;
|
|
76
|
+
const warnCount = warningMatch ? Number.parseInt(warningMatch[1], 10) : 0;
|
|
77
|
+
state = errorCount > 0 ? "error" : warnCount > 0 ? "warning" : "success";
|
|
78
|
+
bodyLines = renderDiagnostics(errorMatch, warningMatch, lines, expanded, theme);
|
|
79
|
+
} else {
|
|
80
|
+
const refMatch = text.match(/(\d+)\s+reference\(s\)/);
|
|
81
|
+
if (refMatch) {
|
|
82
|
+
label = "References";
|
|
83
|
+
bodyLines = renderReferences(refMatch, lines, expanded, theme);
|
|
84
|
+
} else {
|
|
85
|
+
const symbolsMatch = text.match(/Symbols in (.+):/);
|
|
86
|
+
if (symbolsMatch) {
|
|
87
|
+
label = "Symbols";
|
|
88
|
+
bodyLines = renderSymbols(symbolsMatch, lines, expanded, theme);
|
|
89
|
+
} else {
|
|
90
|
+
label = "Response";
|
|
91
|
+
bodyLines = renderGeneric(text, lines, expanded, theme);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
83
95
|
}
|
|
84
96
|
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
|
|
97
|
+
const meta: string[] = [];
|
|
98
|
+
if (args?.action) meta.push(args.action);
|
|
99
|
+
if (args?.file) {
|
|
100
|
+
meta.push(args.file);
|
|
101
|
+
} else if (args?.files?.length) {
|
|
102
|
+
meta.push(`${args.files.length} file(s)`);
|
|
88
103
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
const header = renderStatusLine({ icon: state, title: "LSP", description: label, meta }, theme);
|
|
105
|
+
return {
|
|
106
|
+
render: (width: number) =>
|
|
107
|
+
renderOutputBlock(
|
|
108
|
+
{
|
|
109
|
+
header,
|
|
110
|
+
state,
|
|
111
|
+
sections: [{ label: theme.fg("toolTitle", label), lines: bodyLines }],
|
|
112
|
+
width,
|
|
113
|
+
},
|
|
114
|
+
theme,
|
|
115
|
+
),
|
|
116
|
+
invalidate: () => {},
|
|
117
|
+
};
|
|
92
118
|
}
|
|
93
119
|
|
|
94
120
|
// =============================================================================
|
|
@@ -104,9 +130,11 @@ function renderHover(
|
|
|
104
130
|
_lines: string[],
|
|
105
131
|
expanded: boolean,
|
|
106
132
|
theme: Theme,
|
|
107
|
-
):
|
|
133
|
+
): string[] {
|
|
108
134
|
const lang = codeBlockMatch[1] || "";
|
|
109
135
|
const code = codeBlockMatch[2].trim();
|
|
136
|
+
const codeStart = codeBlockMatch.index ?? 0;
|
|
137
|
+
const beforeCode = fullText.slice(0, codeStart).trimEnd();
|
|
110
138
|
const afterCode = fullText.slice(fullText.indexOf("```", 3) + 3).trim();
|
|
111
139
|
|
|
112
140
|
const codeLines = highlightCode(code, lang, theme);
|
|
@@ -119,6 +147,11 @@ function renderHover(
|
|
|
119
147
|
const top = `${theme.boxSharp.topLeft}${h.repeat(3)}`;
|
|
120
148
|
const bottom = `${theme.boxSharp.bottomLeft}${h.repeat(3)}`;
|
|
121
149
|
let output = `${icon}${langLabel}`;
|
|
150
|
+
if (beforeCode) {
|
|
151
|
+
for (const line of beforeCode.split("\n")) {
|
|
152
|
+
output += `\n ${theme.fg("muted", line)}`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
122
155
|
output += `\n ${theme.fg("mdCodeBlockBorder", top)}`;
|
|
123
156
|
for (const line of codeLines) {
|
|
124
157
|
output += `\n ${theme.fg("mdCodeBlockBorder", v)} ${line}`;
|
|
@@ -127,15 +160,19 @@ function renderHover(
|
|
|
127
160
|
if (afterCode) {
|
|
128
161
|
output += `\n ${theme.fg("muted", afterCode)}`;
|
|
129
162
|
}
|
|
130
|
-
return
|
|
163
|
+
return output.split("\n");
|
|
131
164
|
}
|
|
132
165
|
|
|
133
166
|
// Collapsed view
|
|
134
167
|
const firstCodeLine = codeLines[0] || "";
|
|
135
|
-
const hasMore = codeLines.length > 1 || Boolean(afterCode);
|
|
168
|
+
const hasMore = codeLines.length > 1 || Boolean(afterCode) || Boolean(beforeCode);
|
|
136
169
|
const expandHint = formatExpandHint(theme, expanded, hasMore);
|
|
137
170
|
|
|
138
171
|
let output = `${icon}${langLabel}${expandHint}`;
|
|
172
|
+
if (beforeCode) {
|
|
173
|
+
const preview = truncate(beforeCode, TRUNCATE_LENGTHS.TITLE, theme.format.ellipsis);
|
|
174
|
+
output += `\n ${theme.fg("dim", theme.tree.branch)} ${theme.fg("muted", preview)}`;
|
|
175
|
+
}
|
|
139
176
|
const h = theme.boxSharp.horizontal;
|
|
140
177
|
const v = theme.boxSharp.vertical;
|
|
141
178
|
const bottom = `${theme.boxSharp.bottomLeft}${h.repeat(3)}`;
|
|
@@ -155,7 +192,7 @@ function renderHover(
|
|
|
155
192
|
output += `\n ${theme.fg("mdCodeBlockBorder", bottom)}`;
|
|
156
193
|
}
|
|
157
194
|
|
|
158
|
-
return
|
|
195
|
+
return output.split("\n");
|
|
159
196
|
}
|
|
160
197
|
|
|
161
198
|
/**
|
|
@@ -206,7 +243,7 @@ function renderDiagnostics(
|
|
|
206
243
|
lines: string[],
|
|
207
244
|
expanded: boolean,
|
|
208
245
|
theme: Theme,
|
|
209
|
-
):
|
|
246
|
+
): string[] {
|
|
210
247
|
const errorCount = errorMatch ? Number.parseInt(errorMatch[1], 10) : 0;
|
|
211
248
|
const warnCount = warningMatch ? Number.parseInt(warningMatch[1], 10) : 0;
|
|
212
249
|
|
|
@@ -222,11 +259,11 @@ function renderDiagnostics(
|
|
|
222
259
|
if (warnCount > 0) meta.push(`${warnCount} warning${warnCount !== 1 ? "s" : ""}`);
|
|
223
260
|
if (meta.length === 0) meta.push("No issues");
|
|
224
261
|
|
|
225
|
-
const diagLines = lines.filter(
|
|
262
|
+
const diagLines = lines.filter(l => l.includes(theme.status.error) || /:\d+:\d+/.test(l));
|
|
226
263
|
const parsedDiagnostics = diagLines
|
|
227
|
-
.map(
|
|
264
|
+
.map(line => parseDiagnosticLine(line))
|
|
228
265
|
.filter((diag): diag is ParsedDiagnostic => diag !== null);
|
|
229
|
-
const fallbackDiagnostics: RawDiagnostic[] = diagLines.map(
|
|
266
|
+
const fallbackDiagnostics: RawDiagnostic[] = diagLines.map(line => ({ raw: line.trim() }));
|
|
230
267
|
|
|
231
268
|
if (expanded) {
|
|
232
269
|
let output = `${icon} ${theme.fg("dim", meta.join(theme.sep.dot))}`;
|
|
@@ -253,7 +290,7 @@ function renderDiagnostics(
|
|
|
253
290
|
)}`;
|
|
254
291
|
}
|
|
255
292
|
}
|
|
256
|
-
return
|
|
293
|
+
return output.split("\n");
|
|
257
294
|
}
|
|
258
295
|
|
|
259
296
|
// Collapsed view
|
|
@@ -285,7 +322,7 @@ function renderDiagnostics(
|
|
|
285
322
|
)}`;
|
|
286
323
|
}
|
|
287
324
|
|
|
288
|
-
return
|
|
325
|
+
return output.split("\n");
|
|
289
326
|
}
|
|
290
327
|
|
|
291
328
|
// =============================================================================
|
|
@@ -295,12 +332,12 @@ function renderDiagnostics(
|
|
|
295
332
|
/**
|
|
296
333
|
* Render references grouped by file.
|
|
297
334
|
*/
|
|
298
|
-
function renderReferences(refMatch: RegExpMatchArray, lines: string[], expanded: boolean, theme: Theme):
|
|
335
|
+
function renderReferences(refMatch: RegExpMatchArray, lines: string[], expanded: boolean, theme: Theme): string[] {
|
|
299
336
|
const refCount = Number.parseInt(refMatch[1], 10);
|
|
300
337
|
const icon =
|
|
301
338
|
refCount > 0 ? theme.styledSymbol("status.success", "success") : theme.styledSymbol("status.warning", "warning");
|
|
302
339
|
|
|
303
|
-
const locLines = lines.filter(
|
|
340
|
+
const locLines = lines.filter(l => /^\s*\S+:\d+:\d+/.test(l));
|
|
304
341
|
|
|
305
342
|
// Group by file
|
|
306
343
|
const byFile = new Map<string, Array<[string, string]>>();
|
|
@@ -369,10 +406,10 @@ function renderReferences(refMatch: RegExpMatchArray, lines: string[], expanded:
|
|
|
369
406
|
};
|
|
370
407
|
|
|
371
408
|
if (expanded) {
|
|
372
|
-
return
|
|
409
|
+
return renderGrouped(files.length, 3, false).split("\n");
|
|
373
410
|
}
|
|
374
411
|
|
|
375
|
-
return
|
|
412
|
+
return renderGrouped(3, 1, true).split("\n");
|
|
376
413
|
}
|
|
377
414
|
|
|
378
415
|
// =============================================================================
|
|
@@ -382,7 +419,7 @@ function renderReferences(refMatch: RegExpMatchArray, lines: string[], expanded:
|
|
|
382
419
|
/**
|
|
383
420
|
* Render document symbols in a hierarchical tree.
|
|
384
421
|
*/
|
|
385
|
-
function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded: boolean, theme: Theme):
|
|
422
|
+
function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded: boolean, theme: Theme): string[] {
|
|
386
423
|
const fileName = symbolsMatch[1];
|
|
387
424
|
const icon = theme.styledSymbol("status.info", "accent");
|
|
388
425
|
|
|
@@ -393,7 +430,7 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded
|
|
|
393
430
|
icon: string;
|
|
394
431
|
}
|
|
395
432
|
|
|
396
|
-
const symbolLines = lines.filter(
|
|
433
|
+
const symbolLines = lines.filter(l => l.includes("@") && l.includes("line"));
|
|
397
434
|
const symbols: SymbolInfo[] = [];
|
|
398
435
|
|
|
399
436
|
for (const line of symbolLines) {
|
|
@@ -436,7 +473,7 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded
|
|
|
436
473
|
return prefix;
|
|
437
474
|
};
|
|
438
475
|
|
|
439
|
-
const topLevelCount = symbols.filter(
|
|
476
|
+
const topLevelCount = symbols.filter(s => s.indent === 0).length;
|
|
440
477
|
|
|
441
478
|
if (expanded) {
|
|
442
479
|
let output = `${icon} ${theme.fg("dim", `in ${fileName}`)}`;
|
|
@@ -450,11 +487,11 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded
|
|
|
450
487
|
output += `\n${prefix}${theme.fg("dim", branch)} ${theme.fg("accent", sym.icon)} ${theme.fg("accent", sym.name)}`;
|
|
451
488
|
output += `\n${prefix}${theme.fg("dim", detailPrefix)}${theme.fg("muted", `line ${sym.line}`)}`;
|
|
452
489
|
}
|
|
453
|
-
return
|
|
490
|
+
return output.split("\n");
|
|
454
491
|
}
|
|
455
492
|
|
|
456
493
|
// Collapsed: show first 3 top-level symbols
|
|
457
|
-
const topLevel = symbols.filter(
|
|
494
|
+
const topLevel = symbols.filter(s => s.indent === 0).slice(0, 3);
|
|
458
495
|
const hasMoreSymbols = symbols.length > topLevel.length;
|
|
459
496
|
const expandHint = formatExpandHint(theme, expanded, hasMoreSymbols);
|
|
460
497
|
let output = `${icon} ${theme.fg("dim", `in ${fileName}`)}${expandHint}`;
|
|
@@ -474,7 +511,7 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded
|
|
|
474
511
|
)}`;
|
|
475
512
|
}
|
|
476
513
|
|
|
477
|
-
return
|
|
514
|
+
return output.split("\n");
|
|
478
515
|
}
|
|
479
516
|
|
|
480
517
|
// =============================================================================
|
|
@@ -484,7 +521,7 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded
|
|
|
484
521
|
/**
|
|
485
522
|
* Generic fallback rendering for unknown result types.
|
|
486
523
|
*/
|
|
487
|
-
function renderGeneric(text: string, lines: string[], expanded: boolean, theme: Theme):
|
|
524
|
+
function renderGeneric(text: string, lines: string[], expanded: boolean, theme: Theme): string[] {
|
|
488
525
|
const hasError = text.includes("Error:") || text.includes(theme.status.error);
|
|
489
526
|
const hasSuccess = text.includes(theme.status.success) || text.includes("Applied");
|
|
490
527
|
|
|
@@ -502,7 +539,7 @@ function renderGeneric(text: string, lines: string[], expanded: boolean, theme:
|
|
|
502
539
|
const branch = isLast ? theme.tree.last : theme.tree.branch;
|
|
503
540
|
output += `\n ${theme.fg("dim", branch)} ${lines[i]}`;
|
|
504
541
|
}
|
|
505
|
-
return
|
|
542
|
+
return output.split("\n");
|
|
506
543
|
}
|
|
507
544
|
|
|
508
545
|
const firstLine = lines[0] || "No output";
|
|
@@ -530,7 +567,7 @@ function renderGeneric(text: string, lines: string[], expanded: boolean, theme:
|
|
|
530
567
|
}
|
|
531
568
|
}
|
|
532
569
|
|
|
533
|
-
return
|
|
570
|
+
return output.split("\n");
|
|
534
571
|
}
|
|
535
572
|
|
|
536
573
|
// =============================================================================
|
|
@@ -574,4 +611,5 @@ function severityToColor(severity: string): "error" | "warning" | "accent" | "di
|
|
|
574
611
|
export const lspToolRenderer = {
|
|
575
612
|
renderCall,
|
|
576
613
|
renderResult,
|
|
614
|
+
mergeCallAndResult: true,
|
|
577
615
|
};
|