@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
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Migrates legacy
|
|
3
|
-
*
|
|
2
|
+
* Migrates legacy auth.json to SQLite-based agent.db.
|
|
3
|
+
* Auth credentials merge per-provider when missing.
|
|
4
4
|
* Original JSON files are backed up to .bak and removed after successful migration.
|
|
5
|
+
*
|
|
6
|
+
* NOTE: Settings migration is now handled by SettingsManager.migrateToYaml(),
|
|
7
|
+
* which migrates from both settings.json and agent.db to config.yaml.
|
|
5
8
|
*/
|
|
6
|
-
|
|
7
|
-
import { getAgentDbPath } from "@oh-my-pi/pi-coding-agent/config";
|
|
8
|
-
import type { Settings } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
|
|
9
9
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
10
|
+
import { getAgentDbPath } from "../config";
|
|
10
11
|
import { AgentStorage } from "./agent-storage";
|
|
11
12
|
import type { AuthCredential, AuthCredentialEntry, AuthStorageData } from "./auth-storage";
|
|
12
13
|
|
|
@@ -14,7 +15,7 @@ import type { AuthCredential, AuthCredentialEntry, AuthStorageData } from "./aut
|
|
|
14
15
|
type MigrationPaths = {
|
|
15
16
|
/** Directory containing agent.db */
|
|
16
17
|
agentDir: string;
|
|
17
|
-
/** Path to legacy settings.json file */
|
|
18
|
+
/** Path to legacy settings.json file (kept for API compatibility, no longer used) */
|
|
18
19
|
settingsPath: string;
|
|
19
20
|
/** Candidate paths to search for auth.json (checked in order) */
|
|
20
21
|
authPaths: string[];
|
|
@@ -22,7 +23,7 @@ type MigrationPaths = {
|
|
|
22
23
|
|
|
23
24
|
/** Result of the JSON-to-SQLite storage migration. */
|
|
24
25
|
export interface StorageMigrationResult {
|
|
25
|
-
/** Whether settings.json was migrated
|
|
26
|
+
/** Whether settings.json was migrated (always false - settings migration is handled elsewhere) */
|
|
26
27
|
migratedSettings: boolean;
|
|
27
28
|
/** Whether auth.json was migrated to agent.db */
|
|
28
29
|
migratedAuth: boolean;
|
|
@@ -39,20 +40,6 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
39
40
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
/**
|
|
43
|
-
* Transforms legacy settings to current schema (e.g., queueMode -> steeringMode).
|
|
44
|
-
* @param settings - Settings object potentially containing deprecated keys
|
|
45
|
-
* @returns Settings with deprecated keys renamed to current equivalents
|
|
46
|
-
*/
|
|
47
|
-
function migrateLegacySettings(settings: Settings): Settings {
|
|
48
|
-
const migrated = { ...settings } as Record<string, unknown>;
|
|
49
|
-
if ("queueMode" in migrated && !("steeringMode" in migrated)) {
|
|
50
|
-
migrated.steeringMode = migrated.queueMode;
|
|
51
|
-
delete migrated.queueMode;
|
|
52
|
-
}
|
|
53
|
-
return migrated as Settings;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
43
|
/**
|
|
57
44
|
* Normalizes credential entries to array format (legacy stored single credentials).
|
|
58
45
|
* @param entry - Single credential or array of credentials
|
|
@@ -99,32 +86,6 @@ async function backupJson(path: string): Promise<void> {
|
|
|
99
86
|
}
|
|
100
87
|
}
|
|
101
88
|
|
|
102
|
-
/**
|
|
103
|
-
* Migrates settings.json to SQLite storage if DB is empty.
|
|
104
|
-
* @param storage - AgentStorage instance to migrate into
|
|
105
|
-
* @param settingsPath - Path to legacy settings.json
|
|
106
|
-
* @param warnings - Array to collect non-fatal warnings
|
|
107
|
-
* @returns True if migration was performed
|
|
108
|
-
*/
|
|
109
|
-
async function migrateSettings(storage: AgentStorage, settingsPath: string, warnings: string[]): Promise<boolean> {
|
|
110
|
-
const settingsFile = Bun.file(settingsPath);
|
|
111
|
-
const settingsExists = await settingsFile.exists();
|
|
112
|
-
const hasDbSettings = storage.getSettings() !== null;
|
|
113
|
-
|
|
114
|
-
if (!settingsExists) return false;
|
|
115
|
-
if (hasDbSettings) {
|
|
116
|
-
warnings.push(`settings.json exists but agent.db is authoritative: ${settingsPath}`);
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const settingsJson = await readJsonFile<Settings>(settingsPath);
|
|
121
|
-
if (!settingsJson) return false;
|
|
122
|
-
|
|
123
|
-
storage.saveSettings(migrateLegacySettings(settingsJson));
|
|
124
|
-
await backupJson(settingsPath);
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
89
|
/**
|
|
129
90
|
* Finds the first valid auth.json from candidate paths (checked in priority order).
|
|
130
91
|
* @param authPaths - Candidate paths to search (e.g., project-local before global)
|
|
@@ -166,7 +127,7 @@ async function migrateAuth(storage: AgentStorage, authPaths: string[], warnings:
|
|
|
166
127
|
for (const [provider, entry] of Object.entries(authJson.data)) {
|
|
167
128
|
const credentials = normalizeCredentialEntry(entry)
|
|
168
129
|
.filter(isValidCredential)
|
|
169
|
-
.map(
|
|
130
|
+
.map(credential => credential);
|
|
170
131
|
|
|
171
132
|
if (credentials.length === 0) continue;
|
|
172
133
|
sawValid = true;
|
|
@@ -191,19 +152,16 @@ async function migrateAuth(storage: AgentStorage, authPaths: string[], warnings:
|
|
|
191
152
|
}
|
|
192
153
|
|
|
193
154
|
/**
|
|
194
|
-
* Migrates legacy
|
|
195
|
-
* Settings
|
|
155
|
+
* Migrates legacy auth.json to SQLite-based agent.db.
|
|
156
|
+
* Settings migration is handled separately by SettingsManager.migrateToYaml().
|
|
196
157
|
* @param paths - Configuration specifying locations of legacy files and target DB
|
|
197
158
|
* @returns Result indicating what was migrated and any warnings encountered
|
|
198
159
|
*/
|
|
199
160
|
export async function migrateJsonStorage(paths: MigrationPaths): Promise<StorageMigrationResult> {
|
|
200
|
-
const storage = AgentStorage.open(getAgentDbPath(paths.agentDir));
|
|
161
|
+
const storage = await AgentStorage.open(getAgentDbPath(paths.agentDir));
|
|
201
162
|
const warnings: string[] = [];
|
|
202
163
|
|
|
203
|
-
const
|
|
204
|
-
migrateSettings(storage, paths.settingsPath, warnings),
|
|
205
|
-
migrateAuth(storage, paths.authPaths, warnings),
|
|
206
|
-
]);
|
|
164
|
+
const migratedAuth = await migrateAuth(storage, paths.authPaths, warnings);
|
|
207
165
|
|
|
208
166
|
if (warnings.length > 0) {
|
|
209
167
|
for (const warning of warnings) {
|
|
@@ -211,5 +169,5 @@ export async function migrateJsonStorage(paths: MigrationPaths): Promise<Storage
|
|
|
211
169
|
}
|
|
212
170
|
}
|
|
213
171
|
|
|
214
|
-
return { migratedSettings, migratedAuth, warnings };
|
|
172
|
+
return { migratedSettings: false, migratedAuth, warnings };
|
|
215
173
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_MAX_BYTES } from "@oh-my-pi/pi-coding-agent/tools/truncate";
|
|
2
1
|
import { sanitizeText } from "@oh-my-pi/pi-utils";
|
|
2
|
+
import { DEFAULT_MAX_BYTES } from "../tools/truncate";
|
|
3
3
|
|
|
4
4
|
export interface OutputSummary {
|
|
5
5
|
output: string;
|
|
@@ -141,7 +141,7 @@ export class OutputSink {
|
|
|
141
141
|
};
|
|
142
142
|
|
|
143
143
|
return new WritableStream<Uint8Array | string>({
|
|
144
|
-
write: async
|
|
144
|
+
write: async chunk => {
|
|
145
145
|
if (typeof chunk === "string") {
|
|
146
146
|
await this.push(chunk);
|
|
147
147
|
} else {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { 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 { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
6
5
|
import { $ } from "bun";
|
|
6
|
+
import { CONFIG_DIR_NAME } from "../config";
|
|
7
7
|
|
|
8
8
|
export interface SSHConnectionTarget {
|
|
9
9
|
name: string;
|
|
@@ -25,52 +25,44 @@ export interface SSHHostInfo {
|
|
|
25
25
|
compatEnabled: boolean;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const CONTROL_DIR = join(homedir(), CONFIG_DIR_NAME, "ssh-control");
|
|
29
|
-
const CONTROL_PATH = join(CONTROL_DIR, "%h.sock");
|
|
30
|
-
const HOST_INFO_DIR = join(homedir(), CONFIG_DIR_NAME, "remote-host");
|
|
28
|
+
const CONTROL_DIR = path.join(os.homedir(), CONFIG_DIR_NAME, "ssh-control");
|
|
29
|
+
const CONTROL_PATH = path.join(CONTROL_DIR, "%h.sock");
|
|
30
|
+
const HOST_INFO_DIR = path.join(os.homedir(), CONFIG_DIR_NAME, "remote-host");
|
|
31
31
|
const HOST_INFO_VERSION = 2;
|
|
32
32
|
|
|
33
33
|
const activeHosts = new Map<string, SSHConnectionTarget>();
|
|
34
34
|
const pendingConnections = new Map<string, Promise<void>>();
|
|
35
35
|
const hostInfoCache = new Map<string, SSHHostInfo>();
|
|
36
36
|
|
|
37
|
-
function ensureControlDir(): void {
|
|
38
|
-
|
|
39
|
-
mkdirSync(CONTROL_DIR, { recursive: true, mode: 0o700 });
|
|
40
|
-
}
|
|
37
|
+
async function ensureControlDir(): Promise<void> {
|
|
38
|
+
await fs.mkdir(CONTROL_DIR, { recursive: true, mode: 0o700 });
|
|
41
39
|
try {
|
|
42
|
-
|
|
40
|
+
await fs.chmod(CONTROL_DIR, 0o700);
|
|
43
41
|
} catch (err) {
|
|
44
42
|
logger.debug("SSH control dir chmod failed", { path: CONTROL_DIR, error: String(err) });
|
|
45
43
|
}
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
function ensureHostInfoDir(): void {
|
|
49
|
-
if (!existsSync(HOST_INFO_DIR)) {
|
|
50
|
-
mkdirSync(HOST_INFO_DIR, { recursive: true, mode: 0o700 });
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
chmodSync(HOST_INFO_DIR, 0o700);
|
|
54
|
-
} catch (err) {
|
|
55
|
-
logger.debug("SSH host info dir chmod failed", { path: HOST_INFO_DIR, error: String(err) });
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
46
|
function sanitizeHostName(name: string): string {
|
|
60
47
|
const sanitized = name.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
61
48
|
return sanitized.length > 0 ? sanitized : "host";
|
|
62
49
|
}
|
|
63
50
|
|
|
64
51
|
function getHostInfoPath(name: string): string {
|
|
65
|
-
return join(HOST_INFO_DIR, `${sanitizeHostName(name)}.json`);
|
|
52
|
+
return path.join(HOST_INFO_DIR, `${sanitizeHostName(name)}.json`);
|
|
66
53
|
}
|
|
67
54
|
|
|
68
|
-
function validateKeyPermissions(keyPath?: string): void {
|
|
55
|
+
async function validateKeyPermissions(keyPath?: string): Promise<void> {
|
|
69
56
|
if (!keyPath) return;
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
let stats: Awaited<ReturnType<typeof fs.stat>>;
|
|
58
|
+
try {
|
|
59
|
+
stats = await fs.stat(keyPath);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
if (isEnoent(err)) {
|
|
62
|
+
throw new Error(`SSH key not found: ${keyPath}`);
|
|
63
|
+
}
|
|
64
|
+
throw err;
|
|
72
65
|
}
|
|
73
|
-
const stats = statSync(keyPath);
|
|
74
66
|
if (!stats.isFile()) {
|
|
75
67
|
throw new Error(`SSH key is not a file: ${keyPath}`);
|
|
76
68
|
}
|
|
@@ -208,31 +200,31 @@ function shouldRefreshHostInfo(host: SSHConnectionTarget, info: SSHHostInfo): bo
|
|
|
208
200
|
return false;
|
|
209
201
|
}
|
|
210
202
|
|
|
211
|
-
function loadHostInfoFromDisk(host: SSHConnectionTarget): SSHHostInfo | undefined {
|
|
203
|
+
async function loadHostInfoFromDisk(host: SSHConnectionTarget): Promise<SSHHostInfo | undefined> {
|
|
212
204
|
const path = getHostInfoPath(host.name);
|
|
213
|
-
if (!existsSync(path)) return undefined;
|
|
214
205
|
try {
|
|
215
|
-
const raw =
|
|
206
|
+
const raw = await fs.readFile(path, "utf-8");
|
|
216
207
|
const parsed = parseHostInfo(JSON.parse(raw));
|
|
217
208
|
if (!parsed) return undefined;
|
|
218
209
|
const resolved = applyCompatOverride(host, parsed);
|
|
219
210
|
hostInfoCache.set(host.name, resolved);
|
|
220
211
|
return resolved;
|
|
221
212
|
} catch (err) {
|
|
213
|
+
if (isEnoent(err)) return undefined;
|
|
222
214
|
logger.warn("Failed to load SSH host info", { host: host.name, error: String(err) });
|
|
223
215
|
return undefined;
|
|
224
216
|
}
|
|
225
217
|
}
|
|
226
218
|
|
|
227
|
-
function loadHostInfoFromDiskByName(hostName: string): SSHHostInfo | undefined {
|
|
219
|
+
async function loadHostInfoFromDiskByName(hostName: string): Promise<SSHHostInfo | undefined> {
|
|
228
220
|
const path = getHostInfoPath(hostName);
|
|
229
|
-
if (!existsSync(path)) return undefined;
|
|
230
221
|
try {
|
|
231
|
-
const raw =
|
|
222
|
+
const raw = await fs.readFile(path, "utf-8");
|
|
232
223
|
const parsed = parseHostInfo(JSON.parse(raw));
|
|
233
224
|
if (!parsed) return undefined;
|
|
234
225
|
return parsed;
|
|
235
226
|
} catch (err) {
|
|
227
|
+
if (isEnoent(err)) return undefined;
|
|
236
228
|
logger.warn("Failed to load SSH host info", { host: hostName, error: String(err) });
|
|
237
229
|
return undefined;
|
|
238
230
|
}
|
|
@@ -240,7 +232,6 @@ function loadHostInfoFromDiskByName(hostName: string): SSHHostInfo | undefined {
|
|
|
240
232
|
|
|
241
233
|
async function persistHostInfo(host: SSHConnectionTarget, info: SSHHostInfo): Promise<void> {
|
|
242
234
|
try {
|
|
243
|
-
ensureHostInfoDir();
|
|
244
235
|
const path = getHostInfoPath(host.name);
|
|
245
236
|
const payload = { ...info, version: HOST_INFO_VERSION };
|
|
246
237
|
hostInfoCache.set(host.name, payload);
|
|
@@ -252,7 +243,7 @@ async function persistHostInfo(host: SSHConnectionTarget, info: SSHHostInfo): Pr
|
|
|
252
243
|
|
|
253
244
|
async function probeHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
|
|
254
245
|
const command = 'echo "$OSTYPE|$SHELL|$BASH_VERSION" 2>/dev/null || echo "%OS%|%COMSPEC%|"';
|
|
255
|
-
const result = await runSshCaptureSync(buildRemoteCommand(host, command));
|
|
246
|
+
const result = await runSshCaptureSync(await buildRemoteCommand(host, command));
|
|
256
247
|
if (result.exitCode !== 0 && !result.stdout) {
|
|
257
248
|
logger.debug("SSH host probe failed", { host: host.name, error: result.stderr });
|
|
258
249
|
const fallback: SSHHostInfo = {
|
|
@@ -315,11 +306,11 @@ async function probeHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
|
|
|
315
306
|
const hasBash = !unexpandedPosixVars && (Boolean(bashVersion) || shell === "bash");
|
|
316
307
|
let compatShell: SSHHostInfo["compatShell"];
|
|
317
308
|
if (os === "windows" && host.compat !== false) {
|
|
318
|
-
const bashProbe = await runSshCaptureSync(buildRemoteCommand(host, 'bash -lc "echo OMP_BASH_OK"'));
|
|
309
|
+
const bashProbe = await runSshCaptureSync(await buildRemoteCommand(host, 'bash -lc "echo OMP_BASH_OK"'));
|
|
319
310
|
if (bashProbe.exitCode === 0 && bashProbe.stdout.includes("OMP_BASH_OK")) {
|
|
320
311
|
compatShell = "bash";
|
|
321
312
|
} else {
|
|
322
|
-
const shProbe = await runSshCaptureSync(buildRemoteCommand(host, 'sh -lc "echo OMP_SH_OK"'));
|
|
313
|
+
const shProbe = await runSshCaptureSync(await buildRemoteCommand(host, 'sh -lc "echo OMP_SH_OK"'));
|
|
323
314
|
if (shProbe.exitCode === 0 && shProbe.stdout.includes("OMP_SH_OK")) {
|
|
324
315
|
compatShell = "sh";
|
|
325
316
|
}
|
|
@@ -344,18 +335,20 @@ async function probeHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
|
|
|
344
335
|
return info;
|
|
345
336
|
}
|
|
346
337
|
|
|
347
|
-
export function getHostInfo(hostName: string): SSHHostInfo | undefined {
|
|
348
|
-
|
|
338
|
+
export async function getHostInfo(hostName: string): Promise<SSHHostInfo | undefined> {
|
|
339
|
+
const cached = hostInfoCache.get(hostName);
|
|
340
|
+
if (cached) return cached;
|
|
341
|
+
return loadHostInfoFromDiskByName(hostName);
|
|
349
342
|
}
|
|
350
343
|
|
|
351
|
-
export function getHostInfoForHost(host: SSHConnectionTarget): SSHHostInfo | undefined {
|
|
344
|
+
export async function getHostInfoForHost(host: SSHConnectionTarget): Promise<SSHHostInfo | undefined> {
|
|
352
345
|
const cached = hostInfoCache.get(host.name);
|
|
353
346
|
if (cached) {
|
|
354
347
|
const resolved = applyCompatOverride(host, cached);
|
|
355
348
|
if (resolved !== cached) hostInfoCache.set(host.name, resolved);
|
|
356
349
|
return resolved;
|
|
357
350
|
}
|
|
358
|
-
return loadHostInfoFromDisk(host);
|
|
351
|
+
return await loadHostInfoFromDisk(host);
|
|
359
352
|
}
|
|
360
353
|
|
|
361
354
|
export async function ensureHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
|
|
@@ -365,7 +358,7 @@ export async function ensureHostInfo(host: SSHConnectionTarget): Promise<SSHHost
|
|
|
365
358
|
hostInfoCache.set(host.name, resolved);
|
|
366
359
|
if (!shouldRefreshHostInfo(host, resolved)) return resolved;
|
|
367
360
|
}
|
|
368
|
-
const fromDisk = loadHostInfoFromDisk(host);
|
|
361
|
+
const fromDisk = await loadHostInfoFromDisk(host);
|
|
369
362
|
if (fromDisk && !shouldRefreshHostInfo(host, fromDisk)) return fromDisk;
|
|
370
363
|
await ensureConnection(host);
|
|
371
364
|
const current = hostInfoCache.get(host.name);
|
|
@@ -373,8 +366,8 @@ export async function ensureHostInfo(host: SSHConnectionTarget): Promise<SSHHost
|
|
|
373
366
|
return probeHostInfo(host);
|
|
374
367
|
}
|
|
375
368
|
|
|
376
|
-
export function buildRemoteCommand(host: SSHConnectionTarget, command: string): string[] {
|
|
377
|
-
validateKeyPermissions(host.keyPath);
|
|
369
|
+
export async function buildRemoteCommand(host: SSHConnectionTarget, command: string): Promise<string[]> {
|
|
370
|
+
await validateKeyPermissions(host.keyPath);
|
|
378
371
|
return [...buildCommonArgs(host), buildSshTarget(host), command];
|
|
379
372
|
}
|
|
380
373
|
|
|
@@ -388,14 +381,14 @@ export async function ensureConnection(host: SSHConnectionTarget): Promise<void>
|
|
|
388
381
|
|
|
389
382
|
const promise = (async () => {
|
|
390
383
|
ensureSshBinary();
|
|
391
|
-
ensureControlDir();
|
|
392
|
-
validateKeyPermissions(host.keyPath);
|
|
384
|
+
await ensureControlDir();
|
|
385
|
+
await validateKeyPermissions(host.keyPath);
|
|
393
386
|
|
|
394
387
|
const target = buildSshTarget(host);
|
|
395
388
|
const check = await runSshSync(["-O", "check", ...buildCommonArgs(host), target]);
|
|
396
389
|
if (check.exitCode === 0) {
|
|
397
390
|
activeHosts.set(key, host);
|
|
398
|
-
if (!hostInfoCache.has(key) && !loadHostInfoFromDisk(host)) {
|
|
391
|
+
if (!hostInfoCache.has(key) && !(await loadHostInfoFromDisk(host))) {
|
|
399
392
|
await probeHostInfo(host);
|
|
400
393
|
}
|
|
401
394
|
return;
|
|
@@ -408,7 +401,7 @@ export async function ensureConnection(host: SSHConnectionTarget): Promise<void>
|
|
|
408
401
|
}
|
|
409
402
|
|
|
410
403
|
activeHosts.set(key, host);
|
|
411
|
-
if (!hostInfoCache.has(key) && !loadHostInfoFromDisk(host)) {
|
|
404
|
+
if (!hostInfoCache.has(key) && !(await loadHostInfoFromDisk(host))) {
|
|
412
405
|
await probeHostInfo(host);
|
|
413
406
|
}
|
|
414
407
|
})();
|
package/src/ssh/ssh-executor.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { OutputSink } from "@oh-my-pi/pi-coding-agent/session/streaming-output";
|
|
2
1
|
import { cspawn, logger, ptree } from "@oh-my-pi/pi-utils";
|
|
2
|
+
import { OutputSink } from "../session/streaming-output";
|
|
3
3
|
import { buildRemoteCommand, ensureConnection, ensureHostInfo, type SSHConnectionTarget } from "./connection-manager";
|
|
4
4
|
import { hasSshfs, mountRemote } from "./sshfs-mount";
|
|
5
5
|
|
|
@@ -76,7 +76,7 @@ export async function executeSSH(
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
const child = cspawn(["ssh", ...buildRemoteCommand(host, resolvedCommand)], {
|
|
79
|
+
const child = cspawn(["ssh", ...(await buildRemoteCommand(host, resolvedCommand))], {
|
|
80
80
|
signal: options?.signal,
|
|
81
81
|
timeout: options?.timeout,
|
|
82
82
|
});
|
package/src/ssh/sshfs-mount.ts
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { CONFIG_DIR_NAME } from "@oh-my-pi/pi-coding-agent/config";
|
|
5
|
-
import { logger } from "@oh-my-pi/pi-utils";
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
6
4
|
import { $ } from "bun";
|
|
5
|
+
import { CONFIG_DIR_NAME } from "../config";
|
|
7
6
|
import { getControlDir, getControlPathTemplate, type SSHConnectionTarget } from "./connection-manager";
|
|
8
7
|
|
|
9
|
-
const REMOTE_DIR = join(homedir(), CONFIG_DIR_NAME, "remote");
|
|
8
|
+
const REMOTE_DIR = path.join(os.homedir(), CONFIG_DIR_NAME, "remote");
|
|
10
9
|
const CONTROL_DIR = getControlDir();
|
|
11
10
|
const CONTROL_PATH = getControlPathTemplate();
|
|
12
11
|
|
|
13
12
|
const mountedPaths = new Set<string>();
|
|
14
13
|
|
|
15
|
-
function ensureDir(path: string, mode = 0o700): void {
|
|
16
|
-
if (!existsSync(path)) {
|
|
17
|
-
mkdirSync(path, { recursive: true, mode });
|
|
18
|
-
}
|
|
14
|
+
async function ensureDir(path: string, mode = 0o700): Promise<void> {
|
|
19
15
|
try {
|
|
20
|
-
|
|
21
|
-
} catch
|
|
22
|
-
|
|
16
|
+
await fs.promises.mkdir(path, { recursive: true, mode });
|
|
17
|
+
} catch {
|
|
18
|
+
await fs.promises.chmod(path, mode).catch(e => void e);
|
|
23
19
|
}
|
|
24
20
|
}
|
|
25
21
|
|
|
@@ -30,7 +26,7 @@ function getMountName(host: SSHConnectionTarget): string {
|
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
function getMountPath(host: SSHConnectionTarget): string {
|
|
33
|
-
return join(REMOTE_DIR, getMountName(host));
|
|
29
|
+
return path.join(REMOTE_DIR, getMountName(host));
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
function buildSshTarget(host: SSHConnectionTarget): string {
|
|
@@ -95,11 +91,8 @@ export async function isMounted(path: string): Promise<boolean> {
|
|
|
95
91
|
export async function mountRemote(host: SSHConnectionTarget, remotePath = "/"): Promise<string | undefined> {
|
|
96
92
|
if (!hasSshfs()) return undefined;
|
|
97
93
|
|
|
98
|
-
ensureDir(REMOTE_DIR);
|
|
99
|
-
ensureDir(CONTROL_DIR);
|
|
100
|
-
|
|
101
94
|
const mountPath = getMountPath(host);
|
|
102
|
-
ensureDir(mountPath);
|
|
95
|
+
await Promise.all([ensureDir(REMOTE_DIR), ensureDir(CONTROL_DIR), ensureDir(mountPath)]);
|
|
103
96
|
|
|
104
97
|
if (await isMounted(mountPath)) {
|
|
105
98
|
mountedPaths.add(mountPath);
|
package/src/system-prompt.ts
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* System prompt construction and project context loading
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
import { homedir } from "node:os";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
import { contextFileCapability } from "@oh-my-pi/pi-coding-agent/capability/context-file";
|
|
9
|
-
import { systemPromptCapability } from "@oh-my-pi/pi-coding-agent/capability/system-prompt";
|
|
10
|
-
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
11
|
-
import type { SkillsSettings } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
|
|
12
|
-
import {
|
|
13
|
-
type ContextFile,
|
|
14
|
-
loadCapability,
|
|
15
|
-
type SystemPrompt as SystemPromptFile,
|
|
16
|
-
} from "@oh-my-pi/pi-coding-agent/discovery/index";
|
|
17
|
-
import { loadSkills, type Skill } from "@oh-my-pi/pi-coding-agent/extensibility/skills";
|
|
18
|
-
import customSystemPromptTemplate from "@oh-my-pi/pi-coding-agent/prompts/system/custom-system-prompt.md" with {
|
|
19
|
-
type: "text",
|
|
20
|
-
};
|
|
21
|
-
import systemPromptTemplate from "@oh-my-pi/pi-coding-agent/prompts/system/system-prompt.md" with { type: "text" };
|
|
4
|
+
import * as os from "node:os";
|
|
5
|
+
import * as path from "node:path";
|
|
22
6
|
import { $ } from "bun";
|
|
23
7
|
import chalk from "chalk";
|
|
24
|
-
import
|
|
8
|
+
import { contextFileCapability } from "./capability/context-file";
|
|
9
|
+
import { systemPromptCapability } from "./capability/system-prompt";
|
|
10
|
+
import { renderPromptTemplate } from "./config/prompt-templates";
|
|
11
|
+
import type { SkillsSettings } from "./config/settings-manager";
|
|
12
|
+
import { type ContextFile, loadCapability, type SystemPrompt as SystemPromptFile } from "./discovery";
|
|
13
|
+
import { loadSkills, type Skill } from "./extensibility/skills";
|
|
14
|
+
import customSystemPromptTemplate from "./prompts/system/custom-system-prompt.md" with { type: "text" };
|
|
15
|
+
import systemPromptTemplate from "./prompts/system/system-prompt.md" with { type: "text" };
|
|
16
|
+
import type { ToolName } from "./tools";
|
|
25
17
|
|
|
26
18
|
interface GitContext {
|
|
27
19
|
isRepo: boolean;
|
|
@@ -42,7 +34,7 @@ export async function loadGitContext(cwd: string): Promise<GitContext | null> {
|
|
|
42
34
|
.quiet()
|
|
43
35
|
.text()
|
|
44
36
|
.catch(() => null)
|
|
45
|
-
.then(
|
|
37
|
+
.then(text => text?.trim() ?? null);
|
|
46
38
|
|
|
47
39
|
// Check if inside a git repo
|
|
48
40
|
const isGitRepo = await git("rev-parse", "--is-inside-work-tree");
|
|
@@ -86,7 +78,7 @@ function firstNonEmptyLine(value: string | null): string | null {
|
|
|
86
78
|
if (!value) return null;
|
|
87
79
|
const line = value
|
|
88
80
|
.split("\n")
|
|
89
|
-
.map(
|
|
81
|
+
.map(entry => entry.trim())
|
|
90
82
|
.filter(Boolean)[0];
|
|
91
83
|
return line ?? null;
|
|
92
84
|
}
|
|
@@ -94,9 +86,9 @@ function firstNonEmptyLine(value: string | null): string | null {
|
|
|
94
86
|
function parseWmicTable(output: string, header: string): string | null {
|
|
95
87
|
const lines = output
|
|
96
88
|
.split("\n")
|
|
97
|
-
.map(
|
|
89
|
+
.map(line => line.trim())
|
|
98
90
|
.filter(Boolean);
|
|
99
|
-
const filtered = lines.filter(
|
|
91
|
+
const filtered = lines.filter(line => line.toLowerCase() !== header.toLowerCase());
|
|
100
92
|
return filtered[0] ?? null;
|
|
101
93
|
}
|
|
102
94
|
|
|
@@ -137,8 +129,8 @@ function listAgentsMdFiles(root: string, limit: number): string[] {
|
|
|
137
129
|
new Bun.Glob(AGENTS_MD_PATTERN).scanSync({ cwd: root, onlyFiles: true, dot: false, absolute: false }),
|
|
138
130
|
);
|
|
139
131
|
const normalized = entries
|
|
140
|
-
.map(
|
|
141
|
-
.filter(
|
|
132
|
+
.map(entry => normalizePath(entry))
|
|
133
|
+
.filter(entry => entry.length > 0 && !entry.includes("node_modules"))
|
|
142
134
|
.sort();
|
|
143
135
|
return normalized.length > limit ? normalized.slice(0, limit) : normalized;
|
|
144
136
|
} catch {
|
|
@@ -274,8 +266,8 @@ async function getCpuModel(): Promise<string | null> {
|
|
|
274
266
|
if (lscpu) {
|
|
275
267
|
const match = lscpu
|
|
276
268
|
.split("\n")
|
|
277
|
-
.map(
|
|
278
|
-
.find(
|
|
269
|
+
.map(line => line.trim())
|
|
270
|
+
.find(line => line.toLowerCase().startsWith("model name:"));
|
|
279
271
|
if (match) return match.split(":").slice(1).join(":").trim();
|
|
280
272
|
}
|
|
281
273
|
const cpuInfo = await Bun.file("/proc/cpuinfo")
|
|
@@ -370,7 +362,7 @@ function normalizeDesktopValue(value: string): string {
|
|
|
370
362
|
if (!trimmed) return "unknown";
|
|
371
363
|
const parts = trimmed
|
|
372
364
|
.split(":")
|
|
373
|
-
.map(
|
|
365
|
+
.map(part => part.trim())
|
|
374
366
|
.filter(Boolean);
|
|
375
367
|
return parts[0] ?? trimmed;
|
|
376
368
|
}
|
|
@@ -436,14 +428,15 @@ interface SystemInfoCache {
|
|
|
436
428
|
}
|
|
437
429
|
|
|
438
430
|
function getSystemInfoCachePath(): string {
|
|
439
|
-
return join(homedir(), ".omp", "system_info.json");
|
|
431
|
+
return path.join(os.homedir(), ".omp", "system_info.json");
|
|
440
432
|
}
|
|
441
433
|
|
|
442
434
|
async function loadSystemInfoCache(): Promise<SystemInfoCache | null> {
|
|
443
435
|
try {
|
|
444
436
|
const cachePath = getSystemInfoCachePath();
|
|
445
|
-
|
|
446
|
-
|
|
437
|
+
const file = Bun.file(cachePath);
|
|
438
|
+
if (!(await file.exists())) return null;
|
|
439
|
+
const content = await file.json();
|
|
447
440
|
return content as SystemInfoCache;
|
|
448
441
|
} catch {
|
|
449
442
|
return null;
|
|
@@ -494,7 +487,7 @@ async function getDiskInfo(): Promise<string | null> {
|
|
|
494
487
|
.text()
|
|
495
488
|
.catch(() => null);
|
|
496
489
|
if (!output) return null;
|
|
497
|
-
const lines = output.split("\n").filter(
|
|
490
|
+
const lines = output.split("\n").filter(l => l.trim() && !l.startsWith("Node"));
|
|
498
491
|
const disks: string[] = [];
|
|
499
492
|
for (const line of lines) {
|
|
500
493
|
const parts = line.split(",");
|
|
@@ -590,7 +583,7 @@ export async function loadProjectContextFiles(
|
|
|
590
583
|
const result = await loadCapability(contextFileCapability.id, { cwd: resolvedCwd });
|
|
591
584
|
|
|
592
585
|
// Convert ContextFile items and preserve depth info
|
|
593
|
-
const files = result.items.map(
|
|
586
|
+
const files = result.items.map(item => {
|
|
594
587
|
const contextFile = item as ContextFile;
|
|
595
588
|
return {
|
|
596
589
|
path: contextFile.path,
|
|
@@ -622,8 +615,8 @@ export async function loadSystemPromptFiles(options: LoadContextFilesOptions = {
|
|
|
622
615
|
if (result.items.length === 0) return null;
|
|
623
616
|
|
|
624
617
|
// Combine all SYSTEM.md contents (user-level first, then project-level)
|
|
625
|
-
const userLevel = result.items.filter(
|
|
626
|
-
const projectLevel = result.items.filter(
|
|
618
|
+
const userLevel = result.items.filter(item => item.level === "user");
|
|
619
|
+
const projectLevel = result.items.filter(item => item.level === "project");
|
|
627
620
|
|
|
628
621
|
const parts: string[] = [];
|
|
629
622
|
for (const item of [...userLevel, ...projectLevel]) {
|