feat: ShellDispatcher, ExternalTool wrappers, and pluggable tool registry#1848
Open
aboimpinto wants to merge 27 commits into
Open
feat: ShellDispatcher, ExternalTool wrappers, and pluggable tool registry#1848aboimpinto wants to merge 27 commits into
aboimpinto wants to merge 27 commits into
Conversation
Contributor
There was a problem hiding this comment.
Code Review
This pull request introduces a plugin tool system that allows users to register custom scripts and commands as model-visible tools, alongside a new .NET execution runtime. It also adds a unified ExternalTool trait for subprocess management and a ShellDispatcher to handle cross-platform shell execution safely. Several debugging logs were identified in the TUI rendering and event loop logic that should be removed, and a potential bug in script argument handling in the plugin system was highlighted.
329b648 to
3bb6a6a
Compare
3bb6a6a to
4fc533a
Compare
Contributor
Author
This was referenced May 24, 2026
Introduces a central shell abstraction that replaces hardcoded
Command::new("cmd") / Command::new("sh") across the execution path.
- Shell detection at startup (pwsh -> powershell -> cmd -> sh)
- Correct quoting per shell (PowerShell uses -NoProfile -Command)
- Scope guards restore crossterm raw mode on all spawn paths (Hmbown#1690)
- Direct program+args builder for sandbox ExecEnv integration
Files:
- crates/tui/src/shell_dispatcher.rs (new, 12 tests)
- crates/tui/src/main.rs (register module)
- crates/tui/src/eval.rs (exec_shell delegates to dispatcher)
- crates/tui/src/sandbox/mod.rs (CommandSpec::shell uses dispatcher)
- crates/tui/src/tools/shell.rs (raw mode guards on all spawn paths)
Closes Hmbown#1690
Refs Hmbown#1779
… logging - find_exe(): fall back to known install dirs when PATH lookup fails (C:\Program Files\PowerShell\7, System32\WindowsPowerShell\v1.0) - Encoding prefix: use idiomatic [Console]::OutputEncoding for PowerShell instead of cmd.exe chcp 65001 >NUL - Execution logging: write exec via <ShellKind> entries to SHELL_DISPATCHER_LOG when set - System prompt: use ShellDispatcher detection instead of raw $SHELL so model knows it has PowerShell and generates native cmdlets - display_command(): strip PowerShell encoding prefix for display - Add tests for find_exe PATH + known-dir fallback Refs Hmbown#1779
- Fixed sandbox tests to accept sh/bash/zsh on non-Windows - Added cfg_shell_program() helper that uses ShellDispatcher detection - Marked find_exe_finds_cmd_on_path as #[cfg(windows)] - Fixed shell::tests for issue_1691 to accept sh/bash/zsh - Added #[cfg(windows)] to cmd-specific tests
ShellDispatcher may detect pwsh, powershell, or cmd on Windows. Tests now verify the command is passed as the last arg instead of hardcoding cmd.exe with chcp prefix.
…tection Gh, RustC, Cargo, DotNet, and Go resolve() implementations hardcoded a single binary name instead of iterating over Self::candidates(). This broke .cmd detection on Windows for npm/wrapper-installed tools. Aligned all resolve() implementations to the pattern used by Git and TypeScript: iterate over candidates, log the resolved candidate name, and return None only if all probes fail. Only Go is a new tool in this PR; the other four are pre-existing but flagged in code review as the same anti-pattern. Fixed them in a single pass since the pattern is identical.
…lerplate DOTNET and RuntimeTool tool execution dispatch blocks had identical started_at -> await -> ToolCallComplete -> ToolExecOutcome -> continue patterns. Extracted into Engine::emit_tool_outcome() to reduce code duplication. Responds to code review feedback.
…asks.rs Both call sites used tokio::task::spawn_blocking just to run Gh::command() or Git::command(). Switched to Gh::tokio_command() and Git::tokio_command() respectively — these return tokio::process::Command so the output can be awaited directly without spawn_blocking. Pre-existing code improved per boy-scout rule (leave the place better than found it). Responds to code review feedback on PR Hmbown#1845.
…errides, and plugin tools (Hmbown#1847)
4fc533a to
6adc99e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR folds the previously stacked work into a single PR so it can be reviewed and merged as one coherent change.
Supersedes #2008 and #2031.
Summary
Implementation notes
No
[tools]config requiredThe default
~/.deepseek/tools/directory is always scanned. A user can drop a script there with frontmatter and it becomes available as a plugin tool. The[tools]table inconfig.tomlis only needed for custom plugin directory paths, built-in tool overrides, or disabling tools.Script tool flow
~/.deepseek/tools/count-words.mjsdeclares metadata in comment frontmatter: name, description, JSON schema, and approval behavior.ScriptPluginToolin the ToolRegistry.Key design decisions
#,//, and--prefixes so scripts can be written in multiple languages.#!/usr/bin/env nodeby resolvingnodedirectly, which works better on Windows.Paulo Aboim Pinto