@vandeepunk/pi-coding-agent 0.0.6 → 1.0.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 +814 -16
- package/README.md +66 -27
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +7 -0
- package/dist/bun/cli.js.map +1 -0
- package/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/bun/register-bedrock.d.ts.map +1 -0
- package/dist/bun/register-bedrock.js +4 -0
- package/dist/bun/register-bedrock.js.map +1 -0
- package/dist/cli/args.d.ts +9 -4
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +61 -22
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +4 -0
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/initial-message.d.ts +18 -0
- package/dist/cli/initial-message.d.ts.map +1 -0
- package/dist/cli/initial-message.js +22 -0
- package/dist/cli/initial-message.js.map +1 -0
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +2 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +83 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -0
- package/dist/core/agent-session-runtime.js +236 -0
- package/dist/core/agent-session-runtime.js.map +1 -0
- package/dist/core/agent-session-services.d.ts +86 -0
- package/dist/core/agent-session-services.d.ts.map +1 -0
- package/dist/core/agent-session-services.js +116 -0
- package/dist/core/agent-session-services.js.map +1 -0
- package/dist/core/agent-session.d.ts +63 -49
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +599 -370
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +38 -8
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +220 -96
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts +6 -7
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +27 -114
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -2
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +3 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +31 -25
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/utils.d.ts +3 -0
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +16 -1
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +7 -3
- package/dist/core/exec.js.map +1 -1
- package/dist/core/export-html/index.d.ts +7 -4
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +10 -8
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.css +43 -13
- package/dist/core/export-html/template.html +1 -0
- package/dist/core/export-html/template.js +118 -14
- package/dist/core/export-html/tool-renderer.d.ts +9 -4
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +48 -10
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +5 -4
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +2 -2
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +49 -13
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +13 -11
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +139 -64
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +174 -34
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +10 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts +4 -11
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +6 -86
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +18 -2
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +220 -40
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/index.d.ts +4 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +283 -50
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +221 -134
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts +33 -3
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +165 -97
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +35 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +205 -32
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/output-guard.d.ts +6 -0
- package/dist/core/output-guard.d.ts.map +1 -0
- package/dist/core/output-guard.js +59 -0
- package/dist/core/output-guard.js.map +1 -0
- package/dist/core/package-manager.d.ts +43 -2
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +541 -102
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +5 -4
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +35 -37
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts +6 -0
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +75 -8
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts +6 -5
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +166 -119
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +11 -8
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +31 -29
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-cwd.d.ts +19 -0
- package/dist/core/session-cwd.d.ts.map +1 -0
- package/dist/core/session-cwd.js +38 -0
- package/dist/core/session-cwd.js.map +1 -0
- package/dist/core/session-manager.d.ts +11 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +39 -25
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +60 -10
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +291 -140
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +5 -3
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +54 -9
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts +2 -3
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +3 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/source-info.d.ts +18 -0
- package/dist/core/source-info.d.ts.map +1 -0
- package/dist/core/source-info.js +19 -0
- package/dist/core/source-info.js.map +1 -0
- package/dist/core/system-prompt.d.ts +4 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +31 -52
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/timings.d.ts +1 -0
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +6 -0
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/bash.d.ts +24 -6
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +225 -115
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +23 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +151 -57
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +20 -6
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +111 -61
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-queue.js +37 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -0
- package/dist/core/tools/find.d.ts +11 -4
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +82 -30
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +15 -4
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +83 -29
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +63 -21
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +51 -26
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +9 -3
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +67 -13
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +10 -3
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +110 -51
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +21 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -0
- package/dist/core/tools/render-utils.js +49 -0
- package/dist/core/tools/render-utils.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.js +32 -0
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
- package/dist/core/tools/write.d.ts +9 -3
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +168 -30
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +354 -379
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +31 -11
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +14 -3
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +22 -9
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +2 -2
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +2 -2
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +14 -14
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +6 -6
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +5 -2
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +18 -9
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +5 -5
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +8 -8
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +21 -40
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +3 -36
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +5 -44
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +7 -7
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +13 -9
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +7 -7
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +4 -4
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +33 -36
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +5 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +25 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +5 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +5 -1
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +5 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +17 -29
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +139 -501
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +25 -4
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +184 -34
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +6 -6
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +12 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +25 -17
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +669 -385
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +3 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +83 -48
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +2 -2
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +90 -79
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/modes/rpc/jsonl.d.ts.map +1 -0
- package/dist/modes/rpc/jsonl.js +49 -0
- package/dist/modes/rpc/jsonl.js.map +1 -0
- package/dist/modes/rpc/rpc-client.d.ts +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +8 -11
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +130 -87
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +3 -4
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts +4 -0
- package/dist/package-manager-cli.d.ts.map +1 -0
- package/dist/package-manager-cli.js +234 -0
- package/dist/package-manager-cli.js.map +1 -0
- package/dist/utils/child-process.d.ts +11 -0
- package/dist/utils/child-process.d.ts.map +1 -0
- package/dist/utils/child-process.js +78 -0
- package/dist/utils/child-process.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +94 -11
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +27 -16
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -0
- package/dist/utils/exif-orientation.js +158 -0
- package/dist/utils/exif-orientation.js.map +1 -0
- package/dist/utils/git.d.ts +5 -1
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +14 -3
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +5 -1
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-resize.d.ts +5 -5
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +51 -95
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/paths.d.ts +7 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +19 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +67 -22
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +6 -2
- package/docs/custom-provider.md +57 -9
- package/docs/development.md +3 -1
- package/docs/extensions.md +437 -67
- package/docs/json.md +5 -2
- package/docs/keybindings.md +108 -107
- package/docs/models.md +50 -2
- package/docs/packages.md +17 -10
- package/docs/prompt-templates.md +6 -6
- package/docs/providers.md +10 -1
- package/docs/rpc.md +78 -18
- package/docs/sdk.md +261 -96
- package/docs/settings.md +28 -3
- package/docs/skills.md +9 -4
- package/docs/terminal-setup.md +39 -3
- package/docs/tmux.md +61 -0
- package/docs/tree.md +15 -3
- package/docs/tui.md +2 -2
- package/examples/extensions/README.md +3 -0
- package/examples/extensions/antigravity-image-gen.ts +12 -7
- package/examples/extensions/built-in-tool-renderer.ts +246 -0
- package/examples/extensions/commands.ts +3 -3
- package/examples/extensions/custom-compaction.ts +17 -4
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +2 -2
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/doom-overlay/doom/build.sh +2 -2
- package/examples/extensions/dynamic-tools.ts +74 -0
- package/examples/extensions/handoff.ts +5 -2
- package/examples/extensions/hello.ts +18 -17
- package/examples/extensions/hidden-thinking-label.ts +53 -0
- package/examples/extensions/minimal-mode.ts +14 -14
- package/examples/extensions/overlay-qa-tests.ts +468 -1
- package/examples/extensions/preset.ts +2 -3
- package/examples/extensions/provider-payload.ts +14 -0
- package/examples/extensions/qna.ts +5 -2
- package/examples/extensions/question.ts +2 -2
- package/examples/extensions/questionnaire.ts +2 -2
- package/examples/extensions/rpc-demo.ts +3 -9
- package/examples/extensions/sandbox/index.ts +6 -3
- package/examples/extensions/status-line.ts +0 -8
- package/examples/extensions/subagent/README.md +4 -4
- package/examples/extensions/subagent/agents.ts +2 -3
- package/examples/extensions/subagent/index.ts +30 -8
- package/examples/extensions/summarize.ts +15 -4
- package/examples/extensions/todo.ts +2 -4
- package/examples/extensions/tool-override.ts +10 -9
- package/examples/extensions/tools.ts +0 -5
- package/examples/extensions/trigger-compact.ts +11 -1
- package/examples/extensions/truncated-tool.ts +8 -5
- package/examples/extensions/widget-placement.ts +4 -12
- package/examples/extensions/with-deps/index.ts +1 -5
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/02-custom-model.ts +2 -2
- package/examples/sdk/04-skills.ts +8 -2
- package/examples/sdk/08-prompt-templates.ts +4 -3
- package/examples/sdk/09-api-keys-and-oauth.ts +5 -5
- package/examples/sdk/10-settings.ts +13 -0
- package/examples/sdk/12-full-control.ts +2 -3
- package/examples/sdk/13-session-runtime.ts +67 -0
- package/examples/sdk/README.md +10 -7
- package/package.json +98 -94
- /package/examples/extensions/subagent/{commands → prompts}/implement-and-review.md +0 -0
- /package/examples/extensions/subagent/{commands → prompts}/implement.md +0 -0
- /package/examples/extensions/subagent/{commands → prompts}/scout-and-plan.md +0 -0
package/docs/extensions.md
CHANGED
|
@@ -37,6 +37,7 @@ See [examples/extensions/](../examples/extensions/) for working implementations.
|
|
|
37
37
|
- [Extension Styles](#extension-styles)
|
|
38
38
|
- [Events](#events)
|
|
39
39
|
- [Lifecycle Overview](#lifecycle-overview)
|
|
40
|
+
- [Resource Events](#resource-events)
|
|
40
41
|
- [Session Events](#session-events)
|
|
41
42
|
- [Agent Events](#agent-events)
|
|
42
43
|
- [Tool Events](#tool-events)
|
|
@@ -227,7 +228,8 @@ Run `npm install` in the extension directory, then imports from `node_modules/`
|
|
|
227
228
|
```
|
|
228
229
|
pi starts
|
|
229
230
|
│
|
|
230
|
-
|
|
231
|
+
├─► session_start { reason: "startup" }
|
|
232
|
+
└─► resources_discover { reason: "startup" }
|
|
231
233
|
│
|
|
232
234
|
▼
|
|
233
235
|
user sends prompt ─────────────────────────────────────────┐
|
|
@@ -237,16 +239,20 @@ user sends prompt ────────────────────
|
|
|
237
239
|
├─► (skill/template expansion if not handled) │
|
|
238
240
|
├─► before_agent_start (can inject message, modify system prompt)
|
|
239
241
|
├─► agent_start │
|
|
242
|
+
├─► message_start / message_update / message_end │
|
|
240
243
|
│ │
|
|
241
244
|
│ ┌─── turn (repeats while LLM calls tools) ───┐ │
|
|
242
245
|
│ │ │ │
|
|
243
246
|
│ ├─► turn_start │ │
|
|
244
247
|
│ ├─► context (can modify messages) │ │
|
|
248
|
+
│ ├─► before_provider_request (can inspect or replace payload)
|
|
245
249
|
│ │ │ │
|
|
246
250
|
│ │ LLM responds, may call tools: │ │
|
|
251
|
+
│ │ ├─► tool_execution_start │ │
|
|
247
252
|
│ │ ├─► tool_call (can block) │ │
|
|
248
|
-
│ │
|
|
249
|
-
│ │
|
|
253
|
+
│ │ ├─► tool_execution_update │ │
|
|
254
|
+
│ │ ├─► tool_result (can modify) │ │
|
|
255
|
+
│ │ └─► tool_execution_end │ │
|
|
250
256
|
│ │ │ │
|
|
251
257
|
│ └─► turn_end │ │
|
|
252
258
|
│ │
|
|
@@ -256,11 +262,15 @@ user sends another prompt ◄─────────────────
|
|
|
256
262
|
|
|
257
263
|
/new (new session) or /resume (switch session)
|
|
258
264
|
├─► session_before_switch (can cancel)
|
|
259
|
-
|
|
265
|
+
├─► session_shutdown
|
|
266
|
+
├─► session_start { reason: "new" | "resume", previousSessionFile? }
|
|
267
|
+
└─► resources_discover { reason: "startup" }
|
|
260
268
|
|
|
261
269
|
/fork
|
|
262
270
|
├─► session_before_fork (can cancel)
|
|
263
|
-
|
|
271
|
+
├─► session_shutdown
|
|
272
|
+
├─► session_start { reason: "fork", previousSessionFile }
|
|
273
|
+
└─► resources_discover { reason: "startup" }
|
|
264
274
|
|
|
265
275
|
/compact or auto-compaction
|
|
266
276
|
├─► session_before_compact (can cancel or customize)
|
|
@@ -277,23 +287,44 @@ exit (Ctrl+C, Ctrl+D)
|
|
|
277
287
|
└─► session_shutdown
|
|
278
288
|
```
|
|
279
289
|
|
|
290
|
+
### Resource Events
|
|
291
|
+
|
|
292
|
+
#### resources_discover
|
|
293
|
+
|
|
294
|
+
Fired after `session_start` so extensions can contribute additional skill, prompt, and theme paths.
|
|
295
|
+
The startup path uses `reason: "startup"`. Reload uses `reason: "reload"`.
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
pi.on("resources_discover", async (event, _ctx) => {
|
|
299
|
+
// event.cwd - current working directory
|
|
300
|
+
// event.reason - "startup" | "reload"
|
|
301
|
+
return {
|
|
302
|
+
skillPaths: ["/path/to/skills"],
|
|
303
|
+
promptPaths: ["/path/to/prompts"],
|
|
304
|
+
themePaths: ["/path/to/themes"],
|
|
305
|
+
};
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
280
309
|
### Session Events
|
|
281
310
|
|
|
282
311
|
See [session.md](session.md) for session storage internals and the SessionManager API.
|
|
283
312
|
|
|
284
313
|
#### session_start
|
|
285
314
|
|
|
286
|
-
Fired
|
|
315
|
+
Fired when a session is started, loaded, or reloaded.
|
|
287
316
|
|
|
288
317
|
```typescript
|
|
289
|
-
pi.on("session_start", async (
|
|
318
|
+
pi.on("session_start", async (event, ctx) => {
|
|
319
|
+
// event.reason - "startup" | "reload" | "new" | "resume" | "fork"
|
|
320
|
+
// event.previousSessionFile - present for "new", "resume", and "fork"
|
|
290
321
|
ctx.ui.notify(`Session: ${ctx.sessionManager.getSessionFile() ?? "ephemeral"}`, "info");
|
|
291
322
|
});
|
|
292
323
|
```
|
|
293
324
|
|
|
294
|
-
#### session_before_switch
|
|
325
|
+
#### session_before_switch
|
|
295
326
|
|
|
296
|
-
Fired
|
|
327
|
+
Fired before starting a new session (`/new`) or switching sessions (`/resume`).
|
|
297
328
|
|
|
298
329
|
```typescript
|
|
299
330
|
pi.on("session_before_switch", async (event, ctx) => {
|
|
@@ -305,14 +336,12 @@ pi.on("session_before_switch", async (event, ctx) => {
|
|
|
305
336
|
if (!ok) return { cancel: true };
|
|
306
337
|
}
|
|
307
338
|
});
|
|
308
|
-
|
|
309
|
-
pi.on("session_switch", async (event, ctx) => {
|
|
310
|
-
// event.reason - "new" or "resume"
|
|
311
|
-
// event.previousSessionFile - session we came from
|
|
312
|
-
});
|
|
313
339
|
```
|
|
314
340
|
|
|
315
|
-
|
|
341
|
+
After a successful switch or new-session action, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "new" | "resume"` and `previousSessionFile`.
|
|
342
|
+
Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
|
|
343
|
+
|
|
344
|
+
#### session_before_fork
|
|
316
345
|
|
|
317
346
|
Fired when forking via `/fork`.
|
|
318
347
|
|
|
@@ -323,12 +352,11 @@ pi.on("session_before_fork", async (event, ctx) => {
|
|
|
323
352
|
// OR
|
|
324
353
|
return { skipConversationRestore: true }; // Fork but don't rewind messages
|
|
325
354
|
});
|
|
326
|
-
|
|
327
|
-
pi.on("session_fork", async (event, ctx) => {
|
|
328
|
-
// event.previousSessionFile - previous session file
|
|
329
|
-
});
|
|
330
355
|
```
|
|
331
356
|
|
|
357
|
+
After a successful fork, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "fork"` and `previousSessionFile`.
|
|
358
|
+
Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
|
|
359
|
+
|
|
332
360
|
#### session_before_compact / session_compact
|
|
333
361
|
|
|
334
362
|
Fired on compaction. See [compaction.md](compaction.md) for details.
|
|
@@ -434,6 +462,51 @@ pi.on("turn_end", async (event, ctx) => {
|
|
|
434
462
|
});
|
|
435
463
|
```
|
|
436
464
|
|
|
465
|
+
#### message_start / message_update / message_end
|
|
466
|
+
|
|
467
|
+
Fired for message lifecycle updates.
|
|
468
|
+
|
|
469
|
+
- `message_start` and `message_end` fire for user, assistant, and toolResult messages.
|
|
470
|
+
- `message_update` fires for assistant streaming updates.
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
pi.on("message_start", async (event, ctx) => {
|
|
474
|
+
// event.message
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
pi.on("message_update", async (event, ctx) => {
|
|
478
|
+
// event.message
|
|
479
|
+
// event.assistantMessageEvent (token-by-token stream event)
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
pi.on("message_end", async (event, ctx) => {
|
|
483
|
+
// event.message
|
|
484
|
+
});
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
#### tool_execution_start / tool_execution_update / tool_execution_end
|
|
488
|
+
|
|
489
|
+
Fired for tool execution lifecycle updates.
|
|
490
|
+
|
|
491
|
+
In parallel tool mode:
|
|
492
|
+
- `tool_execution_start` is emitted in assistant source order during the preflight phase
|
|
493
|
+
- `tool_execution_update` events may interleave across tools
|
|
494
|
+
- `tool_execution_end` is emitted in assistant source order, matching final tool result message order
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
pi.on("tool_execution_start", async (event, ctx) => {
|
|
498
|
+
// event.toolCallId, event.toolName, event.args
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
pi.on("tool_execution_update", async (event, ctx) => {
|
|
502
|
+
// event.toolCallId, event.toolName, event.args, event.partialResult
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
pi.on("tool_execution_end", async (event, ctx) => {
|
|
506
|
+
// event.toolCallId, event.toolName, event.result, event.isError
|
|
507
|
+
});
|
|
508
|
+
```
|
|
509
|
+
|
|
437
510
|
#### context
|
|
438
511
|
|
|
439
512
|
Fired before each LLM call. Modify messages non-destructively. See [session.md](session.md) for message types.
|
|
@@ -446,6 +519,21 @@ pi.on("context", async (event, ctx) => {
|
|
|
446
519
|
});
|
|
447
520
|
```
|
|
448
521
|
|
|
522
|
+
#### before_provider_request
|
|
523
|
+
|
|
524
|
+
Fired after the provider-specific payload is built, right before the request is sent. Handlers run in extension load order. Returning `undefined` keeps the payload unchanged. Returning any other value replaces the payload for later handlers and for the actual request.
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
528
|
+
console.log(JSON.stringify(event.payload, null, 2));
|
|
529
|
+
|
|
530
|
+
// Optional: replace payload
|
|
531
|
+
// return { ...event.payload, temperature: 0 };
|
|
532
|
+
});
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
This is mainly useful for debugging provider serialization and cache behavior.
|
|
536
|
+
|
|
449
537
|
### Model Events
|
|
450
538
|
|
|
451
539
|
#### model_select
|
|
@@ -473,7 +561,19 @@ Use this to update UI elements (status bars, footers) or perform model-specific
|
|
|
473
561
|
|
|
474
562
|
#### tool_call
|
|
475
563
|
|
|
476
|
-
Fired before tool executes. **Can block.** Use `isToolCallEventType` to narrow and get typed inputs.
|
|
564
|
+
Fired after `tool_execution_start`, before the tool executes. **Can block.** Use `isToolCallEventType` to narrow and get typed inputs.
|
|
565
|
+
|
|
566
|
+
Before `tool_call` runs, pi waits for previously emitted Agent events to finish draining through `AgentSession`. This means `ctx.sessionManager` is up to date through the current assistant tool-calling message.
|
|
567
|
+
|
|
568
|
+
In the default parallel tool execution mode, sibling tool calls from the same assistant message are preflighted sequentially, then executed concurrently. `tool_call` is not guaranteed to see sibling tool results from that same assistant message in `ctx.sessionManager`.
|
|
569
|
+
|
|
570
|
+
`event.input` is mutable. Mutate it in place to patch tool arguments before execution.
|
|
571
|
+
|
|
572
|
+
Behavior guarantees:
|
|
573
|
+
- Mutations to `event.input` affect the actual tool execution
|
|
574
|
+
- Later `tool_call` handlers see mutations made by earlier handlers
|
|
575
|
+
- No re-validation is performed after your mutation
|
|
576
|
+
- Return values from `tool_call` only control blocking via `{ block: true, reason?: string }`
|
|
477
577
|
|
|
478
578
|
```typescript
|
|
479
579
|
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
|
@@ -481,11 +581,13 @@ import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
|
|
481
581
|
pi.on("tool_call", async (event, ctx) => {
|
|
482
582
|
// event.toolName - "bash", "read", "write", "edit", etc.
|
|
483
583
|
// event.toolCallId
|
|
484
|
-
// event.input - tool parameters
|
|
584
|
+
// event.input - tool parameters (mutable)
|
|
485
585
|
|
|
486
586
|
// Built-in tools: no type params needed
|
|
487
587
|
if (isToolCallEventType("bash", event)) {
|
|
488
588
|
// event.input is { command: string; timeout?: number }
|
|
589
|
+
event.input.command = `source ~/.profile\n${event.input.command}`;
|
|
590
|
+
|
|
489
591
|
if (event.input.command.includes("rm -rf")) {
|
|
490
592
|
return { block: true, reason: "Dangerous command" };
|
|
491
593
|
}
|
|
@@ -522,13 +624,15 @@ pi.on("tool_call", (event) => {
|
|
|
522
624
|
|
|
523
625
|
#### tool_result
|
|
524
626
|
|
|
525
|
-
Fired after tool
|
|
627
|
+
Fired after tool execution finishes and before `tool_execution_end` plus the final tool result message events are emitted. **Can modify result.**
|
|
526
628
|
|
|
527
629
|
`tool_result` handlers chain like middleware:
|
|
528
630
|
- Handlers run in extension load order
|
|
529
631
|
- Each handler sees the latest result after previous handler changes
|
|
530
632
|
- Handlers can return partial patches (`content`, `details`, or `isError`); omitted fields keep their current values
|
|
531
633
|
|
|
634
|
+
Use `ctx.signal` for nested async work inside the handler. This lets Esc cancel model calls, `fetch()`, and other abort-aware operations started by the extension.
|
|
635
|
+
|
|
532
636
|
```typescript
|
|
533
637
|
import { isBashToolResult } from "@mariozechner/pi-coding-agent";
|
|
534
638
|
|
|
@@ -540,6 +644,12 @@ pi.on("tool_result", async (event, ctx) => {
|
|
|
540
644
|
// event.details is typed as BashToolDetails
|
|
541
645
|
}
|
|
542
646
|
|
|
647
|
+
const response = await fetch("https://example.com/summarize", {
|
|
648
|
+
method: "POST",
|
|
649
|
+
body: JSON.stringify({ content: event.content }),
|
|
650
|
+
signal: ctx.signal,
|
|
651
|
+
});
|
|
652
|
+
|
|
543
653
|
// Modify result:
|
|
544
654
|
return { content: [...], details: {...}, isError: false };
|
|
545
655
|
});
|
|
@@ -552,6 +662,8 @@ pi.on("tool_result", async (event, ctx) => {
|
|
|
552
662
|
Fired when user executes `!` or `!!` commands. **Can intercept.**
|
|
553
663
|
|
|
554
664
|
```typescript
|
|
665
|
+
import { createLocalBashOperations } from "@mariozechner/pi-coding-agent";
|
|
666
|
+
|
|
555
667
|
pi.on("user_bash", (event, ctx) => {
|
|
556
668
|
// event.command - the bash command
|
|
557
669
|
// event.excludeFromContext - true if !! prefix
|
|
@@ -560,7 +672,17 @@ pi.on("user_bash", (event, ctx) => {
|
|
|
560
672
|
// Option 1: Provide custom operations (e.g., SSH)
|
|
561
673
|
return { operations: remoteBashOps };
|
|
562
674
|
|
|
563
|
-
// Option 2:
|
|
675
|
+
// Option 2: Wrap pi's built-in local bash backend
|
|
676
|
+
const local = createLocalBashOperations();
|
|
677
|
+
return {
|
|
678
|
+
operations: {
|
|
679
|
+
exec(command, cwd, options) {
|
|
680
|
+
return local.exec(`source ~/.profile\n${command}`, cwd, options);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// Option 3: Full replacement - return result directly
|
|
564
686
|
return { result: { output: "...", exitCode: 0, cancelled: false, truncated: false } };
|
|
565
687
|
});
|
|
566
688
|
```
|
|
@@ -615,7 +737,7 @@ Transforms chain across handlers. See [input-transform.ts](../examples/extension
|
|
|
615
737
|
|
|
616
738
|
## ExtensionContext
|
|
617
739
|
|
|
618
|
-
|
|
740
|
+
All handlers receive `ctx: ExtensionContext`.
|
|
619
741
|
|
|
620
742
|
### ctx.ui
|
|
621
743
|
|
|
@@ -633,6 +755,8 @@ Current working directory.
|
|
|
633
755
|
|
|
634
756
|
Read-only access to session state. See [session.md](session.md) for the full SessionManager API and entry types.
|
|
635
757
|
|
|
758
|
+
For `tool_call`, this state is synchronized through the current assistant message before handlers run. In parallel tool execution mode it is still not guaranteed to include sibling tool results from the same assistant message.
|
|
759
|
+
|
|
636
760
|
```typescript
|
|
637
761
|
ctx.sessionManager.getEntries() // All entries
|
|
638
762
|
ctx.sessionManager.getBranch() // Current branch
|
|
@@ -643,6 +767,31 @@ ctx.sessionManager.getLeafId() // Current leaf entry ID
|
|
|
643
767
|
|
|
644
768
|
Access to models and API keys.
|
|
645
769
|
|
|
770
|
+
### ctx.signal
|
|
771
|
+
|
|
772
|
+
The current agent abort signal, or `undefined` when no agent turn is active.
|
|
773
|
+
|
|
774
|
+
Use this for abort-aware nested work started by extension handlers, for example:
|
|
775
|
+
- `fetch(..., { signal: ctx.signal })`
|
|
776
|
+
- model calls that accept `signal`
|
|
777
|
+
- file or process helpers that accept `AbortSignal`
|
|
778
|
+
|
|
779
|
+
`ctx.signal` is typically defined during active turn events such as `tool_call`, `tool_result`, `message_update`, and `turn_end`.
|
|
780
|
+
It is usually `undefined` in idle or non-turn contexts such as session events, extension commands, and shortcuts fired while pi is idle.
|
|
781
|
+
|
|
782
|
+
```typescript
|
|
783
|
+
pi.on("tool_result", async (event, ctx) => {
|
|
784
|
+
const response = await fetch("https://example.com/api", {
|
|
785
|
+
method: "POST",
|
|
786
|
+
body: JSON.stringify(event),
|
|
787
|
+
signal: ctx.signal,
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
const data = await response.json();
|
|
791
|
+
return { details: data };
|
|
792
|
+
});
|
|
793
|
+
```
|
|
794
|
+
|
|
646
795
|
### ctx.isIdle() / ctx.abort() / ctx.hasPendingMessages()
|
|
647
796
|
|
|
648
797
|
Control flow helpers.
|
|
@@ -771,6 +920,38 @@ Options:
|
|
|
771
920
|
- `replaceInstructions`: If true, `customInstructions` replaces the default prompt instead of being appended
|
|
772
921
|
- `label`: Label to attach to the branch summary entry (or target entry if not summarizing)
|
|
773
922
|
|
|
923
|
+
### ctx.switchSession(sessionPath)
|
|
924
|
+
|
|
925
|
+
Switch to a different session file:
|
|
926
|
+
|
|
927
|
+
```typescript
|
|
928
|
+
const result = await ctx.switchSession("/path/to/session.jsonl");
|
|
929
|
+
if (result.cancelled) {
|
|
930
|
+
// An extension cancelled the switch via session_before_switch
|
|
931
|
+
}
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
To discover available sessions, use the static `SessionManager.list()` or `SessionManager.listAll()` methods:
|
|
935
|
+
|
|
936
|
+
```typescript
|
|
937
|
+
import { SessionManager } from "@mariozechner/pi-coding-agent";
|
|
938
|
+
|
|
939
|
+
pi.registerCommand("switch", {
|
|
940
|
+
description: "Switch to another session",
|
|
941
|
+
handler: async (args, ctx) => {
|
|
942
|
+
const sessions = await SessionManager.list(ctx.cwd);
|
|
943
|
+
if (sessions.length === 0) return;
|
|
944
|
+
const choice = await ctx.ui.select(
|
|
945
|
+
"Pick session:",
|
|
946
|
+
sessions.map(s => s.file),
|
|
947
|
+
);
|
|
948
|
+
if (choice) {
|
|
949
|
+
await ctx.switchSession(choice);
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
});
|
|
953
|
+
```
|
|
954
|
+
|
|
774
955
|
### ctx.reload()
|
|
775
956
|
|
|
776
957
|
Run the same reload flow as `/reload`.
|
|
@@ -787,7 +968,7 @@ pi.registerCommand("reload-runtime", {
|
|
|
787
968
|
|
|
788
969
|
Important behavior:
|
|
789
970
|
- `await ctx.reload()` emits `session_shutdown` for the current extension runtime
|
|
790
|
-
- It then reloads resources and emits `session_start`
|
|
971
|
+
- It then reloads resources and emits `session_start` with `reason: "reload"` and `resources_discover` with reason `"reload"`
|
|
791
972
|
- The currently running command handler still continues in the old call frame
|
|
792
973
|
- Code after `await ctx.reload()` still runs from the pre-reload version
|
|
793
974
|
- Code after `await ctx.reload()` must not assume old in-memory extension state is still valid
|
|
@@ -837,6 +1018,14 @@ Subscribe to events. See [Events](#events) for event types and return values.
|
|
|
837
1018
|
|
|
838
1019
|
Register a custom tool callable by the LLM. See [Custom Tools](#custom-tools) for full details.
|
|
839
1020
|
|
|
1021
|
+
`pi.registerTool()` works both during extension load and after startup. You can call it inside `session_start`, command handlers, or other event handlers. New tools are refreshed immediately in the same session, so they appear in `pi.getAllTools()` and are callable by the LLM without `/reload`.
|
|
1022
|
+
|
|
1023
|
+
Use `pi.setActiveTools()` to enable or disable tools (including dynamically added tools) at runtime.
|
|
1024
|
+
|
|
1025
|
+
Use `promptSnippet` to opt a custom tool into a one-line entry in `Available tools`, and `promptGuidelines` to append tool-specific bullets to the default `Guidelines` section when the tool is active.
|
|
1026
|
+
|
|
1027
|
+
See [dynamic-tools.ts](../examples/extensions/dynamic-tools.ts) for a full example.
|
|
1028
|
+
|
|
840
1029
|
```typescript
|
|
841
1030
|
import { Type } from "@sinclair/typebox";
|
|
842
1031
|
import { StringEnum } from "@mariozechner/pi-ai";
|
|
@@ -845,10 +1034,18 @@ pi.registerTool({
|
|
|
845
1034
|
name: "my_tool",
|
|
846
1035
|
label: "My Tool",
|
|
847
1036
|
description: "What this tool does",
|
|
1037
|
+
promptSnippet: "Summarize or transform text according to action",
|
|
1038
|
+
promptGuidelines: ["Use this tool when the user asks to summarize previously generated text."],
|
|
848
1039
|
parameters: Type.Object({
|
|
849
1040
|
action: StringEnum(["list", "add"] as const),
|
|
850
1041
|
text: Type.Optional(Type.String()),
|
|
851
1042
|
}),
|
|
1043
|
+
prepareArguments(args) {
|
|
1044
|
+
// Optional compatibility shim. Runs before schema validation.
|
|
1045
|
+
// Return the current schema shape, for example to fold legacy fields
|
|
1046
|
+
// into the modern parameter object.
|
|
1047
|
+
return args;
|
|
1048
|
+
},
|
|
852
1049
|
|
|
853
1050
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
854
1051
|
// Stream progress
|
|
@@ -861,8 +1058,8 @@ pi.registerTool({
|
|
|
861
1058
|
},
|
|
862
1059
|
|
|
863
1060
|
// Optional: Custom rendering
|
|
864
|
-
renderCall(args, theme) { ... },
|
|
865
|
-
renderResult(result, options, theme) { ... },
|
|
1061
|
+
renderCall(args, theme, context) { ... },
|
|
1062
|
+
renderResult(result, options, theme, context) { ... },
|
|
866
1063
|
});
|
|
867
1064
|
```
|
|
868
1065
|
|
|
@@ -884,7 +1081,7 @@ pi.sendMessage({
|
|
|
884
1081
|
|
|
885
1082
|
**Options:**
|
|
886
1083
|
- `deliverAs` - Delivery mode:
|
|
887
|
-
- `"steer"` (default) -
|
|
1084
|
+
- `"steer"` (default) - Queues the message while streaming. Delivered after the current assistant turn finishes executing its tool calls, before the next LLM call.
|
|
888
1085
|
- `"followUp"` - Waits for agent to finish. Delivered only when agent has no more tool calls.
|
|
889
1086
|
- `"nextTurn"` - Queued for next user prompt. Does not interrupt or trigger anything.
|
|
890
1087
|
- `triggerTurn: true` - If agent is idle, trigger an LLM response immediately. Only applies to `"steer"` and `"followUp"` modes (ignored for `"nextTurn"`).
|
|
@@ -910,7 +1107,7 @@ pi.sendUserMessage("And then summarize", { deliverAs: "followUp" });
|
|
|
910
1107
|
|
|
911
1108
|
**Options:**
|
|
912
1109
|
- `deliverAs` - Required when agent is streaming:
|
|
913
|
-
- `"steer"` -
|
|
1110
|
+
- `"steer"` - Queues the message for delivery after the current assistant turn finishes executing its tool calls
|
|
914
1111
|
- `"followUp"` - Waits for agent to finish all tools
|
|
915
1112
|
|
|
916
1113
|
When not streaming, the message is sent immediately and triggers a new turn. When streaming without `deliverAs`, throws an error.
|
|
@@ -974,6 +1171,8 @@ Labels persist in the session and survive restarts. Use them to mark important p
|
|
|
974
1171
|
|
|
975
1172
|
Register a command.
|
|
976
1173
|
|
|
1174
|
+
If multiple extensions register the same command name, pi keeps them all and assigns numeric invocation suffixes in load order, for example `/review:1` and `/review:2`.
|
|
1175
|
+
|
|
977
1176
|
```typescript
|
|
978
1177
|
pi.registerCommand("stats", {
|
|
979
1178
|
description: "Show session statistics",
|
|
@@ -1011,20 +1210,28 @@ The list matches the RPC `get_commands` ordering: extensions first, then templat
|
|
|
1011
1210
|
```typescript
|
|
1012
1211
|
const commands = pi.getCommands();
|
|
1013
1212
|
const bySource = commands.filter((command) => command.source === "extension");
|
|
1213
|
+
const userScoped = commands.filter((command) => command.sourceInfo.scope === "user");
|
|
1014
1214
|
```
|
|
1015
1215
|
|
|
1016
1216
|
Each entry has this shape:
|
|
1017
1217
|
|
|
1018
1218
|
```typescript
|
|
1019
1219
|
{
|
|
1020
|
-
name: string; //
|
|
1220
|
+
name: string; // Invokable command name without the leading slash. May be suffixed like "review:1"
|
|
1021
1221
|
description?: string;
|
|
1022
1222
|
source: "extension" | "prompt" | "skill";
|
|
1023
|
-
|
|
1024
|
-
|
|
1223
|
+
sourceInfo: {
|
|
1224
|
+
path: string;
|
|
1225
|
+
source: string;
|
|
1226
|
+
scope: "user" | "project" | "temporary";
|
|
1227
|
+
origin: "package" | "top-level";
|
|
1228
|
+
baseDir?: string;
|
|
1229
|
+
};
|
|
1025
1230
|
}
|
|
1026
1231
|
```
|
|
1027
1232
|
|
|
1233
|
+
Use `sourceInfo` as the canonical provenance field. Do not infer ownership from command names or from ad hoc path parsing.
|
|
1234
|
+
|
|
1028
1235
|
Built-in interactive commands (like `/model` and `/settings`) are not included here. They are handled only in interactive
|
|
1029
1236
|
mode and would not execute if sent via `prompt`.
|
|
1030
1237
|
|
|
@@ -1073,15 +1280,30 @@ const result = await pi.exec("git", ["status"], { signal, timeout: 5000 });
|
|
|
1073
1280
|
|
|
1074
1281
|
### pi.getActiveTools() / pi.getAllTools() / pi.setActiveTools(names)
|
|
1075
1282
|
|
|
1076
|
-
Manage active tools.
|
|
1283
|
+
Manage active tools. This works for both built-in tools and dynamically registered tools.
|
|
1077
1284
|
|
|
1078
1285
|
```typescript
|
|
1079
|
-
const active = pi.getActiveTools();
|
|
1080
|
-
const all = pi.getAllTools();
|
|
1081
|
-
|
|
1286
|
+
const active = pi.getActiveTools();
|
|
1287
|
+
const all = pi.getAllTools();
|
|
1288
|
+
// [{
|
|
1289
|
+
// name: "read",
|
|
1290
|
+
// description: "Read file contents...",
|
|
1291
|
+
// parameters: ...,
|
|
1292
|
+
// sourceInfo: { path: "<builtin:read>", source: "builtin", scope: "temporary", origin: "top-level" }
|
|
1293
|
+
// }, ...]
|
|
1294
|
+
const names = all.map(t => t.name);
|
|
1295
|
+
const builtinTools = all.filter((t) => t.sourceInfo.source === "builtin");
|
|
1296
|
+
const extensionTools = all.filter((t) => t.sourceInfo.source !== "builtin" && t.sourceInfo.source !== "sdk");
|
|
1082
1297
|
pi.setActiveTools(["read", "bash"]); // Switch to read-only
|
|
1083
1298
|
```
|
|
1084
1299
|
|
|
1300
|
+
`pi.getAllTools()` returns `name`, `description`, `parameters`, and `sourceInfo`.
|
|
1301
|
+
|
|
1302
|
+
Typical `sourceInfo.source` values:
|
|
1303
|
+
- `builtin` for built-in tools
|
|
1304
|
+
- `sdk` for tools passed via `createAgentSession({ customTools })`
|
|
1305
|
+
- extension source metadata for tools registered by extensions
|
|
1306
|
+
|
|
1085
1307
|
### pi.setModel(model)
|
|
1086
1308
|
|
|
1087
1309
|
Set the current model. Returns `false` if no API key is available for the model. See [models.md](models.md) for configuring custom models.
|
|
@@ -1118,6 +1340,8 @@ pi.events.emit("my:event", { ... });
|
|
|
1118
1340
|
|
|
1119
1341
|
Register or override a model provider dynamically. Useful for proxies, custom endpoints, or team-wide model configurations.
|
|
1120
1342
|
|
|
1343
|
+
Calls made during the extension factory function are queued and applied once the runner initialises. Calls made after that — for example from a command handler following a user setup flow — take effect immediately without requiring a `/reload`.
|
|
1344
|
+
|
|
1121
1345
|
```typescript
|
|
1122
1346
|
// Register a new provider with custom models
|
|
1123
1347
|
pi.registerProvider("my-proxy", {
|
|
@@ -1178,6 +1402,21 @@ pi.registerProvider("corporate-ai", {
|
|
|
1178
1402
|
|
|
1179
1403
|
See [custom-provider.md](custom-provider.md) for advanced topics: custom streaming APIs, OAuth details, model definition reference.
|
|
1180
1404
|
|
|
1405
|
+
### pi.unregisterProvider(name)
|
|
1406
|
+
|
|
1407
|
+
Remove a previously registered provider and its models. Built-in models that were overridden by the provider are restored. Has no effect if the provider was not registered.
|
|
1408
|
+
|
|
1409
|
+
Like `registerProvider`, this takes effect immediately when called after the initial load phase, so a `/reload` is not required.
|
|
1410
|
+
|
|
1411
|
+
```typescript
|
|
1412
|
+
pi.registerCommand("my-setup-teardown", {
|
|
1413
|
+
description: "Remove the custom proxy provider",
|
|
1414
|
+
handler: async (_args, _ctx) => {
|
|
1415
|
+
pi.unregisterProvider("my-proxy");
|
|
1416
|
+
},
|
|
1417
|
+
});
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1181
1420
|
## State Management
|
|
1182
1421
|
|
|
1183
1422
|
Extensions with state should store it in tool result `details` for proper branching support:
|
|
@@ -1216,8 +1455,42 @@ export default function (pi: ExtensionAPI) {
|
|
|
1216
1455
|
|
|
1217
1456
|
Register tools the LLM can call via `pi.registerTool()`. Tools appear in the system prompt and can have custom rendering.
|
|
1218
1457
|
|
|
1458
|
+
Use `promptSnippet` for a short one-line entry in the `Available tools` section in the default system prompt. If omitted, custom tools are left out of that section.
|
|
1459
|
+
|
|
1460
|
+
Use `promptGuidelines` to add tool-specific bullets to the default system prompt `Guidelines` section. These bullets are included only while the tool is active (for example, after `pi.setActiveTools([...])`).
|
|
1461
|
+
|
|
1219
1462
|
Note: Some models are idiots and include the @ prefix in tool path arguments. Built-in tools strip a leading @ before resolving paths. If your custom tool accepts a path, normalize a leading @ as well.
|
|
1220
1463
|
|
|
1464
|
+
If your custom tool mutates files, use `withFileMutationQueue()` so it participates in the same per-file queue as built-in `edit` and `write`. This matters because tool calls run in parallel by default. Without the queue, two tools can read the same old file contents, compute different updates, and then whichever write lands last overwrites the other.
|
|
1465
|
+
|
|
1466
|
+
Example failure case: your custom tool edits `foo.ts` while built-in `edit` also changes `foo.ts` in the same assistant turn. If your tool does not participate in the queue, both can read the original `foo.ts`, apply separate changes, and one of those changes is lost.
|
|
1467
|
+
|
|
1468
|
+
Pass the real target file path to `withFileMutationQueue()`, not the raw user argument. Resolve it to an absolute path first, relative to `ctx.cwd` or your tool's working directory. For existing files, the helper canonicalizes through `realpath()`, so symlink aliases for the same file share one queue. For new files, it falls back to the resolved absolute path because there is nothing to `realpath()` yet.
|
|
1469
|
+
|
|
1470
|
+
Queue the entire mutation window on that target path. That includes read-modify-write logic, not just the final write.
|
|
1471
|
+
|
|
1472
|
+
```typescript
|
|
1473
|
+
import { withFileMutationQueue } from "@mariozechner/pi-coding-agent";
|
|
1474
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1475
|
+
import { dirname, resolve } from "node:path";
|
|
1476
|
+
|
|
1477
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1478
|
+
const absolutePath = resolve(ctx.cwd, params.path);
|
|
1479
|
+
|
|
1480
|
+
return withFileMutationQueue(absolutePath, async () => {
|
|
1481
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
1482
|
+
const current = await readFile(absolutePath, "utf8");
|
|
1483
|
+
const next = current.replace(params.oldText, params.newText);
|
|
1484
|
+
await writeFile(absolutePath, next, "utf8");
|
|
1485
|
+
|
|
1486
|
+
return {
|
|
1487
|
+
content: [{ type: "text", text: `Updated ${params.path}` }],
|
|
1488
|
+
details: {},
|
|
1489
|
+
};
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
```
|
|
1493
|
+
|
|
1221
1494
|
### Tool Definition
|
|
1222
1495
|
|
|
1223
1496
|
```typescript
|
|
@@ -1229,10 +1502,22 @@ pi.registerTool({
|
|
|
1229
1502
|
name: "my_tool",
|
|
1230
1503
|
label: "My Tool",
|
|
1231
1504
|
description: "What this tool does (shown to LLM)",
|
|
1505
|
+
promptSnippet: "List or add items in the project todo list",
|
|
1506
|
+
promptGuidelines: [
|
|
1507
|
+
"Use this tool for todo planning instead of direct file edits when the user asks for a task list."
|
|
1508
|
+
],
|
|
1232
1509
|
parameters: Type.Object({
|
|
1233
1510
|
action: StringEnum(["list", "add"] as const), // Use StringEnum for Google compatibility
|
|
1234
1511
|
text: Type.Optional(Type.String()),
|
|
1235
1512
|
}),
|
|
1513
|
+
prepareArguments(args) {
|
|
1514
|
+
if (!args || typeof args !== "object") return args;
|
|
1515
|
+
const input = args as { action?: string; oldAction?: string };
|
|
1516
|
+
if (typeof input.oldAction === "string" && input.action === undefined) {
|
|
1517
|
+
return { ...input, action: input.oldAction };
|
|
1518
|
+
}
|
|
1519
|
+
return args;
|
|
1520
|
+
},
|
|
1236
1521
|
|
|
1237
1522
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
1238
1523
|
// Check for cancellation
|
|
@@ -1257,13 +1542,72 @@ pi.registerTool({
|
|
|
1257
1542
|
},
|
|
1258
1543
|
|
|
1259
1544
|
// Optional: Custom rendering
|
|
1260
|
-
renderCall(args, theme) { ... },
|
|
1261
|
-
renderResult(result, options, theme) { ... },
|
|
1545
|
+
renderCall(args, theme, context) { ... },
|
|
1546
|
+
renderResult(result, options, theme, context) { ... },
|
|
1262
1547
|
});
|
|
1263
1548
|
```
|
|
1264
1549
|
|
|
1550
|
+
**Signaling errors:** To mark a tool execution as failed (sets `isError: true` on the result and reports it to the LLM), throw an error from `execute`. Returning a value never sets the error flag regardless of what properties you include in the return object.
|
|
1551
|
+
|
|
1552
|
+
```typescript
|
|
1553
|
+
// Correct: throw to signal an error
|
|
1554
|
+
async execute(toolCallId, params) {
|
|
1555
|
+
if (!isValid(params.input)) {
|
|
1556
|
+
throw new Error(`Invalid input: ${params.input}`);
|
|
1557
|
+
}
|
|
1558
|
+
return { content: [{ type: "text", text: "OK" }], details: {} };
|
|
1559
|
+
}
|
|
1560
|
+
```
|
|
1561
|
+
|
|
1265
1562
|
**Important:** Use `StringEnum` from `@mariozechner/pi-ai` for string enums. `Type.Union`/`Type.Literal` doesn't work with Google's API.
|
|
1266
1563
|
|
|
1564
|
+
**Argument preparation:** `prepareArguments(args)` is optional. If defined, it runs before schema validation and before `execute()`. Use it to mimic an older accepted input shape when pi resumes an older session whose stored tool call arguments no longer match the current schema. Return the object you want validated against `parameters`. Keep the public schema strict. Do not add deprecated compatibility fields to `parameters` just to keep old resumed sessions working.
|
|
1565
|
+
|
|
1566
|
+
Example: an older session may contain an `edit` tool call with top-level `oldText` and `newText`, while the current schema only accepts `edits: [{ oldText, newText }]`.
|
|
1567
|
+
|
|
1568
|
+
```typescript
|
|
1569
|
+
pi.registerTool({
|
|
1570
|
+
name: "edit",
|
|
1571
|
+
label: "Edit",
|
|
1572
|
+
description: "Edit a single file using exact text replacement",
|
|
1573
|
+
parameters: Type.Object({
|
|
1574
|
+
path: Type.String(),
|
|
1575
|
+
edits: Type.Array(
|
|
1576
|
+
Type.Object({
|
|
1577
|
+
oldText: Type.String(),
|
|
1578
|
+
newText: Type.String(),
|
|
1579
|
+
}),
|
|
1580
|
+
),
|
|
1581
|
+
}),
|
|
1582
|
+
prepareArguments(args) {
|
|
1583
|
+
if (!args || typeof args !== "object") return args;
|
|
1584
|
+
|
|
1585
|
+
const input = args as {
|
|
1586
|
+
path?: string;
|
|
1587
|
+
edits?: Array<{ oldText: string; newText: string }>;
|
|
1588
|
+
oldText?: unknown;
|
|
1589
|
+
newText?: unknown;
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
if (typeof input.oldText !== "string" || typeof input.newText !== "string") {
|
|
1593
|
+
return args;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
return {
|
|
1597
|
+
...input,
|
|
1598
|
+
edits: [...(input.edits ?? []), { oldText: input.oldText, newText: input.newText }],
|
|
1599
|
+
};
|
|
1600
|
+
},
|
|
1601
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
1602
|
+
// params now matches the current schema
|
|
1603
|
+
return {
|
|
1604
|
+
content: [{ type: "text", text: `Applying ${params.edits.length} edit block(s)` }],
|
|
1605
|
+
details: {},
|
|
1606
|
+
};
|
|
1607
|
+
},
|
|
1608
|
+
});
|
|
1609
|
+
```
|
|
1610
|
+
|
|
1267
1611
|
### Overriding Built-in Tools
|
|
1268
1612
|
|
|
1269
1613
|
Extensions can override built-in tools (`read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`) by registering a tool with the same name. Interactive mode displays a warning when this happens.
|
|
@@ -1281,7 +1625,9 @@ pi --no-tools -e ./my-extension.ts
|
|
|
1281
1625
|
|
|
1282
1626
|
See [examples/extensions/tool-override.ts](../examples/extensions/tool-override.ts) for a complete example that overrides `read` with logging and access control.
|
|
1283
1627
|
|
|
1284
|
-
**Rendering:** If your override
|
|
1628
|
+
**Rendering:** Built-in renderer inheritance is resolved per slot. Execution override and rendering override are independent. If your override omits `renderCall`, the built-in `renderCall` is used. If your override omits `renderResult`, the built-in `renderResult` is used. If your override omits both, the built-in renderer is used automatically (syntax highlighting, diffs, etc.). This lets you wrap built-in tools for logging or access control without reimplementing the UI.
|
|
1629
|
+
|
|
1630
|
+
**Prompt metadata:** `promptSnippet` and `promptGuidelines` are not inherited from the built-in tool. If your override should keep those prompt instructions, define them on the override explicitly.
|
|
1285
1631
|
|
|
1286
1632
|
**Your implementation must match the exact result shape**, including the `details` type. The UI and session logic depend on these shapes for rendering and state tracking.
|
|
1287
1633
|
|
|
@@ -1325,6 +1671,8 @@ pi.registerTool({
|
|
|
1325
1671
|
|
|
1326
1672
|
**Operations interfaces:** `ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, `LsOperations`, `GrepOperations`, `FindOperations`
|
|
1327
1673
|
|
|
1674
|
+
For `user_bash`, extensions can reuse pi's local shell backend via `createLocalBashOperations()` instead of reimplementing local process spawning, shell resolution, and process-tree termination.
|
|
1675
|
+
|
|
1328
1676
|
The bash tool also supports a spawn hook to adjust the command, cwd, or env before execution:
|
|
1329
1677
|
|
|
1330
1678
|
```typescript
|
|
@@ -1413,44 +1761,52 @@ export default function (pi: ExtensionAPI) {
|
|
|
1413
1761
|
|
|
1414
1762
|
### Custom Rendering
|
|
1415
1763
|
|
|
1416
|
-
Tools can provide `renderCall` and `renderResult` for custom TUI display. See [tui.md](tui.md) for the full component API and [tool-execution.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/modes/interactive/components/tool-execution.ts) for how
|
|
1764
|
+
Tools can provide `renderCall` and `renderResult` for custom TUI display. See [tui.md](tui.md) for the full component API and [tool-execution.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/modes/interactive/components/tool-execution.ts) for how tool rows are composed.
|
|
1765
|
+
|
|
1766
|
+
Tool output is wrapped in a `Box` that handles padding and background. A defined `renderCall` or `renderResult` must return a `Component`. If a slot renderer is not defined, `tool-execution.ts` uses fallback rendering for that slot.
|
|
1767
|
+
|
|
1768
|
+
`renderCall` and `renderResult` each receive a `context` object with:
|
|
1769
|
+
- `args` - the current tool call arguments
|
|
1770
|
+
- `state` - shared row-local state across `renderCall` and `renderResult`
|
|
1771
|
+
- `lastComponent` - the previously returned component for that slot, if any
|
|
1772
|
+
- `invalidate()` - request a rerender of this tool row
|
|
1773
|
+
- `toolCallId`, `cwd`, `executionStarted`, `argsComplete`, `isPartial`, `expanded`, `showImages`, `isError`
|
|
1417
1774
|
|
|
1418
|
-
|
|
1775
|
+
Use `context.state` for cross-slot shared state. Keep slot-local caches on the returned component instance when you want to reuse and mutate the same component across renders.
|
|
1419
1776
|
|
|
1420
1777
|
#### renderCall
|
|
1421
1778
|
|
|
1422
|
-
Renders the tool call
|
|
1779
|
+
Renders the tool call or header:
|
|
1423
1780
|
|
|
1424
1781
|
```typescript
|
|
1425
1782
|
import { Text } from "@mariozechner/pi-tui";
|
|
1426
1783
|
|
|
1427
|
-
renderCall(args, theme) {
|
|
1428
|
-
|
|
1429
|
-
|
|
1784
|
+
renderCall(args, theme, context) {
|
|
1785
|
+
const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
|
|
1786
|
+
let content = theme.fg("toolTitle", theme.bold("my_tool "));
|
|
1787
|
+
content += theme.fg("muted", args.action);
|
|
1430
1788
|
if (args.text) {
|
|
1431
|
-
|
|
1789
|
+
content += " " + theme.fg("dim", `"${args.text}"`);
|
|
1432
1790
|
}
|
|
1433
|
-
|
|
1791
|
+
text.setText(content);
|
|
1792
|
+
return text;
|
|
1434
1793
|
}
|
|
1435
1794
|
```
|
|
1436
1795
|
|
|
1437
1796
|
#### renderResult
|
|
1438
1797
|
|
|
1439
|
-
Renders the tool result:
|
|
1798
|
+
Renders the tool result or output:
|
|
1440
1799
|
|
|
1441
1800
|
```typescript
|
|
1442
|
-
renderResult(result, { expanded, isPartial }, theme) {
|
|
1443
|
-
// Handle streaming
|
|
1801
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
1444
1802
|
if (isPartial) {
|
|
1445
1803
|
return new Text(theme.fg("warning", "Processing..."), 0, 0);
|
|
1446
1804
|
}
|
|
1447
1805
|
|
|
1448
|
-
// Handle errors
|
|
1449
1806
|
if (result.details?.error) {
|
|
1450
1807
|
return new Text(theme.fg("error", `Error: ${result.details.error}`), 0, 0);
|
|
1451
1808
|
}
|
|
1452
1809
|
|
|
1453
|
-
// Normal result - support expanded view (Ctrl+O)
|
|
1454
1810
|
let text = theme.fg("success", "✓ Done");
|
|
1455
1811
|
if (expanded && result.details?.items) {
|
|
1456
1812
|
for (const item of result.details.items) {
|
|
@@ -1461,40 +1817,52 @@ renderResult(result, { expanded, isPartial }, theme) {
|
|
|
1461
1817
|
}
|
|
1462
1818
|
```
|
|
1463
1819
|
|
|
1820
|
+
If a slot intentionally has no visible content, return an empty `Component` such as an empty `Container`.
|
|
1821
|
+
|
|
1464
1822
|
#### Keybinding Hints
|
|
1465
1823
|
|
|
1466
|
-
Use `keyHint()` to display keybinding hints that respect
|
|
1824
|
+
Use `keyHint()` to display keybinding hints that respect the active keybinding configuration:
|
|
1467
1825
|
|
|
1468
1826
|
```typescript
|
|
1469
1827
|
import { keyHint } from "@mariozechner/pi-coding-agent";
|
|
1470
1828
|
|
|
1471
|
-
renderResult(result, { expanded }, theme) {
|
|
1829
|
+
renderResult(result, { expanded }, theme, context) {
|
|
1472
1830
|
let text = theme.fg("success", "✓ Done");
|
|
1473
1831
|
if (!expanded) {
|
|
1474
|
-
text += ` (${keyHint("
|
|
1832
|
+
text += ` (${keyHint("app.tools.expand", "to expand")})`;
|
|
1475
1833
|
}
|
|
1476
1834
|
return new Text(text, 0, 0);
|
|
1477
1835
|
}
|
|
1478
1836
|
```
|
|
1479
1837
|
|
|
1480
1838
|
Available functions:
|
|
1481
|
-
- `keyHint(
|
|
1482
|
-
- `
|
|
1483
|
-
- `editorKey(action)` - Get raw key string for editor action
|
|
1839
|
+
- `keyHint(keybinding, description)` - Formats a configured keybinding id such as `"app.tools.expand"` or `"tui.select.confirm"`
|
|
1840
|
+
- `keyText(keybinding)` - Returns the raw configured key text for a keybinding id
|
|
1484
1841
|
- `rawKeyHint(key, description)` - Format a raw key string
|
|
1485
1842
|
|
|
1843
|
+
Use namespaced keybinding ids:
|
|
1844
|
+
- Coding-agent ids use the `app.*` namespace, for example `app.tools.expand`, `app.editor.external`, `app.session.rename`
|
|
1845
|
+
- Shared TUI ids use the `tui.*` namespace, for example `tui.select.confirm`, `tui.select.cancel`, `tui.input.tab`
|
|
1846
|
+
|
|
1847
|
+
For the exhaustive list of keybinding ids and defaults, see [keybindings.md](keybindings.md). `keybindings.json` uses those same namespaced ids.
|
|
1848
|
+
|
|
1849
|
+
Custom editors and `ctx.ui.custom()` components receive `keybindings: KeybindingsManager` as an injected argument. They should use that injected manager directly instead of calling `getKeybindings()` or `setKeybindings()`.
|
|
1850
|
+
|
|
1486
1851
|
#### Best Practices
|
|
1487
1852
|
|
|
1488
|
-
- Use `Text` with padding `(0, 0)
|
|
1489
|
-
- Use `\n` for multi-line content
|
|
1490
|
-
- Handle `isPartial` for streaming progress
|
|
1491
|
-
- Support `expanded` for detail on demand
|
|
1492
|
-
- Keep default view compact
|
|
1853
|
+
- Use `Text` with padding `(0, 0)`. The Box handles padding.
|
|
1854
|
+
- Use `\n` for multi-line content.
|
|
1855
|
+
- Handle `isPartial` for streaming progress.
|
|
1856
|
+
- Support `expanded` for detail on demand.
|
|
1857
|
+
- Keep default view compact.
|
|
1858
|
+
- Read `context.args` in `renderResult` instead of copying args into `context.state`.
|
|
1859
|
+
- Use `context.state` only for data that must be shared across call and result slots.
|
|
1860
|
+
- Reuse `context.lastComponent` when the same component instance can be updated in place.
|
|
1493
1861
|
|
|
1494
1862
|
#### Fallback
|
|
1495
1863
|
|
|
1496
|
-
If
|
|
1497
|
-
- `renderCall`: Shows tool name
|
|
1864
|
+
If a slot renderer is not defined or throws:
|
|
1865
|
+
- `renderCall`: Shows the tool name
|
|
1498
1866
|
- `renderResult`: Shows raw text from `content`
|
|
1499
1867
|
|
|
1500
1868
|
## Custom UI
|
|
@@ -1802,7 +2170,7 @@ const highlighted = highlightCode(code, lang, theme);
|
|
|
1802
2170
|
|
|
1803
2171
|
- Extension errors are logged, agent continues
|
|
1804
2172
|
- `tool_call` errors block the tool (fail-safe)
|
|
1805
|
-
- Tool `execute` errors
|
|
2173
|
+
- Tool `execute` errors must be signaled by throwing; the thrown error is caught, reported to the LLM with `isError: true`, and execution continues
|
|
1806
2174
|
|
|
1807
2175
|
## Mode Behavior
|
|
1808
2176
|
|
|
@@ -1826,6 +2194,7 @@ All examples in [examples/extensions/](../examples/extensions/).
|
|
|
1826
2194
|
| `question.ts` | Tool with user interaction | `registerTool`, `ui.select` |
|
|
1827
2195
|
| `questionnaire.ts` | Multi-step wizard tool | `registerTool`, `ui.custom` |
|
|
1828
2196
|
| `todo.ts` | Stateful tool with persistence | `registerTool`, `appendEntry`, `renderResult`, session events |
|
|
2197
|
+
| `dynamic-tools.ts` | Register tools after startup and during commands | `registerTool`, `session_start`, `registerCommand` |
|
|
1829
2198
|
| `truncated-tool.ts` | Output truncation example | `registerTool`, `truncateHead` |
|
|
1830
2199
|
| `tool-override.ts` | Override built-in read tool | `registerTool` (same name as built-in) |
|
|
1831
2200
|
| **Commands** |||
|
|
@@ -1843,13 +2212,14 @@ All examples in [examples/extensions/](../examples/extensions/).
|
|
|
1843
2212
|
| `dirty-repo-guard.ts` | Warn on dirty git repo | `on("session_before_*")`, `exec` |
|
|
1844
2213
|
| `input-transform.ts` | Transform user input | `on("input")` |
|
|
1845
2214
|
| `model-status.ts` | React to model changes | `on("model_select")`, `setStatus` |
|
|
2215
|
+
| `provider-payload.ts` | Inspect or patch provider payloads | `on("before_provider_request")` |
|
|
1846
2216
|
| `system-prompt-header.ts` | Display system prompt info | `on("agent_start")`, `getSystemPrompt` |
|
|
1847
2217
|
| `claude-rules.ts` | Load rules from files | `on("session_start")`, `on("before_agent_start")` |
|
|
1848
2218
|
| `file-trigger.ts` | File watcher triggers messages | `sendMessage` |
|
|
1849
2219
|
| **Compaction & Sessions** |||
|
|
1850
2220
|
| `custom-compaction.ts` | Custom compaction summary | `on("session_before_compact")` |
|
|
1851
2221
|
| `trigger-compact.ts` | Trigger compaction manually | `compact()` |
|
|
1852
|
-
| `git-checkpoint.ts` | Git stash on turns | `on("
|
|
2222
|
+
| `git-checkpoint.ts` | Git stash on turns | `on("turn_start")`, `on("session_before_fork")`, `exec` |
|
|
1853
2223
|
| `auto-commit-on-exit.ts` | Commit on shutdown | `on("session_shutdown")`, `exec` |
|
|
1854
2224
|
| **UI Components** |||
|
|
1855
2225
|
| `status-line.ts` | Footer status indicator | `setStatus`, session events |
|