feat: ShellDispatcher, ExternalTool wrappers, and pluggable tool registry#1848
feat: ShellDispatcher, ExternalTool wrappers, and pluggable tool registry#1848aboimpinto wants to merge 27 commits into
Conversation
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
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
|
Independent review: Big PR (+2576/-533) with three legitimate ideas — Security concerns (pluggable tools):
Architectural / merge:
v0.8.48 (PR #2256): 5 conflicts — overlaps |
|
This PR is closed, and I will make smaller PRs for this feature. |
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