@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
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hierarchical tree list rendering helper.
|
|
3
|
+
*/
|
|
4
|
+
import type { Theme } from "../modes/theme/theme";
|
|
5
|
+
import { formatMoreItems } from "../tools/render-utils";
|
|
6
|
+
import type { TreeContext } from "./types";
|
|
7
|
+
import { getTreeBranch, getTreeContinuePrefix } from "./utils";
|
|
8
|
+
|
|
9
|
+
export interface TreeListOptions<T> {
|
|
10
|
+
items: T[];
|
|
11
|
+
expanded?: boolean;
|
|
12
|
+
maxCollapsed?: number;
|
|
13
|
+
itemType?: string;
|
|
14
|
+
renderItem: (item: T, context: TreeContext) => string | string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function renderTreeList<T>(options: TreeListOptions<T>, theme: Theme): string[] {
|
|
18
|
+
const { items, expanded = false, maxCollapsed = 8, itemType = "item", renderItem } = options;
|
|
19
|
+
const lines: string[] = [];
|
|
20
|
+
const maxItems = expanded ? items.length : Math.min(items.length, maxCollapsed);
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < maxItems; i++) {
|
|
23
|
+
const isLast = i === maxItems - 1 && (expanded || items.length <= maxCollapsed);
|
|
24
|
+
const branch = getTreeBranch(isLast, theme);
|
|
25
|
+
const prefix = `${theme.fg("dim", branch)} `;
|
|
26
|
+
const continuePrefix = `${theme.fg("dim", getTreeContinuePrefix(isLast, theme))}`;
|
|
27
|
+
const context: TreeContext = {
|
|
28
|
+
index: i,
|
|
29
|
+
isLast,
|
|
30
|
+
depth: 0,
|
|
31
|
+
theme,
|
|
32
|
+
prefix,
|
|
33
|
+
continuePrefix,
|
|
34
|
+
};
|
|
35
|
+
const rendered = renderItem(items[i], context);
|
|
36
|
+
if (Array.isArray(rendered)) {
|
|
37
|
+
if (rendered.length === 0) continue;
|
|
38
|
+
lines.push(`${prefix}${rendered[0]}`);
|
|
39
|
+
for (let j = 1; j < rendered.length; j++) {
|
|
40
|
+
lines.push(`${continuePrefix}${rendered[j]}`);
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
lines.push(`${prefix}${rendered}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!expanded && items.length > maxItems) {
|
|
48
|
+
const remaining = items.length - maxItems;
|
|
49
|
+
lines.push(
|
|
50
|
+
`${theme.fg("dim", theme.tree.last)} ${theme.fg("muted", formatMoreItems(remaining, itemType, theme))}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return lines;
|
|
55
|
+
}
|
package/src/tui/types.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for TUI rendering components.
|
|
3
|
+
*/
|
|
4
|
+
import type { Theme } from "../modes/theme/theme";
|
|
5
|
+
|
|
6
|
+
export type State = "pending" | "running" | "success" | "error" | "warning";
|
|
7
|
+
export type IconType = "success" | "error" | "running" | "pending" | "warning" | "info";
|
|
8
|
+
|
|
9
|
+
export interface TreeContext {
|
|
10
|
+
index: number;
|
|
11
|
+
isLast: boolean;
|
|
12
|
+
depth: number;
|
|
13
|
+
theme: Theme;
|
|
14
|
+
prefix: string;
|
|
15
|
+
continuePrefix: string;
|
|
16
|
+
}
|
package/src/tui/utils.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for tool-rendered UI components.
|
|
3
|
+
*/
|
|
4
|
+
import { truncateToWidth as truncateToWidthBase, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
5
|
+
import type { Theme, ThemeBg } from "../modes/theme/theme";
|
|
6
|
+
import type { IconType, State } from "./types";
|
|
7
|
+
|
|
8
|
+
export function buildTreePrefix(ancestors: boolean[], theme: Theme): string {
|
|
9
|
+
return ancestors.map(hasNext => (hasNext ? `${theme.tree.vertical} ` : " ")).join("");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getTreeBranch(isLast: boolean, theme: Theme): string {
|
|
13
|
+
return isLast ? theme.tree.last : theme.tree.branch;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getTreeContinuePrefix(isLast: boolean, theme: Theme): string {
|
|
17
|
+
return isLast ? " " : `${theme.tree.vertical} `;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function truncateToWidth(text: string, width: number, ellipsis: string): string {
|
|
21
|
+
return truncateToWidthBase(text, width, ellipsis);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function padToWidth(text: string, width: number, bgFn?: (s: string) => string): string {
|
|
25
|
+
if (width <= 0) return bgFn ? bgFn(text) : text;
|
|
26
|
+
const paddingNeeded = Math.max(0, width - visibleWidth(text));
|
|
27
|
+
const padded = paddingNeeded > 0 ? text + " ".repeat(paddingNeeded) : text;
|
|
28
|
+
return bgFn ? bgFn(padded) : padded;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getStateBgColor(state: State): ThemeBg {
|
|
32
|
+
if (state === "success") return "toolSuccessBg";
|
|
33
|
+
if (state === "error") return "toolErrorBg";
|
|
34
|
+
return "toolPendingBg";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getStateIcon(icon: IconType, theme: Theme, spinnerFrame?: number): string {
|
|
38
|
+
if (icon === "success") return theme.styledSymbol("status.success", "success");
|
|
39
|
+
if (icon === "error") return theme.styledSymbol("status.error", "error");
|
|
40
|
+
if (icon === "warning") return theme.styledSymbol("status.warning", "warning");
|
|
41
|
+
if (icon === "info") return theme.styledSymbol("status.info", "accent");
|
|
42
|
+
if (icon === "pending") return theme.styledSymbol("status.pending", "accent");
|
|
43
|
+
if (spinnerFrame !== undefined) {
|
|
44
|
+
const frames = theme.spinnerFrames;
|
|
45
|
+
return frames[spinnerFrame % frames.length];
|
|
46
|
+
}
|
|
47
|
+
return theme.styledSymbol("status.running", "accent");
|
|
48
|
+
}
|
package/src/utils/changelog.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
2
2
|
|
|
3
3
|
export interface ChangelogEntry {
|
|
4
4
|
major: number;
|
|
@@ -11,13 +11,9 @@ export interface ChangelogEntry {
|
|
|
11
11
|
* Parse changelog entries from CHANGELOG.md
|
|
12
12
|
* Scans for ## lines and collects content until next ## or EOF
|
|
13
13
|
*/
|
|
14
|
-
export function parseChangelog(changelogPath: string): ChangelogEntry[] {
|
|
15
|
-
if (!existsSync(changelogPath)) {
|
|
16
|
-
return [];
|
|
17
|
-
}
|
|
18
|
-
|
|
14
|
+
export async function parseChangelog(changelogPath: string): Promise<ChangelogEntry[]> {
|
|
19
15
|
try {
|
|
20
|
-
const content =
|
|
16
|
+
const content = await Bun.file(changelogPath).text();
|
|
21
17
|
const lines = content.split("\n");
|
|
22
18
|
const entries: ChangelogEntry[] = [];
|
|
23
19
|
|
|
@@ -65,7 +61,10 @@ export function parseChangelog(changelogPath: string): ChangelogEntry[] {
|
|
|
65
61
|
|
|
66
62
|
return entries;
|
|
67
63
|
} catch (error) {
|
|
68
|
-
|
|
64
|
+
if (isEnoent(error)) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
logger.error(`Warning: Could not parse changelog: ${error}`);
|
|
69
68
|
return [];
|
|
70
69
|
}
|
|
71
70
|
}
|
|
@@ -92,8 +91,8 @@ export function getNewEntries(entries: ChangelogEntry[], lastVersion: string): C
|
|
|
92
91
|
content: "",
|
|
93
92
|
};
|
|
94
93
|
|
|
95
|
-
return entries.filter(
|
|
94
|
+
return entries.filter(entry => compareVersions(entry, last) > 0);
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
// Re-export getChangelogPath from paths.ts for convenience
|
|
99
|
-
export { getChangelogPath } from "
|
|
98
|
+
export { getChangelogPath } from "../config";
|
package/src/utils/clipboard.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as os from "node:os";
|
|
3
3
|
import { $ } from "bun";
|
|
4
4
|
import { nanoid } from "nanoid";
|
|
5
5
|
|
|
@@ -16,23 +16,23 @@ function baseMimeType(mimeType: string): string {
|
|
|
16
16
|
|
|
17
17
|
function selectPreferredImageMimeType(mimeTypes: string[]): string | null {
|
|
18
18
|
const normalized = mimeTypes
|
|
19
|
-
.map(
|
|
19
|
+
.map(t => t.trim())
|
|
20
20
|
.filter(Boolean)
|
|
21
|
-
.map(
|
|
21
|
+
.map(t => ({ raw: t, base: baseMimeType(t) }));
|
|
22
22
|
|
|
23
23
|
for (const preferred of PREFERRED_IMAGE_MIME_TYPES) {
|
|
24
|
-
const match = normalized.find(
|
|
24
|
+
const match = normalized.find(t => t.base === preferred);
|
|
25
25
|
if (match) {
|
|
26
26
|
return match.raw;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const anyImage = normalized.find(
|
|
30
|
+
const anyImage = normalized.find(t => t.base.startsWith("image/"));
|
|
31
31
|
return anyImage?.raw ?? null;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export async function copyToClipboard(text: string): Promise<void> {
|
|
35
|
-
const p = platform();
|
|
35
|
+
const p = os.platform();
|
|
36
36
|
const timeout = 5000;
|
|
37
37
|
|
|
38
38
|
try {
|
|
@@ -83,7 +83,7 @@ export interface ClipboardImage {
|
|
|
83
83
|
* - Windows: uses PowerShell
|
|
84
84
|
*/
|
|
85
85
|
export async function readImageFromClipboard(): Promise<ClipboardImage | null> {
|
|
86
|
-
const p = platform();
|
|
86
|
+
const p = os.platform();
|
|
87
87
|
const timeout = 3000;
|
|
88
88
|
let promise: Promise<ClipboardImage | null>;
|
|
89
89
|
switch (p) {
|
|
@@ -125,7 +125,7 @@ async function readImageWayland(): Promise<ClipboardReadResult> {
|
|
|
125
125
|
|
|
126
126
|
const typeList = types
|
|
127
127
|
.split(/\r?\n/)
|
|
128
|
-
.map(
|
|
128
|
+
.map(t => t.trim())
|
|
129
129
|
.filter(Boolean);
|
|
130
130
|
|
|
131
131
|
const selectedType = selectPreferredImageMimeType(typeList);
|
|
@@ -149,7 +149,7 @@ async function readImageX11(): Promise<ClipboardReadResult> {
|
|
|
149
149
|
|
|
150
150
|
const candidateTypes = targets
|
|
151
151
|
.split(/\r?\n/)
|
|
152
|
-
.map(
|
|
152
|
+
.map(t => t.trim())
|
|
153
153
|
.filter(Boolean);
|
|
154
154
|
|
|
155
155
|
const selectedType = selectPreferredImageMimeType(candidateTypes);
|
|
@@ -207,7 +207,7 @@ async function readImageMacOS(): Promise<ClipboardImage | null> {
|
|
|
207
207
|
const file = Bun.file(tempFile);
|
|
208
208
|
if (await file.exists()) {
|
|
209
209
|
const buffer = await file.bytes();
|
|
210
|
-
await unlink(tempFile).catch(() => {});
|
|
210
|
+
await fs.unlink(tempFile).catch(() => {});
|
|
211
211
|
|
|
212
212
|
if (buffer.length > 0) {
|
|
213
213
|
return {
|
|
@@ -5,18 +5,12 @@
|
|
|
5
5
|
* we automatically inject the file contents as a FileMentionMessage
|
|
6
6
|
* so the agent doesn't need to read them manually.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
8
|
import path from "node:path";
|
|
10
9
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
11
|
-
import type { FileMentionMessage } from "
|
|
12
|
-
import { resolveReadPath } from "
|
|
13
|
-
import { formatAge } from "
|
|
14
|
-
import {
|
|
15
|
-
DEFAULT_MAX_BYTES,
|
|
16
|
-
formatSize,
|
|
17
|
-
truncateHead,
|
|
18
|
-
truncateStringToBytesFromStart,
|
|
19
|
-
} from "@oh-my-pi/pi-coding-agent/tools/truncate";
|
|
10
|
+
import type { FileMentionMessage } from "../session/messages";
|
|
11
|
+
import { resolveReadPath } from "../tools/path-utils";
|
|
12
|
+
import { formatAge } from "../tools/render-utils";
|
|
13
|
+
import { DEFAULT_MAX_BYTES, formatSize, truncateHead, truncateStringToBytesFromStart } from "../tools/truncate";
|
|
20
14
|
|
|
21
15
|
/** Regex to match @filepath patterns in text */
|
|
22
16
|
const FILE_MENTION_REGEX = /@([^\s@]+)/g;
|
package/src/utils/frontmatter.ts
CHANGED
|
@@ -38,7 +38,9 @@ export class FrontmatterError extends Error {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export interface FrontmatterOptions {
|
|
41
|
-
/** Source of the content */
|
|
41
|
+
/** Source of the content (alias: source) */
|
|
42
|
+
location?: unknown;
|
|
43
|
+
/** Source of the content (alias for location) */
|
|
42
44
|
source?: unknown;
|
|
43
45
|
/** Fallback frontmatter values */
|
|
44
46
|
fallback?: Record<string, unknown>;
|
|
@@ -56,7 +58,8 @@ export function parseFrontmatter(
|
|
|
56
58
|
content: string,
|
|
57
59
|
options?: FrontmatterOptions,
|
|
58
60
|
): { frontmatter: Record<string, unknown>; body: string } {
|
|
59
|
-
const { source, fallback, normalize = true, level = "warn" } = options ?? {};
|
|
61
|
+
const { location, source, fallback, normalize = true, level = "warn" } = options ?? {};
|
|
62
|
+
const loc = location ?? source;
|
|
60
63
|
const frontmatter: Record<string, unknown> = Object.assign({}, fallback);
|
|
61
64
|
|
|
62
65
|
const normalized = normalize ? stripHtmlComments(content.replace(/\r\n/g, "\n").replace(/\r/g, "\n")) : content;
|
|
@@ -77,7 +80,7 @@ export function parseFrontmatter(
|
|
|
77
80
|
const loaded = YAML.parse(metadata.replaceAll("\t", " ")) as Record<string, unknown> | null;
|
|
78
81
|
return { frontmatter: Object.assign(frontmatter, loaded), body: body };
|
|
79
82
|
} catch (error) {
|
|
80
|
-
const err = new FrontmatterError(toError(error),
|
|
83
|
+
const err = new FrontmatterError(toError(error), loc ?? `Inline '${truncate(content, 64)}'`);
|
|
81
84
|
if (level === "warn" || level === "fatal") {
|
|
82
85
|
logger.warn("Failed to parse YAML frontmatter", { err: err.toString() });
|
|
83
86
|
}
|
package/src/utils/fuzzy.ts
CHANGED
|
@@ -71,7 +71,7 @@ export function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) =>
|
|
|
71
71
|
const tokens = query
|
|
72
72
|
.trim()
|
|
73
73
|
.split(/\s+/)
|
|
74
|
-
.filter(
|
|
74
|
+
.filter(t => t.length > 0);
|
|
75
75
|
|
|
76
76
|
if (tokens.length === 0) {
|
|
77
77
|
return items;
|
|
@@ -104,5 +104,5 @@ export function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) =>
|
|
|
104
104
|
// Sort by score (asc, lower is better)
|
|
105
105
|
results.sort((a, b) => a.totalScore - b.totalScore);
|
|
106
106
|
|
|
107
|
-
return results.map(
|
|
107
|
+
return results.map(r => r.item);
|
|
108
108
|
}
|
package/src/utils/mime.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
2
|
import { fileTypeFromBuffer } from "file-type";
|
|
3
3
|
|
|
4
4
|
const IMAGE_MIME_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
|
|
@@ -6,7 +6,7 @@ const IMAGE_MIME_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image
|
|
|
6
6
|
const FILE_TYPE_SNIFF_BYTES = 4100;
|
|
7
7
|
|
|
8
8
|
export async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {
|
|
9
|
-
const fileHandle = await open(filePath, "r");
|
|
9
|
+
const fileHandle = await fs.open(filePath, "r");
|
|
10
10
|
try {
|
|
11
11
|
const buffer = Buffer.alloc(FILE_TYPE_SNIFF_BYTES);
|
|
12
12
|
const { bytesRead } = await fileHandle.read(buffer, 0, FILE_TYPE_SNIFF_BYTES, 0);
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
* .bashrc/.zshrc, which can be sourced before each command to provide a familiar
|
|
6
6
|
* shell experience.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import { homedir, tmpdir } from "node:os";
|
|
12
|
-
import { join } from "node:path";
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as path from "node:path";
|
|
13
11
|
import { postmortem } from "@oh-my-pi/pi-utils";
|
|
14
12
|
import { $ } from "bun";
|
|
15
13
|
|
|
@@ -19,10 +17,10 @@ let cachedSnapshotPath: string | null = null;
|
|
|
19
17
|
* Get the user's shell config file path.
|
|
20
18
|
*/
|
|
21
19
|
function getShellConfigFile(shell: string): string {
|
|
22
|
-
const home = homedir();
|
|
23
|
-
if (shell.includes("zsh")) return join(home, ".zshrc");
|
|
24
|
-
if (shell.includes("bash")) return join(home, ".bashrc");
|
|
25
|
-
return join(home, ".profile");
|
|
20
|
+
const home = os.homedir();
|
|
21
|
+
if (shell.includes("zsh")) return path.join(home, ".zshrc");
|
|
22
|
+
if (shell.includes("bash")) return path.join(home, ".bashrc");
|
|
23
|
+
return path.join(home, ".profile");
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
/**
|
|
@@ -131,12 +129,12 @@ export async function getOrCreateSnapshot(
|
|
|
131
129
|
const rcFile = getShellConfigFile(shell);
|
|
132
130
|
|
|
133
131
|
// Create snapshot directory
|
|
134
|
-
const snapshotDir = join(tmpdir(), "omp-shell-snapshots");
|
|
135
|
-
await mkdir(snapshotDir, { recursive: true });
|
|
132
|
+
const snapshotDir = path.join(os.tmpdir(), "omp-shell-snapshots");
|
|
133
|
+
await fs.promises.mkdir(snapshotDir, { recursive: true });
|
|
136
134
|
|
|
137
135
|
// Generate unique snapshot path
|
|
138
136
|
const shellName = shell.includes("zsh") ? "zsh" : shell.includes("bash") ? "bash" : "sh";
|
|
139
|
-
const snapshotPath = join(snapshotDir, `snapshot-${shellName}-${crypto.randomUUID()}.sh`);
|
|
137
|
+
const snapshotPath = path.join(snapshotDir, `snapshot-${shellName}-${crypto.randomUUID()}.sh`);
|
|
140
138
|
|
|
141
139
|
// Generate and execute snapshot script
|
|
142
140
|
const script = await generateSnapshotScript(shell, snapshotPath, rcFile);
|
|
@@ -167,6 +165,6 @@ export function getSnapshotSourceCommand(snapshotPath: string | null): string {
|
|
|
167
165
|
|
|
168
166
|
postmortem.register("shell-snapshot", () => {
|
|
169
167
|
if (cachedSnapshotPath) {
|
|
170
|
-
unlinkSync(cachedSnapshotPath);
|
|
168
|
+
fs.unlinkSync(cachedSnapshotPath);
|
|
171
169
|
}
|
|
172
170
|
});
|
package/src/utils/shell.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { access } from "node:fs/promises";
|
|
3
|
-
import { SettingsManager } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
|
|
1
|
+
import * as fs from "node:fs";
|
|
4
2
|
import { $ } from "bun";
|
|
3
|
+
import { SettingsManager } from "../config/settings-manager";
|
|
5
4
|
|
|
6
5
|
export interface ShellConfig {
|
|
7
6
|
shell: string;
|
|
@@ -17,7 +16,7 @@ let cachedShellConfig: ShellConfig | null = null;
|
|
|
17
16
|
*/
|
|
18
17
|
async function isExecutable(path: string): Promise<boolean> {
|
|
19
18
|
try {
|
|
20
|
-
await access(path, constants.X_OK);
|
|
19
|
+
await fs.promises.access(path, fs.constants.X_OK);
|
|
21
20
|
return true;
|
|
22
21
|
} catch {
|
|
23
22
|
return false;
|
|
@@ -138,7 +137,7 @@ export async function getShellConfig(): Promise<ShellConfig> {
|
|
|
138
137
|
` 1. Install Git for Windows: https://git-scm.com/download/win\n` +
|
|
139
138
|
` 2. Add your bash to PATH (Cygwin, MSYS2, etc.)\n` +
|
|
140
139
|
` 3. Set shellPath in ~/.omp/agent/settings.json\n\n` +
|
|
141
|
-
`Searched Git Bash in:\n${paths.map(
|
|
140
|
+
`Searched Git Bash in:\n${paths.map(p => ` ${p}`).join("\n")}`,
|
|
142
141
|
);
|
|
143
142
|
}
|
|
144
143
|
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generate session titles using a smol, fast model.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
4
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
6
5
|
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
7
|
-
import type { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
|
|
8
|
-
import { parseModelString, SMOL_MODEL_PRIORITY } from "@oh-my-pi/pi-coding-agent/config/model-resolver";
|
|
9
|
-
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
10
|
-
import titleSystemPrompt from "@oh-my-pi/pi-coding-agent/prompts/system/title-system.md" with { type: "text" };
|
|
11
6
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import type { ModelRegistry } from "../config/model-registry";
|
|
8
|
+
import { parseModelString, SMOL_MODEL_PRIORITY } from "../config/model-resolver";
|
|
9
|
+
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
10
|
+
import titleSystemPrompt from "../prompts/system/title-system.md" with { type: "text" };
|
|
12
11
|
|
|
13
12
|
const TITLE_SYSTEM_PROMPT = renderPromptTemplate(titleSystemPrompt);
|
|
14
13
|
|
|
@@ -21,7 +20,7 @@ function getTitleModelCandidates(registry: ModelRegistry, savedSmolModel?: strin
|
|
|
21
20
|
const candidates: Model<Api>[] = [];
|
|
22
21
|
const addCandidate = (model?: Model<Api>): void => {
|
|
23
22
|
if (!model) return;
|
|
24
|
-
const exists = candidates.some(
|
|
23
|
+
const exists = candidates.some(candidate => candidate.provider === model.provider && candidate.id === model.id);
|
|
25
24
|
if (!exists) {
|
|
26
25
|
candidates.push(model);
|
|
27
26
|
}
|
|
@@ -30,17 +29,17 @@ function getTitleModelCandidates(registry: ModelRegistry, savedSmolModel?: strin
|
|
|
30
29
|
if (savedSmolModel) {
|
|
31
30
|
const parsed = parseModelString(savedSmolModel);
|
|
32
31
|
if (parsed) {
|
|
33
|
-
const match = availableModels.find(
|
|
32
|
+
const match = availableModels.find(model => model.provider === parsed.provider && model.id === parsed.id);
|
|
34
33
|
addCandidate(match);
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
for (const pattern of SMOL_MODEL_PRIORITY) {
|
|
39
38
|
const needle = pattern.toLowerCase();
|
|
40
|
-
const exactMatch = availableModels.find(
|
|
39
|
+
const exactMatch = availableModels.find(model => model.id.toLowerCase() === needle);
|
|
41
40
|
addCandidate(exactMatch);
|
|
42
41
|
|
|
43
|
-
const fuzzyMatch = availableModels.find(
|
|
42
|
+
const fuzzyMatch = availableModels.find(model => model.id.toLowerCase().includes(needle));
|
|
44
43
|
addCandidate(fuzzyMatch);
|
|
45
44
|
}
|
|
46
45
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { createTempDir, logger } from "@oh-my-pi/pi-utils";
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { logger, TempDir } from "@oh-my-pi/pi-utils";
|
|
6
5
|
import { $ } from "bun";
|
|
6
|
+
import { APP_NAME, getBinDir } from "../config";
|
|
7
7
|
|
|
8
8
|
const TOOLS_DIR = getBinDir();
|
|
9
9
|
|
|
@@ -148,7 +148,7 @@ export async function getToolPath(tool: ToolName): Promise<string | null> {
|
|
|
148
148
|
if (!config) return null;
|
|
149
149
|
|
|
150
150
|
// Check our tools directory first
|
|
151
|
-
const localPath = join(TOOLS_DIR, config.binaryName + (platform() === "win32" ? ".exe" : ""));
|
|
151
|
+
const localPath = path.join(TOOLS_DIR, config.binaryName + (os.platform() === "win32" ? ".exe" : ""));
|
|
152
152
|
if (await Bun.file(localPath).exists()) {
|
|
153
153
|
return localPath;
|
|
154
154
|
}
|
|
@@ -187,8 +187,8 @@ async function downloadTool(tool: ToolName): Promise<string> {
|
|
|
187
187
|
const config = TOOLS[tool];
|
|
188
188
|
if (!config) throw new Error(`Unknown tool: ${tool}`);
|
|
189
189
|
|
|
190
|
-
const plat = platform();
|
|
191
|
-
const architecture = arch();
|
|
190
|
+
const plat = os.platform();
|
|
191
|
+
const architecture = os.arch();
|
|
192
192
|
|
|
193
193
|
// Get latest version
|
|
194
194
|
const version = await getLatestVersion(config.repo);
|
|
@@ -200,64 +200,64 @@ async function downloadTool(tool: ToolName): Promise<string> {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
// Create tools directory
|
|
203
|
-
await mkdir(TOOLS_DIR, { recursive: true });
|
|
203
|
+
await fs.mkdir(TOOLS_DIR, { recursive: true });
|
|
204
204
|
|
|
205
205
|
const downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;
|
|
206
206
|
const binaryExt = plat === "win32" ? ".exe" : "";
|
|
207
|
-
const binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);
|
|
207
|
+
const binaryPath = path.join(TOOLS_DIR, config.binaryName + binaryExt);
|
|
208
208
|
|
|
209
209
|
// Handle direct binary downloads (no archive extraction needed)
|
|
210
210
|
if (config.isDirectBinary) {
|
|
211
211
|
await downloadFile(downloadUrl, binaryPath);
|
|
212
212
|
if (plat !== "win32") {
|
|
213
|
-
await chmod(binaryPath, 0o755);
|
|
213
|
+
await fs.chmod(binaryPath, 0o755);
|
|
214
214
|
}
|
|
215
215
|
return binaryPath;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
// Download archive
|
|
219
|
-
const archivePath = join(TOOLS_DIR, assetName);
|
|
219
|
+
const archivePath = path.join(TOOLS_DIR, assetName);
|
|
220
220
|
await downloadFile(downloadUrl, archivePath);
|
|
221
221
|
|
|
222
222
|
// Extract
|
|
223
|
-
const tmp = await
|
|
223
|
+
const tmp = await TempDir.create("@omp-tools-extract-");
|
|
224
224
|
|
|
225
225
|
try {
|
|
226
226
|
if (assetName.endsWith(".tar.gz")) {
|
|
227
227
|
const archive = new Bun.Archive(await Bun.file(archivePath).arrayBuffer());
|
|
228
228
|
const files = await archive.files();
|
|
229
|
-
for (const [
|
|
230
|
-
await Bun.write(join(tmp.path,
|
|
229
|
+
for (const [filePath, file] of files) {
|
|
230
|
+
await Bun.write(path.join(tmp.path(), filePath), file);
|
|
231
231
|
}
|
|
232
232
|
} else if (assetName.endsWith(".zip")) {
|
|
233
|
-
await mkdir(tmp.path, { recursive: true });
|
|
234
|
-
await $`unzip -o ${archivePath} -d ${tmp.path}`.quiet().nothrow();
|
|
233
|
+
await fs.mkdir(tmp.path(), { recursive: true });
|
|
234
|
+
await $`unzip -o ${archivePath} -d ${tmp.path()}`.quiet().nothrow();
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// Find the binary in extracted files
|
|
238
238
|
// ast-grep releases the binary directly in the zip, not in a subdirectory
|
|
239
239
|
let extractedBinary: string;
|
|
240
240
|
if (tool === "sg") {
|
|
241
|
-
extractedBinary = join(tmp.path, config.binaryName + binaryExt);
|
|
241
|
+
extractedBinary = path.join(tmp.path(), config.binaryName + binaryExt);
|
|
242
242
|
} else {
|
|
243
|
-
const extractedDir = join(tmp.path, assetName.replace(/\.(tar\.gz|zip)$/, ""));
|
|
244
|
-
extractedBinary = join(extractedDir, config.binaryName + binaryExt);
|
|
243
|
+
const extractedDir = path.join(tmp.path(), assetName.replace(/\.(tar\.gz|zip)$/, ""));
|
|
244
|
+
extractedBinary = path.join(extractedDir, config.binaryName + binaryExt);
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
if (await Bun.file(extractedBinary).exists()) {
|
|
248
|
-
await rename(extractedBinary, binaryPath);
|
|
248
|
+
await fs.rename(extractedBinary, binaryPath);
|
|
249
249
|
} else {
|
|
250
250
|
throw new Error(`Binary not found in archive: ${extractedBinary}`);
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
// Make executable (Unix only)
|
|
254
254
|
if (plat !== "win32") {
|
|
255
|
-
await chmod(binaryPath, 0o755);
|
|
255
|
+
await fs.chmod(binaryPath, 0o755);
|
|
256
256
|
}
|
|
257
257
|
} finally {
|
|
258
258
|
// Cleanup
|
|
259
259
|
await tmp.remove();
|
|
260
|
-
await rm(archivePath, { force: true });
|
|
260
|
+
await fs.rm(archivePath, { force: true });
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
return binaryPath;
|