Persistent just-py-bash shell capability and toolset wrapper for Pydantic AI
Project description
pydantic-ai-just-bash
pydantic-ai-just-bash is a small extension package for Pydantic AI that gives an agent a persistent just-py-bash shell.
The extension exposes a bash tool plus helper tools like bash_list_tools, bash_search_tools, and bash_describe_tool, and binds wrapped Pydantic AI tools into that shell as commands.
By default, wrapped tools stay directly visible to the model as normal Pydantic AI tools. If you want a shell-only interface, set expose_wrapped_tools=False.
It is designed to let an agent mix shell workflows with normal Pydantic AI tool calls while keeping a long-lived virtual filesystem for the lifetime of the agent run.
Why use it?
- Give an agent a persistent bash-like environment without a real OS shell
- Expose normal Pydantic AI tools as shell commands
- Keep deferred tools hidden until the model discovers them with shell-side search
- Reuse
just-py-bashsession controls likefiles,env,cwd,fs,python, andjavascript - Return structured execution results instead of raw subprocess plumbing
Install
Requires Python 3.11+
| Use case | Command |
|---|---|
Install from PyPI with uv |
uv add pydantic-ai-just-bash |
| Install spec/YAML support too | uv add 'pydantic-ai-just-bash[spec]' |
Install from PyPI with pip |
pip install pydantic-ai-just-bash |
| Install a cloned checkout in editable mode | pip install -e . |
Install name: pydantic-ai-just-bash
Import name: pydantic_ai_just_bash
This package only adds the shell capability. Install whatever Pydantic AI model/provider extras you already need separately.
Quick start
from pydantic_ai import Agent
from pydantic_ai_just_bash import JustBash
agent = Agent(
'openai:gpt-5.2',
capabilities=[JustBash(python=True)],
)
@agent.tool_plain
def get_weather(city: str) -> str:
"""Get the weather for a city."""
return f'Sunny in {city}'
@agent.tool_plain(defer_loading=True)
def stock_lookup(symbol: str) -> str:
"""Look up a stock price."""
return f'{symbol}=150.00'
Inside bash, the model can do things like:
bash_list_tools
get_weather Paris
printf 'draft note' > note.txt
cat note.txt
bash_search_tools stock
stock_lookup AAPL
The shell session and virtual filesystem persist across bash calls for the lifetime of the agent run.
API overview
JustBash
Use JustBash as an agent capability. It wraps the assembled toolset and injects a bash tool plus bash helper tools.
from pydantic_ai_just_bash import JustBash
capability = JustBash(
tool_name='bash',
command_prefix='',
helper_prefix='bash_',
python=True,
)
Common configuration knobs include:
tool_namecommand_prefixhelper_prefixexposed_toolsexpose_wrapped_toolsinstructionshelp_flag_namerename_help_argumentfiles,env,cwd,fs,execution_limitspython,javascript,commands,network,process_infonode_command,js_entry,package_json
JustBashToolset
Use JustBashToolset if you want to wrap a specific toolset directly instead of using a capability.
from pydantic_ai import Agent, FunctionToolset
from pydantic_ai_just_bash import JustBashToolset
base = FunctionToolset()
@base.tool_plain
def echo(text: str) -> str:
return text
agent = Agent('openai:gpt-5.2', toolsets=[JustBashToolset(base)])
Result models
The package exports structured result models for the public helper tools:
BashExecutionResultBashListToolsResultBashSearchToolsResultBashDescribeToolResultBashCommandInfo
Agent specs and YAML
JustBash can be loaded from Agent.from_spec(...) and Agent.from_file(...).
model: test
capabilities:
- JustBash:
tool_name: bash
helper_prefix: bash_
python: true
files:
/workspace/seed.txt: hello from spec
/workspace/lazy.txt:
provider: lazy content from spec
Use the spec extra if you want the package to carry the YAML/spec dependencies itself:
uv add 'pydantic-ai-just-bash[spec]'
Spec-safe configuration surface
The current spec-safe surface includes the public JustBash fields, including:
- shell naming/config fields like
tool_name,command_prefix,helper_prefix,instructions,help_flag_name, andexpose_wrapped_tools - runtime/session fields like
env,cwd,execution_limits,python,javascript,commands,network,process_info,node_command,js_entry, andpackage_json - filesystem configuration via
filesandfs - spec-friendly file values such as plain text/bytes,
FileInit, andLazyFilewith a staticprovidervalue - string-based
rename_help_argumentvalues
Python-only configuration surface
Some configuration remains Python-only because it depends on runtime callables rather than JSON/YAML data.
The current Python-only surface includes:
- callable
exposed_toolsselectors - callback-based lazy file providers, either passed directly or wrapped in
LazyFile(...) - callable
rename_help_argumentvalues
For example:
from just_bash import LazyFile
from pydantic_ai_just_bash import JustBash
cap = JustBash(
files={
'/workspace/generated.txt': LazyFile(provider=lambda: 'generated at session start\n'),
},
)
That callable form is supported when you configure JustBash(...) in Python, but it is not spec/YAML-serializable.
Public helper tools
At the agent level, the wrapper exposes:
bashbash_list_toolsbash_search_toolsbash_describe_tool
Inside the shell, the same helper concepts are available as commands:
bash_list_toolsbash_describe_tool <tool-or-command>bash_call_tool <tool-or-command> --json '{...}'bash_search_tools <keywords>
Tool visibility model
Wrapped tools remain directly visible to the model by default, so an agent can either call them normally or use them through bash.
If you want the model to go through the shell interface only, set:
JustBash(expose_wrapped_tools=False)
In shell-only mode, the model still sees bash, bash_list_tools, bash_search_tools, and bash_describe_tool, but the wrapped tools themselves are omitted from the public agent tool list.
Argument binding
Wrapped tools are still validated by Pydantic AI, but the shell adapter tries to behave like a small CLI layer first:
| Form | Example |
|---|---|
| Named flags for simple values | my_tool --a 1 --b 2 |
Booleans with --flag / --no-flag |
my_tool --verbose --no-cache |
| Single positional value for single-argument tools | get_weather Paris |
-- to stop option parsing |
show_value -- --help |
| JSON object escape hatch | my_tool --json '{"a": 1, "b": 2}' |
| JSON via stdin | `echo '{"a": 1, "b": 2}' |
Use shell-style flags for simple scalar values. Prefer --json or --stdin-json for objects, arrays, or anything that becomes awkward to quote safely.
When a command has multiple parameters, bare positional input is rejected with a CLI-style error that points back to --help and --json.
Use --help or -h on a bound command, or bash_describe_tool, to inspect generated command help, rendered signatures, renamed arguments, and JSON schema.
Help flag behavior
By default, shell-exposed commands reserve --help and -h for generated command help.
If a wrapped tool has a real argument named help, wrapper creation fails by default with a configuration error. This makes the collision explicit instead of silently changing command behavior.
You can override that behavior with:
help_flag_name='usage'to reserve a different help flag instead of--helprename_help_argument=...to rename the wrapped tool's conflicting shell argument
For example:
JustBash(rename_help_argument='{tool_name}_{arg_name}')
would expose a tool argument named help as --<tool_name>_help in the shell, and generated help text will explain that rename explicitly.
Behavior notes
- The shell session is persistent for a run, so virtual filesystem changes carry across
bashcalls. - Wrapped tools stay directly visible by default; set
expose_wrapped_tools=Falsefor shell-only mode. - The set of bound commands is captured on first shell use in a run. If wrapped tools change dynamically and you want a fresh command set, call
bash(..., reset_session=True). - Deferred tools are hidden in the shell until discovered with
bash_search_tools. - Shell command failures are formatted as CLI-style stderr messages instead of leaking raw framework internals where possible.
- Direct shell commands return the tool result. If a wrapped tool returns
ToolReturn, the shell uses itsreturn_value.
Development
make install
make all-ci
uv build
Common commands:
| Task | Command |
|---|---|
| Install dev + lint tooling | make install |
| Format code and config | make format |
| Check formatting only | make format-check |
| Lint | make lint |
| Type-check | make typecheck |
| Test | make test |
| Run the full local CI suite | make all-ci |
Project status
This package is still early-stage and the public API may evolve as the shell command model settles. See ROADMAP.md.
Related projects
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pydantic_ai_just_bash-0.2.0.tar.gz.
File metadata
- Download URL: pydantic_ai_just_bash-0.2.0.tar.gz
- Upload date:
- Size: 26.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4b9e880e702ce4e670cdcf3c8ebfa48b38cf3c516b9bd49e4ea5266ae1d9ccea
|
|
| MD5 |
ece0f6c4ad92718cfc54d289bc9260cf
|
|
| BLAKE2b-256 |
2248e933d84bbd119e0ee030f69dec9554122e7c1fcd9ed151af2a8e7a88c150
|
Provenance
The following attestation bundles were made for pydantic_ai_just_bash-0.2.0.tar.gz:
Publisher:
release.yml on nathan-gage/pydantic-ai-just-bash
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantic_ai_just_bash-0.2.0.tar.gz -
Subject digest:
4b9e880e702ce4e670cdcf3c8ebfa48b38cf3c516b9bd49e4ea5266ae1d9ccea - Sigstore transparency entry: 1291034788
- Sigstore integration time:
-
Permalink:
nathan-gage/pydantic-ai-just-bash@8391b626885d19037ac9ca9e36d376472c11022e -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/nathan-gage
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8391b626885d19037ac9ca9e36d376472c11022e -
Trigger Event:
push
-
Statement type:
File details
Details for the file pydantic_ai_just_bash-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pydantic_ai_just_bash-0.2.0-py3-none-any.whl
- Upload date:
- Size: 22.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c6700edf253d65ca5f1f0befe61c6005cc9edd50866ac9907c620d0617bff42
|
|
| MD5 |
432ebf38ee9a759356dfa21f68604418
|
|
| BLAKE2b-256 |
66b70d024c983c275422205f332ae724e2bc7b86877774171e5867dd578cd20e
|
Provenance
The following attestation bundles were made for pydantic_ai_just_bash-0.2.0-py3-none-any.whl:
Publisher:
release.yml on nathan-gage/pydantic-ai-just-bash
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantic_ai_just_bash-0.2.0-py3-none-any.whl -
Subject digest:
0c6700edf253d65ca5f1f0befe61c6005cc9edd50866ac9907c620d0617bff42 - Sigstore transparency entry: 1291034896
- Sigstore integration time:
-
Permalink:
nathan-gage/pydantic-ai-just-bash@8391b626885d19037ac9ca9e36d376472c11022e -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/nathan-gage
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8391b626885d19037ac9ca9e36d376472c11022e -
Trigger Event:
push
-
Statement type: