@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/tools/fetch.ts
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
4
|
-
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
5
|
-
import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
|
|
6
|
-
import { type Theme, theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
7
|
-
import fetchDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/fetch.md" with { type: "text" };
|
|
8
|
-
import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
|
9
|
-
import { ToolAbortError } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
|
10
|
-
import { ensureTool } from "@oh-my-pi/pi-coding-agent/utils/tools-manager";
|
|
11
|
-
import { specialHandlers } from "@oh-my-pi/pi-coding-agent/web/scrapers/index";
|
|
12
|
-
import type { RenderResult } from "@oh-my-pi/pi-coding-agent/web/scrapers/types";
|
|
13
|
-
import { finalizeOutput, loadPage, MAX_OUTPUT_CHARS } from "@oh-my-pi/pi-coding-agent/web/scrapers/types";
|
|
14
|
-
import { convertWithMarkitdown, fetchBinary } from "@oh-my-pi/pi-coding-agent/web/scrapers/utils";
|
|
15
4
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
16
5
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
17
6
|
import { ptree } from "@oh-my-pi/pi-utils";
|
|
18
7
|
import { type Static, Type } from "@sinclair/typebox";
|
|
19
8
|
import { nanoid } from "nanoid";
|
|
20
9
|
import { parse as parseHtml } from "node-html-parser";
|
|
21
|
-
import
|
|
10
|
+
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
11
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
|
+
import { type Theme, theme } from "../modes/theme/theme";
|
|
13
|
+
import fetchDescription from "../prompts/tools/fetch.md" with { type: "text" };
|
|
14
|
+
import { renderOutputBlock, renderStatusLine } from "../tui";
|
|
15
|
+
import { ensureTool } from "../utils/tools-manager";
|
|
16
|
+
import { specialHandlers } from "../web/scrapers";
|
|
17
|
+
import type { RenderResult } from "../web/scrapers/types";
|
|
18
|
+
import { finalizeOutput, loadPage, MAX_OUTPUT_CHARS } from "../web/scrapers/types";
|
|
19
|
+
import { convertWithMarkitdown, fetchBinary } from "../web/scrapers/utils";
|
|
20
|
+
import type { ToolSession } from ".";
|
|
22
21
|
import { applyListLimit } from "./list-limit";
|
|
22
|
+
import type { OutputMeta } from "./output-meta";
|
|
23
23
|
import { formatExpandHint } from "./render-utils";
|
|
24
|
+
import { ToolAbortError } from "./tool-errors";
|
|
24
25
|
import { toolResult } from "./tool-result";
|
|
25
26
|
|
|
26
27
|
// =============================================================================
|
|
@@ -449,7 +450,6 @@ async function renderHtmlToText(
|
|
|
449
450
|
timeout: number,
|
|
450
451
|
scratchDir: string,
|
|
451
452
|
): Promise<{ content: string; ok: boolean; method: string }> {
|
|
452
|
-
await mkdir(scratchDir, { recursive: true });
|
|
453
453
|
const tmpFile = path.join(scratchDir, `omp-${nanoid()}.html`);
|
|
454
454
|
|
|
455
455
|
try {
|
|
@@ -478,7 +478,7 @@ async function renderHtmlToText(
|
|
|
478
478
|
return { content: "", ok: false, method: "none" };
|
|
479
479
|
} finally {
|
|
480
480
|
try {
|
|
481
|
-
await rm(tmpFile, { force: true });
|
|
481
|
+
await fs.rm(tmpFile, { force: true });
|
|
482
482
|
} catch {}
|
|
483
483
|
}
|
|
484
484
|
}
|
|
@@ -497,13 +497,13 @@ function isLowQualityOutput(content: string): boolean {
|
|
|
497
497
|
"please enable javascript",
|
|
498
498
|
"browser not supported",
|
|
499
499
|
];
|
|
500
|
-
if (content.length < 1024 && jsGated.some(
|
|
500
|
+
if (content.length < 1024 && jsGated.some(t => lower.includes(t))) {
|
|
501
501
|
return true;
|
|
502
502
|
}
|
|
503
503
|
|
|
504
504
|
// Mostly navigation (high link/menu density)
|
|
505
|
-
const lines = content.split("\n").filter(
|
|
506
|
-
const shortLines = lines.filter(
|
|
505
|
+
const lines = content.split("\n").filter(l => l.trim());
|
|
506
|
+
const shortLines = lines.filter(l => l.trim().length < 40);
|
|
507
507
|
if (lines.length > 10 && shortLines.length / lines.length > 0.7) {
|
|
508
508
|
return true;
|
|
509
509
|
}
|
|
@@ -694,7 +694,7 @@ async function renderUrl(
|
|
|
694
694
|
if (isHtml && !raw) {
|
|
695
695
|
// 5A: Check for page-specific markdown alternate
|
|
696
696
|
const alternates = parseAlternateLinks(rawContent, finalUrl);
|
|
697
|
-
const markdownAlt = alternates.find(
|
|
697
|
+
const markdownAlt = alternates.find(alt => alt.endsWith(".md") || alt.includes("markdown"));
|
|
698
698
|
if (markdownAlt) {
|
|
699
699
|
const resolved = markdownAlt.startsWith("http") ? markdownAlt : new URL(markdownAlt, finalUrl).href;
|
|
700
700
|
const altResult = await loadPage(resolved, { timeout, signal });
|
|
@@ -766,7 +766,7 @@ async function renderUrl(
|
|
|
766
766
|
}
|
|
767
767
|
|
|
768
768
|
// 5E: Check for feed alternates
|
|
769
|
-
const feedAlternates = alternates.filter(
|
|
769
|
+
const feedAlternates = alternates.filter(alt => !alt.endsWith(".md") && !alt.includes("markdown"));
|
|
770
770
|
for (const altUrl of feedAlternates.slice(0, 2)) {
|
|
771
771
|
const resolved = altUrl.startsWith("http") ? altUrl : new URL(altUrl, finalUrl).href;
|
|
772
772
|
const altResult = await loadPage(resolved, { timeout, signal });
|
|
@@ -983,7 +983,7 @@ function getDomain(url: string): string {
|
|
|
983
983
|
|
|
984
984
|
/** Count non-empty lines */
|
|
985
985
|
function countNonEmptyLines(text: string): number {
|
|
986
|
-
return text.split("\n").filter(
|
|
986
|
+
return text.split("\n").filter(l => l.trim()).length;
|
|
987
987
|
}
|
|
988
988
|
|
|
989
989
|
/** Render fetch call (URL preview) */
|
|
@@ -993,8 +993,11 @@ export function renderFetchCall(
|
|
|
993
993
|
): Component {
|
|
994
994
|
const domain = getDomain(args.url);
|
|
995
995
|
const path = truncate(args.url.replace(/^https?:\/\/[^/]+/, ""), 50, uiTheme.format.ellipsis);
|
|
996
|
-
const
|
|
997
|
-
const
|
|
996
|
+
const description = `${domain}${path ? ` ${path}` : ""}`.trim();
|
|
997
|
+
const meta: string[] = [];
|
|
998
|
+
if (args.raw) meta.push("raw");
|
|
999
|
+
if (args.timeout !== undefined) meta.push(`timeout:${args.timeout}s`);
|
|
1000
|
+
const text = renderStatusLine({ icon: "pending", title: "Fetch", description, meta }, uiTheme);
|
|
998
1001
|
return new Text(text, 0, 0);
|
|
999
1002
|
}
|
|
1000
1003
|
|
|
@@ -1012,125 +1015,82 @@ export function renderFetchResult(
|
|
|
1012
1015
|
}
|
|
1013
1016
|
|
|
1014
1017
|
const domain = getDomain(details.finalUrl);
|
|
1018
|
+
const path = truncate(details.finalUrl.replace(/^https?:\/\/[^/]+/, ""), 50, uiTheme.format.ellipsis);
|
|
1015
1019
|
const hasRedirect = details.url !== details.finalUrl;
|
|
1016
1020
|
const hasNotes = details.notes.length > 0;
|
|
1017
1021
|
const truncation = details.meta?.truncation;
|
|
1018
1022
|
const truncated = Boolean(details.truncated || truncation);
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1023
|
+
|
|
1024
|
+
const header = renderStatusLine(
|
|
1025
|
+
{
|
|
1026
|
+
icon: truncated ? "warning" : "success",
|
|
1027
|
+
title: "Fetch",
|
|
1028
|
+
description: `${domain}${path ? ` ${path}` : ""}`,
|
|
1029
|
+
},
|
|
1030
|
+
uiTheme,
|
|
1031
|
+
);
|
|
1032
|
+
|
|
1027
1033
|
const contentText = result.content[0]?.text ?? "";
|
|
1028
|
-
// Extract just the content part (after the --- separator)
|
|
1029
1034
|
const contentBody = contentText.includes("---\n\n")
|
|
1030
1035
|
? contentText.split("---\n\n").slice(1).join("---\n\n")
|
|
1031
1036
|
: contentText;
|
|
1032
1037
|
const lineCount = countNonEmptyLines(contentBody);
|
|
1033
1038
|
const charCount = contentBody.trim().length;
|
|
1034
|
-
const contentLines = contentBody.split("\n").filter(
|
|
1035
|
-
|
|
1036
|
-
if (!expanded) {
|
|
1037
|
-
// Collapsed view: metadata + preview
|
|
1038
|
-
const metaLines: string[] = [
|
|
1039
|
-
`${uiTheme.fg("muted", "Content-Type:")} ${details.contentType || "unknown"}`,
|
|
1040
|
-
`${uiTheme.fg("muted", "Method:")} ${details.method}`,
|
|
1041
|
-
];
|
|
1042
|
-
if (hasRedirect) {
|
|
1043
|
-
metaLines.push(`${uiTheme.fg("muted", "Final URL:")} ${uiTheme.fg("mdLinkUrl", details.finalUrl)}`);
|
|
1044
|
-
}
|
|
1045
|
-
if (truncated) {
|
|
1046
|
-
metaLines.push(uiTheme.fg("warning", `${uiTheme.status.warning} Output truncated`));
|
|
1047
|
-
if (truncation?.artifactId) {
|
|
1048
|
-
metaLines.push(uiTheme.fg("warning", `Full output: artifact://${truncation.artifactId}`));
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
if (hasNotes) {
|
|
1052
|
-
metaLines.push(`${uiTheme.fg("muted", "Notes:")} ${details.notes.join("; ")}`);
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
const previewLimit = 3;
|
|
1056
|
-
const previewList = applyListLimit(contentLines, { headLimit: previewLimit });
|
|
1057
|
-
const previewLines = previewList.items.map((line) => truncate(line.trim(), 100, uiTheme.format.ellipsis));
|
|
1058
|
-
const detailLines: string[] = [...metaLines];
|
|
1059
|
-
|
|
1060
|
-
if (previewLines.length === 0) {
|
|
1061
|
-
detailLines.push(uiTheme.fg("dim", "(no content)"));
|
|
1062
|
-
} else {
|
|
1063
|
-
for (const line of previewLines) {
|
|
1064
|
-
detailLines.push(uiTheme.fg("dim", line));
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const remaining = Math.max(0, contentLines.length - previewLines.length);
|
|
1069
|
-
if (remaining > 0) {
|
|
1070
|
-
detailLines.push(uiTheme.fg("muted", `${uiTheme.format.ellipsis} ${remaining} more lines`));
|
|
1071
|
-
} else {
|
|
1072
|
-
const lineLabel = `${lineCount} line${lineCount === 1 ? "" : "s"}`;
|
|
1073
|
-
detailLines.push(uiTheme.fg("muted", `${lineLabel}${uiTheme.sep.dot}${charCount} chars`));
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
for (let i = 0; i < detailLines.length; i++) {
|
|
1077
|
-
const isLast = i === detailLines.length - 1;
|
|
1078
|
-
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.vertical;
|
|
1079
|
-
text += `\n ${uiTheme.fg("dim", branch)} ${detailLines[i]}`;
|
|
1080
|
-
}
|
|
1081
|
-
} else {
|
|
1082
|
-
// Expanded view: structured metadata + bounded content preview
|
|
1083
|
-
const metaLines: string[] = [
|
|
1084
|
-
`${uiTheme.fg("muted", "Content-Type:")} ${details.contentType || "unknown"}`,
|
|
1085
|
-
`${uiTheme.fg("muted", "Method:")} ${details.method}`,
|
|
1086
|
-
];
|
|
1087
|
-
if (hasRedirect) {
|
|
1088
|
-
metaLines.push(`${uiTheme.fg("muted", "Final URL:")} ${uiTheme.fg("mdLinkUrl", details.finalUrl)}`);
|
|
1089
|
-
}
|
|
1090
|
-
const lineLabel = `${lineCount} line${lineCount === 1 ? "" : "s"}`;
|
|
1091
|
-
metaLines.push(`${uiTheme.fg("muted", "Lines:")} ${lineLabel}`);
|
|
1092
|
-
metaLines.push(`${uiTheme.fg("muted", "Chars:")} ${charCount}`);
|
|
1093
|
-
if (truncated) {
|
|
1094
|
-
metaLines.push(uiTheme.fg("warning", `${uiTheme.status.warning} Output truncated`));
|
|
1095
|
-
if (truncation?.artifactId) {
|
|
1096
|
-
metaLines.push(uiTheme.fg("warning", `Full output: artifact://${truncation.artifactId}`));
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
if (hasNotes) {
|
|
1100
|
-
metaLines.push(`${uiTheme.fg("muted", "Notes:")} ${details.notes.join("; ")}`);
|
|
1101
|
-
}
|
|
1039
|
+
const contentLines = contentBody.split("\n").filter(l => l.trim());
|
|
1102
1040
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
if (previewLines.length === 0) {
|
|
1118
|
-
text += `\n ${contentPrefix} ${uiTheme.fg("dim", "(no content)")}`;
|
|
1119
|
-
} else {
|
|
1120
|
-
for (const line of previewLines) {
|
|
1121
|
-
text += `\n ${contentPrefix} ${uiTheme.fg("dim", line)}`;
|
|
1122
|
-
}
|
|
1041
|
+
const metadataLines: string[] = [
|
|
1042
|
+
`${uiTheme.fg("muted", "Content-Type:")} ${details.contentType || "unknown"}`,
|
|
1043
|
+
`${uiTheme.fg("muted", "Method:")} ${details.method}`,
|
|
1044
|
+
];
|
|
1045
|
+
if (hasRedirect) {
|
|
1046
|
+
metadataLines.push(`${uiTheme.fg("muted", "Final URL:")} ${uiTheme.fg("mdLinkUrl", details.finalUrl)}`);
|
|
1047
|
+
}
|
|
1048
|
+
const lineLabel = `${lineCount} line${lineCount === 1 ? "" : "s"}`;
|
|
1049
|
+
metadataLines.push(`${uiTheme.fg("muted", "Lines:")} ${lineLabel}`);
|
|
1050
|
+
metadataLines.push(`${uiTheme.fg("muted", "Chars:")} ${charCount}`);
|
|
1051
|
+
if (truncated) {
|
|
1052
|
+
metadataLines.push(uiTheme.fg("warning", `${uiTheme.status.warning} Output truncated`));
|
|
1053
|
+
if (truncation?.artifactId) {
|
|
1054
|
+
metadataLines.push(uiTheme.fg("warning", `Full output: artifact://${truncation.artifactId}`));
|
|
1123
1055
|
}
|
|
1056
|
+
}
|
|
1057
|
+
if (hasNotes) {
|
|
1058
|
+
metadataLines.push(`${uiTheme.fg("muted", "Notes:")} ${details.notes.join("; ")}`);
|
|
1059
|
+
}
|
|
1124
1060
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1061
|
+
const previewLimit = expanded ? 12 : 3;
|
|
1062
|
+
const previewList = applyListLimit(contentLines, { headLimit: previewLimit });
|
|
1063
|
+
const previewLines = previewList.items.map(line => truncate(line.trimEnd(), 120, uiTheme.format.ellipsis));
|
|
1064
|
+
const remaining = Math.max(0, contentLines.length - previewLines.length);
|
|
1065
|
+
const contentPreviewLines =
|
|
1066
|
+
previewLines.length > 0 ? previewLines.map(line => uiTheme.fg("dim", line)) : [uiTheme.fg("dim", "(no content)")];
|
|
1067
|
+
if (remaining > 0) {
|
|
1068
|
+
const hint = formatExpandHint(uiTheme, expanded, true);
|
|
1069
|
+
contentPreviewLines.push(
|
|
1070
|
+
uiTheme.fg("muted", `${uiTheme.format.ellipsis} ${remaining} more lines${hint ? ` ${hint}` : ""}`),
|
|
1071
|
+
);
|
|
1128
1072
|
}
|
|
1129
1073
|
|
|
1130
|
-
return
|
|
1074
|
+
return {
|
|
1075
|
+
render: (width: number) =>
|
|
1076
|
+
renderOutputBlock(
|
|
1077
|
+
{
|
|
1078
|
+
header,
|
|
1079
|
+
state: truncated ? "warning" : "success",
|
|
1080
|
+
sections: [
|
|
1081
|
+
{ label: uiTheme.fg("toolTitle", "Metadata"), lines: metadataLines },
|
|
1082
|
+
{ label: uiTheme.fg("toolTitle", "Content Preview"), lines: contentPreviewLines },
|
|
1083
|
+
],
|
|
1084
|
+
width,
|
|
1085
|
+
},
|
|
1086
|
+
uiTheme,
|
|
1087
|
+
),
|
|
1088
|
+
invalidate: () => {},
|
|
1089
|
+
};
|
|
1131
1090
|
}
|
|
1132
1091
|
|
|
1133
1092
|
export const fetchToolRenderer = {
|
|
1134
1093
|
renderCall: renderFetchCall,
|
|
1135
1094
|
renderResult: renderFetchResult,
|
|
1095
|
+
mergeCallAndResult: true,
|
|
1136
1096
|
};
|
package/src/tools/find.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
3
3
|
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
4
|
-
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
5
|
-
import type { RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
|
|
6
|
-
import { getLanguageFromPath, type Theme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
7
|
-
import findDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/find.md" with { type: "text" };
|
|
8
|
-
import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
|
9
|
-
import { ToolAbortError, ToolError, throwIfAborted } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
|
10
|
-
import { ensureTool } from "@oh-my-pi/pi-coding-agent/utils/tools-manager";
|
|
11
4
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
12
5
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
13
6
|
import { ptree, untilAborted } from "@oh-my-pi/pi-utils";
|
|
14
7
|
import type { Static } from "@sinclair/typebox";
|
|
15
8
|
import { Type } from "@sinclair/typebox";
|
|
16
|
-
|
|
17
|
-
import type {
|
|
9
|
+
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
10
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
11
|
+
import type { Theme } from "../modes/theme/theme";
|
|
12
|
+
import findDescription from "../prompts/tools/find.md" with { type: "text" };
|
|
13
|
+
import { renderFileList, renderStatusLine, renderTreeList } from "../tui";
|
|
14
|
+
import { ensureTool } from "../utils/tools-manager";
|
|
15
|
+
import type { ToolSession } from ".";
|
|
18
16
|
import { applyListLimit } from "./list-limit";
|
|
17
|
+
import type { OutputMeta } from "./output-meta";
|
|
19
18
|
import { resolveToCwd } from "./path-utils";
|
|
20
|
-
import {
|
|
19
|
+
import { formatCount, formatEmptyMessage, formatErrorMessage, PREVIEW_LIMITS } from "./render-utils";
|
|
20
|
+
import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
|
|
21
21
|
import { toolResult } from "./tool-result";
|
|
22
22
|
import { type TruncationResult, truncateHead } from "./truncate";
|
|
23
23
|
|
|
@@ -34,6 +34,7 @@ const findSchema = Type.Object({
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
const DEFAULT_LIMIT = 1000;
|
|
37
|
+
const FD_TIMEOUT_MS = 5000;
|
|
37
38
|
|
|
38
39
|
export interface FindToolDetails {
|
|
39
40
|
truncation?: TruncationResult;
|
|
@@ -132,6 +133,11 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
132
133
|
|
|
133
134
|
return untilAborted(signal, async () => {
|
|
134
135
|
const searchPath = resolveToCwd(searchDir || ".", this.session.cwd);
|
|
136
|
+
|
|
137
|
+
if (searchPath === "/") {
|
|
138
|
+
throw new ToolError("Searching from root directory '/' is not allowed");
|
|
139
|
+
}
|
|
140
|
+
|
|
135
141
|
const scopePath = (() => {
|
|
136
142
|
const relative = path.relative(this.session.cwd, searchPath).replace(/\\/g, "/");
|
|
137
143
|
return relative.length === 0 ? "." : relative;
|
|
@@ -157,7 +163,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
// Relativize paths
|
|
160
|
-
const relativized = results.map(
|
|
166
|
+
const relativized = results.map(p => {
|
|
161
167
|
if (p.startsWith(searchPath)) {
|
|
162
168
|
return p.slice(searchPath.length + 1);
|
|
163
169
|
}
|
|
@@ -246,7 +252,9 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
246
252
|
"--absolute-path",
|
|
247
253
|
searchPath,
|
|
248
254
|
];
|
|
249
|
-
const
|
|
255
|
+
const timeoutSignal = AbortSignal.timeout(FD_TIMEOUT_MS);
|
|
256
|
+
const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
257
|
+
const { stdout: gitignoreStdout } = await runFd(fdPath, gitignoreArgs, combinedSignal);
|
|
250
258
|
for (const rawLine of gitignoreStdout.split("\n")) {
|
|
251
259
|
const file = rawLine.trim();
|
|
252
260
|
if (!file) continue;
|
|
@@ -266,8 +274,10 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
266
274
|
// Pattern and path
|
|
267
275
|
args.push(effectivePattern, searchPath);
|
|
268
276
|
|
|
269
|
-
// Run fd
|
|
270
|
-
const
|
|
277
|
+
// Run fd with timeout
|
|
278
|
+
const mainTimeoutSignal = AbortSignal.timeout(FD_TIMEOUT_MS);
|
|
279
|
+
const mainCombinedSignal = signal ? AbortSignal.any([signal, mainTimeoutSignal]) : mainTimeoutSignal;
|
|
280
|
+
const { stdout, stderr, exitCode } = await runFd(fdPath, args, mainCombinedSignal);
|
|
271
281
|
const output = stdout.trim();
|
|
272
282
|
|
|
273
283
|
// fd exit codes: 0 = found files, 1 = no matches, other = error
|
|
@@ -320,7 +330,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
320
330
|
const indexed = relativized.map((path, idx) => ({ path, mtime: mtimes[idx] }));
|
|
321
331
|
indexed.sort((a, b) => b.mtime - a.mtime);
|
|
322
332
|
relativized.length = 0;
|
|
323
|
-
relativized.push(...indexed.map(
|
|
333
|
+
relativized.push(...indexed.map(item => item.path));
|
|
324
334
|
}
|
|
325
335
|
|
|
326
336
|
const listLimit = applyListLimit(relativized, { limit: effectiveLimit });
|
|
@@ -371,10 +381,6 @@ const COLLAPSED_LIST_LIMIT = PREVIEW_LIMITS.COLLAPSED_ITEMS;
|
|
|
371
381
|
export const findToolRenderer = {
|
|
372
382
|
inline: true,
|
|
373
383
|
renderCall(args: FindRenderArgs, uiTheme: Theme): Component {
|
|
374
|
-
const ui = new ToolUIKit(uiTheme);
|
|
375
|
-
const label = ui.title("Find");
|
|
376
|
-
let text = `${uiTheme.format.bullet} ${label} ${uiTheme.fg("accent", args.pattern || "*")}`;
|
|
377
|
-
|
|
378
384
|
const meta: string[] = [];
|
|
379
385
|
if (args.path) meta.push(`in ${args.path}`);
|
|
380
386
|
if (args.type && args.type !== "all") meta.push(`type:${args.type}`);
|
|
@@ -382,8 +388,10 @@ export const findToolRenderer = {
|
|
|
382
388
|
if (args.sortByMtime) meta.push("sort:mtime");
|
|
383
389
|
if (args.limit !== undefined) meta.push(`limit:${args.limit}`);
|
|
384
390
|
|
|
385
|
-
text
|
|
386
|
-
|
|
391
|
+
const text = renderStatusLine(
|
|
392
|
+
{ icon: "pending", title: "Find", description: args.pattern || "*", meta },
|
|
393
|
+
uiTheme,
|
|
394
|
+
);
|
|
387
395
|
return new Text(text, 0, 0);
|
|
388
396
|
},
|
|
389
397
|
|
|
@@ -391,106 +399,94 @@ export const findToolRenderer = {
|
|
|
391
399
|
result: { content: Array<{ type: string; text?: string }>; details?: FindToolDetails; isError?: boolean },
|
|
392
400
|
{ expanded }: RenderResultOptions,
|
|
393
401
|
uiTheme: Theme,
|
|
402
|
+
args?: FindRenderArgs,
|
|
394
403
|
): Component {
|
|
395
|
-
const ui = new ToolUIKit(uiTheme);
|
|
396
404
|
const details = result.details;
|
|
397
405
|
|
|
398
406
|
if (result.isError || details?.error) {
|
|
399
|
-
const errorText = details?.error || result.content?.find(
|
|
400
|
-
return new Text(
|
|
407
|
+
const errorText = details?.error || result.content?.find(c => c.type === "text")?.text || "Unknown error";
|
|
408
|
+
return new Text(formatErrorMessage(errorText, uiTheme), 0, 0);
|
|
401
409
|
}
|
|
402
410
|
|
|
403
411
|
const hasDetailedData = details?.fileCount !== undefined;
|
|
404
|
-
const textContent = result.content?.find(
|
|
412
|
+
const textContent = result.content?.find(c => c.type === "text")?.text;
|
|
405
413
|
|
|
406
414
|
if (!hasDetailedData) {
|
|
407
|
-
if (
|
|
408
|
-
|
|
415
|
+
if (
|
|
416
|
+
!textContent ||
|
|
417
|
+
textContent.includes("No files matching") ||
|
|
418
|
+
textContent.includes("No files found") ||
|
|
419
|
+
textContent.trim() === ""
|
|
420
|
+
) {
|
|
421
|
+
return new Text(formatEmptyMessage("No files found", uiTheme), 0, 0);
|
|
409
422
|
}
|
|
410
423
|
|
|
411
|
-
const lines = textContent.split("\n").filter(
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
424
|
+
const lines = textContent.split("\n").filter(l => l.trim());
|
|
425
|
+
const header = renderStatusLine(
|
|
426
|
+
{
|
|
427
|
+
icon: "success",
|
|
428
|
+
title: "Find",
|
|
429
|
+
description: args?.pattern,
|
|
430
|
+
meta: [formatCount("file", lines.length)],
|
|
431
|
+
},
|
|
432
|
+
uiTheme,
|
|
433
|
+
);
|
|
434
|
+
const listLines = renderTreeList(
|
|
435
|
+
{
|
|
436
|
+
items: lines,
|
|
437
|
+
expanded,
|
|
438
|
+
maxCollapsed: COLLAPSED_LIST_LIMIT,
|
|
439
|
+
itemType: "file",
|
|
440
|
+
renderItem: line => uiTheme.fg("accent", line),
|
|
441
|
+
},
|
|
442
|
+
uiTheme,
|
|
443
|
+
);
|
|
444
|
+
return new Text([header, ...listLines].join("\n"), 0, 0);
|
|
431
445
|
}
|
|
432
446
|
|
|
433
447
|
const fileCount = details?.fileCount ?? 0;
|
|
434
|
-
const truncation = details?.meta?.truncation;
|
|
448
|
+
const truncation = details?.truncation ?? details?.meta?.truncation;
|
|
435
449
|
const limits = details?.meta?.limits;
|
|
436
|
-
const truncated = Boolean(
|
|
437
|
-
details?.truncated || truncation || limits?.resultLimit || limits?.headLimit || limits?.matchLimit,
|
|
438
|
-
);
|
|
450
|
+
const truncated = Boolean(details?.truncated || truncation || details?.resultLimitReached || limits?.resultLimit);
|
|
439
451
|
const files = details?.files ?? [];
|
|
440
452
|
|
|
441
453
|
if (fileCount === 0) {
|
|
442
|
-
|
|
454
|
+
const header = renderStatusLine(
|
|
455
|
+
{ icon: "warning", title: "Find", description: args?.pattern, meta: ["0 files"] },
|
|
456
|
+
uiTheme,
|
|
457
|
+
);
|
|
458
|
+
return new Text([header, formatEmptyMessage("No files found", uiTheme)].join("\n"), 0, 0);
|
|
443
459
|
}
|
|
460
|
+
const meta: string[] = [formatCount("file", fileCount)];
|
|
461
|
+
if (details?.scopePath) meta.push(`in ${details.scopePath}`);
|
|
462
|
+
if (truncated) meta.push(uiTheme.fg("warning", "truncated"));
|
|
463
|
+
const header = renderStatusLine(
|
|
464
|
+
{ icon: truncated ? "warning" : "success", title: "Find", description: args?.pattern, meta },
|
|
465
|
+
uiTheme,
|
|
466
|
+
);
|
|
444
467
|
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
468
|
+
const fileLines = renderFileList(
|
|
469
|
+
{
|
|
470
|
+
files: files.map(entry => ({ path: entry, isDirectory: entry.endsWith("/") })),
|
|
471
|
+
expanded,
|
|
472
|
+
maxCollapsed: COLLAPSED_LIST_LIMIT,
|
|
473
|
+
},
|
|
474
|
+
uiTheme,
|
|
475
|
+
);
|
|
453
476
|
|
|
454
477
|
const truncationReasons: string[] = [];
|
|
455
|
-
if (
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
478
|
+
if (details?.resultLimitReached) truncationReasons.push(`limit ${details.resultLimitReached} results`);
|
|
479
|
+
if (limits?.resultLimit) truncationReasons.push(`limit ${limits.resultLimit.reached} results`);
|
|
480
|
+
if (truncation) truncationReasons.push(truncation.truncatedBy === "lines" ? "line limit" : "size limit");
|
|
481
|
+
const artifactId = truncation && "artifactId" in truncation ? truncation.artifactId : undefined;
|
|
482
|
+
if (artifactId) truncationReasons.push(`full output: artifact://${artifactId}`);
|
|
483
|
+
|
|
484
|
+
const extraLines: string[] = [];
|
|
485
|
+
if (truncationReasons.length > 0) {
|
|
486
|
+
extraLines.push(uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`));
|
|
463
487
|
}
|
|
464
488
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if (files.length > 0) {
|
|
468
|
-
for (let i = 0; i < maxFiles; i++) {
|
|
469
|
-
const isLast = i === maxFiles - 1 && !hasMoreFiles && !hasTruncation;
|
|
470
|
-
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
471
|
-
const entry = files[i];
|
|
472
|
-
const isDir = entry.endsWith("/");
|
|
473
|
-
const entryPath = isDir ? entry.slice(0, -1) : entry;
|
|
474
|
-
const lang = isDir ? undefined : getLanguageFromPath(entryPath);
|
|
475
|
-
const entryIcon = isDir
|
|
476
|
-
? uiTheme.fg("accent", uiTheme.icon.folder)
|
|
477
|
-
: uiTheme.fg("muted", uiTheme.getLangIcon(lang));
|
|
478
|
-
text += `\n ${uiTheme.fg("dim", branch)} ${entryIcon} ${uiTheme.fg("accent", entry)}`;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (hasMoreFiles) {
|
|
482
|
-
const moreFilesBranch = hasTruncation ? uiTheme.tree.branch : uiTheme.tree.last;
|
|
483
|
-
text += `\n ${uiTheme.fg("dim", moreFilesBranch)} ${uiTheme.fg(
|
|
484
|
-
"muted",
|
|
485
|
-
ui.moreItems(files.length - maxFiles, "file"),
|
|
486
|
-
)}`;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if (hasTruncation) {
|
|
491
|
-
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`)}`;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return new Text(text, 0, 0);
|
|
489
|
+
return new Text([header, ...fileLines, ...extraLines].join("\n"), 0, 0);
|
|
495
490
|
},
|
|
491
|
+
mergeCallAndResult: true,
|
|
496
492
|
};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import * as path from "node:path";
|
|
3
3
|
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
4
|
-
import type { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
|
|
5
|
-
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
6
|
-
import type { CustomTool } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";
|
|
7
|
-
import geminiImageDescription from "@oh-my-pi/pi-coding-agent/prompts/tools/gemini-image.md" with { type: "text" };
|
|
8
|
-
import { detectSupportedImageMimeTypeFromFile } from "@oh-my-pi/pi-coding-agent/utils/mime";
|
|
9
|
-
import { getEnv } from "@oh-my-pi/pi-coding-agent/web/search/auth";
|
|
10
4
|
import { untilAborted } from "@oh-my-pi/pi-utils";
|
|
11
5
|
import { type Static, Type } from "@sinclair/typebox";
|
|
12
6
|
import { nanoid } from "nanoid";
|
|
7
|
+
import type { ModelRegistry } from "../config/model-registry";
|
|
8
|
+
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
9
|
+
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
10
|
+
import geminiImageDescription from "../prompts/tools/gemini-image.md" with { type: "text" };
|
|
11
|
+
import { detectSupportedImageMimeTypeFromFile } from "../utils/mime";
|
|
12
|
+
import { getEnv } from "../web/search/auth";
|
|
13
13
|
import { resolveReadPath } from "./path-utils";
|
|
14
14
|
|
|
15
15
|
const DEFAULT_MODEL = "gemini-3-pro-image-preview";
|
|
@@ -151,7 +151,7 @@ function assemblePrompt(params: GeminiImageParams): string {
|
|
|
151
151
|
if (params.style) parts.push(params.style);
|
|
152
152
|
|
|
153
153
|
// Join with periods for sentence structure
|
|
154
|
-
let prompt = `${parts.map(
|
|
154
|
+
let prompt = `${parts.map(p => p.replace(/[.!,;:]+$/, "")).join(". ")}.`;
|
|
155
155
|
|
|
156
156
|
// Text rendering specs
|
|
157
157
|
if (params.text) {
|
|
@@ -160,7 +160,7 @@ function assemblePrompt(params: GeminiImageParams): string {
|
|
|
160
160
|
|
|
161
161
|
// Edit mode: changes and preserve directives
|
|
162
162
|
if (params.changes?.length) {
|
|
163
|
-
prompt += `\n\nChanges:\n${params.changes.map(
|
|
163
|
+
prompt += `\n\nChanges:\n${params.changes.map(c => `- ${c}`).join("\n")}`;
|
|
164
164
|
if (params.preserve) {
|
|
165
165
|
prompt += `\n\nPreserve: ${params.preserve}`;
|
|
166
166
|
}
|
|
@@ -330,8 +330,8 @@ function collectOpenRouterResponseText(message: OpenRouterMessage | undefined):
|
|
|
330
330
|
}
|
|
331
331
|
if (Array.isArray(message.content)) {
|
|
332
332
|
const texts = message.content
|
|
333
|
-
.filter(
|
|
334
|
-
.map(
|
|
333
|
+
.filter(part => part.type === "text")
|
|
334
|
+
.map(part => part.text)
|
|
335
335
|
.filter((text): text is string => Boolean(text));
|
|
336
336
|
const combined = texts.join("\n").trim();
|
|
337
337
|
return combined.length > 0 ? combined : undefined;
|
|
@@ -489,7 +489,7 @@ function getExtensionForMime(mimeType: string): string {
|
|
|
489
489
|
async function saveImageToTemp(image: InlineImageData): Promise<string> {
|
|
490
490
|
const ext = getExtensionForMime(image.mimeType);
|
|
491
491
|
const filename = `omp-image-${nanoid()}.${ext}`;
|
|
492
|
-
const filepath = join(tmpdir(), filename);
|
|
492
|
+
const filepath = path.join(os.tmpdir(), filename);
|
|
493
493
|
await Bun.write(filepath, Buffer.from(image.data, "base64"));
|
|
494
494
|
return filepath;
|
|
495
495
|
}
|
|
@@ -515,7 +515,7 @@ function buildResponseSummary(
|
|
|
515
515
|
}
|
|
516
516
|
|
|
517
517
|
function collectResponseText(parts: GeminiPart[]): string | undefined {
|
|
518
|
-
const texts = parts.map(
|
|
518
|
+
const texts = parts.map(part => part.text).filter((text): text is string => Boolean(text));
|
|
519
519
|
const combined = texts.join("\n").trim();
|
|
520
520
|
return combined.length > 0 ? combined : undefined;
|
|
521
521
|
}
|