feat(scripted_tool): add ScriptedTool for multi-tool bash composition#213
Merged
feat(scripted_tool): add ScriptedTool for multi-tool bash composition#213
Conversation
Compose multiple CallableTools into a single Tool that accepts bash scripts. Each CallableTool becomes a builtin command, so an LLM can orchestrate N tools in one call using pipes, variables, loops, and jq. - CallableTool trait: sync sub-tool interface (name, description, call) - OrchestratorTool: implements Tool, creates fresh Bash per execute() - Arc-based sharing: tools survive across multiple execute() calls - OrchestratorToolBuilder: fluent API for configuration - Auto-generated system_prompt/help with tool command docs - 19 unit tests + doctests covering pipelines, loops, error handling - Example: e-commerce API demo (get_user, list_orders, get_inventory) - Spec: specs/014-scripted-tool-orchestration.md https://claude.ai/code/session_01UcTwLqLrhbCFAuL26Fcm5W
…ack API - Rename scripted_tool.rs → orchestrator/ module (mod.rs + execute.rs) - Replace CallableTool trait with ToolDef (OpenAPI-style: name, description, input_schema) + closure callback — two arguments to builder.tool() - Gate entire module behind `orchestrator` feature flag - System prompt now renders input_schema when present - Rename example to orchestrator.rs - Update spec 014 and AGENTS.md https://claude.ai/code/session_01UcTwLqLrhbCFAuL26Fcm5W
- Module: orchestrator/ → scripted_tool/ - Structs: OrchestratorTool → ScriptedTool, OrchestratorToolBuilder → ScriptedToolBuilder - Feature flag: orchestrator → scripted_tool - Example: orchestrator.rs → scripted_tool.rs - Update spec 014 https://claude.ai/code/session_01UcTwLqLrhbCFAuL26Fcm5W
…lue flag parsing
Callbacks now receive ToolArgs {params, stdin} instead of (&[String], Option<&str>).
The adapter parses --key value / --key=value flags from bash args, coercing types
per the tool's input_schema (integer, number, boolean, string). ToolArgs provides
convenience accessors: param_str, param_i64, param_f64, param_bool.
https://claude.ai/code/session_01UcTwLqLrhbCFAuL26Fcm5W
Clippy disallows unwrap() even in test code. Replace all instances with expect() providing descriptive panic messages. https://claude.ai/code/session_01MARrisPkoUhn1L7j3rirwU
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.
Summary
Adds
ScriptedTool, a newToolimplementation that lets LLMs orchestrate multiple sub-tools in a single bash script. Each registered tool becomes a builtin command, enabling composition via pipes, variables, loops, and conditionals without multiple round-trips.Also removes the PydanticAI integration (unused) and reverts for-loop brace/glob expansion from #211 that was causing issues.
New:
ScriptedToolmoduleNew module
crates/bashkit/src/scripted_tool/gated by thescripted_toolfeature flag:ToolDef+ToolArgs: OpenAPI-style tool definitions with optional JSON Schema for input parameters and type coercion--key valueand--key=valueCLI args into typed JSON objects per the tool's schema (integer, number, boolean, string)ToolBuiltinAdapter: WrapsToolCallback(Arc-based) as aBuiltin, so tool callbacks can be invoked as bash commandsScriptedToolBuilder: Fluent API to register tools with definitions + closures, set execution limits, and configure env varsTooltrait impl: Provides introspection (help, system_prompt, schemas) and execution (single + status-callback variants)crates/bashkit/examples/scripted_tool.rs— e-commerce API with get_user, list_orders, get_inventory, create_discount toolsspecs/014-scripted-tool-orchestration.mdDesign
execute()call creates a freshBashinstance with all tool callbacks registered as builtins viaArc::cloneToolDef.input_schemaRemoved: PydanticAI integration
crates/bashkit-python/bashkit/pydantic_ai.pyandexamples/pydantic_ai_bash_agent.pypydantic-aioptional dependency frompyproject.tomlspecs/013-python-package.mdto reflect removalReverted: for-loop brace/glob expansion
Interpreter::execute_forback to plain field expansion (removed brace + glob expansion added in fix(interpreter): apply brace/glob expansion in for-loop word list #211)arrays.test.sh,brace-expansion.test.sh,globs.test.shspecs/009-implementation-status.mdFiles changed
src/scripted_tool/{mod,execute}.rs,Cargo.toml,lib.rsexamples/scripted_tool.rsspecs/014-scripted-tool-orchestration.mdbashkit-python/bashkit/{__init__,pydantic_ai}.py,pyproject.toml,examples/pydantic_ai_bash_agent.pysrc/interpreter/mod.rs, test spec casesAGENTS.md,specs/009-implementation-status.md,specs/013-python-package.md