Skip to content

Commit 259caff

Browse files
chaliyclaude
andauthored
Rebrand Python bindings as native Python package (#440)
## Summary - Rewrote `crates/bashkit-python/README.md` to present bashkit as a native Python package (tiktoken-style), not "bindings" - Moved Rust/Bashkit core mention to a "How it works" section at the bottom - Replaced all "bindings" references with "package" across specs, AGENTS.md, CI workflows, Cargo.toml, lib.rs, and tests ## Test plan - [ ] Verify README renders correctly on PyPI/GitHub - [ ] `just check` passes (no functional changes) - [ ] CI green --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8afde72 commit 259caff

File tree

13 files changed

+96
-71
lines changed

13 files changed

+96
-71
lines changed

.github/workflows/publish-python.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# PyPI publishing workflow for bashkit Python bindings
1+
# PyPI publishing workflow for bashkit Python package
22
# Builds pre-compiled wheels for all major platforms and publishes to PyPI.
33
# Triggered alongside publish.yml on GitHub Release or manual dispatch.
44
# Adapted from https://github.com/pydantic/monty CI wheel-building pattern.

.github/workflows/python.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# CI for bashkit Python bindings
1+
# CI for bashkit Python package
22
# Builds the native extension via maturin and runs pytest on each PR.
33
# Complements publish-python.yml (release-only) with per-PR validation.
44

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Fix root cause. Unsure: read more code; if stuck, ask w/ short options. Unrecogn
3939
| 011-python-builtin | Embedded Python via Monty, security, resource limits |
4040
| 012-eval | LLM evaluation harness, dataset format, scoring |
4141
| 012-maintenance | Pre-release maintenance checklist |
42-
| 013-python-package | Python bindings, PyPI wheels, platform matrix |
42+
| 013-python-package | Python package, PyPI wheels, platform matrix |
4343
| 014-scripted-tool-orchestration | Compose ToolDef+callback pairs into OrchestratorTool via bash scripts |
4444

4545
### Documentation
@@ -88,7 +88,7 @@ just pre-pr # Pre-PR checks
8888

8989
### Python
9090

91-
- Python bindings in `crates/bashkit-python/`
91+
- Python package in `crates/bashkit-python/`
9292
- Linter/formatter: `ruff` (config in `pyproject.toml`)
9393
- `ruff check crates/bashkit-python` and `ruff format --check crates/bashkit-python`
9494
- Tests: `pytest crates/bashkit-python/tests/ -v` (requires `maturin develop` first)

crates/bashkit-python/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Python bindings for Bashkit
1+
# Bashkit Python package
22
# Exposes Bashkit as a Python module via PyO3
33

44
[package]
@@ -8,7 +8,7 @@ edition.workspace = true
88
license.workspace = true
99
authors.workspace = true
1010
repository.workspace = true
11-
description = "Python bindings for Bashkit virtual bash interpreter"
11+
description = "A sandboxed bash interpreter for AI agents"
1212

1313
[lib]
1414
crate-type = ["cdylib"]
@@ -19,7 +19,7 @@ doc = false # Python extension, no Rust docs needed
1919
# Bashkit core
2020
bashkit = { path = "../bashkit", features = ["scripted_tool"] }
2121

22-
# PyO3 for Python bindings
22+
# PyO3 native extension
2323
pyo3 = { workspace = true }
2424
pyo3-async-runtimes = { workspace = true }
2525

crates/bashkit-python/README.md

Lines changed: 76 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
1-
# Bashkit Python Bindings
1+
# Bashkit
22

3-
Python bindings for [Bashkit](https://github.com/everruns/bashkit) - a virtual bash interpreter for AI agents.
3+
A sandboxed bash interpreter for AI agents.
4+
5+
```python
6+
from bashkit import BashTool
7+
8+
tool = BashTool()
9+
result = tool.execute_sync("echo 'Hello, World!'")
10+
print(result.stdout) # Hello, World!
11+
```
412

513
## Features
614

7-
- **Sandboxed, in-process execution**: All commands run in isolation with a virtual filesystem
8-
- **68+ built-in commands**: echo, cat, grep, sed, awk, jq, curl, find, and more
9-
- **Full bash syntax**: Variables, pipelines, redirects, loops, functions, arrays
10-
- **Resource limits**: Protect against infinite loops and runaway scripts
11-
- **LangChain integration**: Ready-to-use tool for LangChain agents
15+
- **Sandboxed execution** — all commands run in-process with a virtual filesystem, no containers needed
16+
- **68+ built-in commands** echo, cat, grep, sed, awk, jq, curl, find, and more
17+
- **Full bash syntax** — variables, pipelines, redirects, loops, functions, arrays
18+
- **Resource limits** — protect against infinite loops and runaway scripts
19+
- **Framework integrations** — LangChain, PydanticAI, and Deep Agents
1220

1321
## Installation
1422

1523
```bash
16-
# From PyPI (when published)
1724
pip install bashkit
1825

19-
# With LangChain support
26+
# With framework support
2027
pip install 'bashkit[langchain]'
21-
22-
# From source
23-
pip install maturin
24-
maturin develop
28+
pip install 'bashkit[pydantic-ai]'
2529
```
2630

27-
## Quick Start
31+
## Usage
32+
33+
### Async
2834

2935
```python
3036
import asyncio
@@ -51,29 +57,17 @@ async def main():
5157
asyncio.run(main())
5258
```
5359

54-
## LangChain Integration
60+
### Sync
5561

5662
```python
57-
from bashkit.langchain import create_bash_tool
58-
from langchain.agents import create_agent
59-
60-
# Create tool
61-
bash_tool = create_bash_tool()
62-
63-
# Create agent
64-
agent = create_agent(
65-
model="claude-sonnet-4-20250514",
66-
tools=[bash_tool],
67-
system_prompt="You are a helpful assistant with bash skills."
68-
)
63+
from bashkit import BashTool
6964

70-
# Run
71-
result = agent.invoke({
72-
"messages": [{"role": "user", "content": "Create a file with today's date"}]
73-
})
65+
tool = BashTool()
66+
result = tool.execute_sync("echo 'Hello!'")
67+
print(result.stdout)
7468
```
7569

76-
## Configuration
70+
### Configuration
7771

7872
```python
7973
tool = BashTool(
@@ -84,35 +78,68 @@ tool = BashTool(
8478
)
8579
```
8680

87-
## Synchronous API
81+
### Scripted Tool Orchestration
82+
83+
Compose multiple tools into a single bash-scriptable interface:
8884

8985
```python
90-
from bashkit import BashTool
86+
from bashkit import ScriptedTool
9187

92-
tool = BashTool()
93-
result = tool.execute_sync("echo 'Hello!'")
94-
print(result.stdout)
88+
tool = ScriptedTool("api")
89+
tool.add_tool("greet", "Greet a user", callback=lambda p, s=None: f"hello {p.get('name', 'world')}")
90+
result = tool.execute_sync("greet --name Alice")
91+
print(result.stdout) # hello Alice
92+
```
93+
94+
### LangChain
95+
96+
```python
97+
from bashkit.langchain import create_bash_tool
98+
99+
bash_tool = create_bash_tool()
100+
# Use with any LangChain agent
101+
```
102+
103+
### PydanticAI
104+
105+
```python
106+
from bashkit.pydantic_ai import create_bash_tool
107+
108+
bash_tool = create_bash_tool()
109+
# Use with any PydanticAI agent
95110
```
96111

97112
## API Reference
98113

99114
### BashTool
100115

101-
- `execute(commands: str) -> ExecResult`: Execute commands asynchronously
102-
- `execute_sync(commands: str) -> ExecResult`: Execute commands synchronously
103-
- `description() -> str`: Get tool description
104-
- `help() -> str`: Get LLM documentation
105-
- `input_schema() -> str`: Get JSON input schema
106-
- `output_schema() -> str`: Get JSON output schema
116+
- `execute(commands: str) -> ExecResult` — execute commands asynchronously
117+
- `execute_sync(commands: str) -> ExecResult` — execute commands synchronously
118+
- `reset()` — reset interpreter state
119+
- `description() -> str` — tool description for LLM integration
120+
- `help() -> str` — detailed documentation
121+
- `input_schema() -> str` — JSON input schema
122+
- `output_schema() -> str` — JSON output schema
107123

108124
### ExecResult
109125

110-
- `stdout: str`: Standard output
111-
- `stderr: str`: Standard error
112-
- `exit_code: int`: Exit code (0 = success)
113-
- `error: Optional[str]`: Error message if execution failed
114-
- `success: bool`: True if exit_code == 0
115-
- `to_dict() -> dict`: Convert to dictionary
126+
- `stdout: str` — standard output
127+
- `stderr: str` — standard error
128+
- `exit_code: int` — exit code (0 = success)
129+
- `error: Optional[str]` — error message if execution failed
130+
- `success: bool` — True if exit_code == 0
131+
- `to_dict() -> dict` — convert to dictionary
132+
133+
### ScriptedTool
134+
135+
- `add_tool(name, description, callback, schema=None)` — register a tool
136+
- `execute(script: str) -> ExecResult` — execute script asynchronously
137+
- `execute_sync(script: str) -> ExecResult` — execute script synchronously
138+
- `env(key: str, value: str)` — set environment variable
139+
140+
## How it works
141+
142+
Bashkit is built on top of [Bashkit core](https://github.com/everruns/bashkit), a bash interpreter written in Rust. The Python package provides a native extension for fast, sandboxed execution without spawning subprocesses or containers.
116143

117144
## License
118145

crates/bashkit-python/bashkit/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
"""
2-
Bashkit Python Bindings
3-
4-
A sandboxed bash interpreter for AI agents with virtual filesystem.
2+
Bashkit — a sandboxed bash interpreter for AI agents.
53
64
Example:
75
>>> from bashkit import BashTool
86
>>> tool = BashTool()
9-
>>> result = await tool.execute("echo 'Hello, World!'")
7+
>>> result = tool.execute_sync("echo 'Hello, World!'")
108
>>> print(result.stdout)
119
Hello, World!
1210

crates/bashkit-python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "maturin"
55
[project]
66
name = "bashkit"
77
dynamic = ["version"]
8-
description = "Python bindings for Bashkit - a sandboxed bash interpreter for AI agents"
8+
description = "A sandboxed bash interpreter for AI agents"
99
readme = "README.md"
1010
license = { text = "MIT" }
1111
requires-python = ">=3.9"

crates/bashkit-python/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Python bindings for Bashkit
1+
//! Bashkit Python package
22
//!
33
//! Exposes the Bash interpreter and ScriptedTool as Python classes for use in
44
//! AI agent frameworks. BashTool provides stateful execution (filesystem persists

crates/bashkit-python/tests/test_bashkit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Tests for bashkit Python bindings."""
1+
"""Tests for bashkit Python package."""
22

33
import json
44

specs/001-architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ impl BashTool {
118118
### Single crate vs workspace
119119
Rejected single crate because:
120120
- CLI binary would bloat the library
121-
- Future Python bindings need separate crate
121+
- Python package needs separate crate
122122
- Cleaner separation of concerns
123123

124124
### Sync vs async filesystem

0 commit comments

Comments
 (0)