Merge pull request 'docs: drop stale bash-first framing' (#24) from docs-bash-first-cleanup into main
test / unit (push) Successful in 17s
test / integration (push) Successful in 1m36s

This commit was merged in pull request #24.
This commit is contained in:
2026-05-25 06:36:05 -04:00
8 changed files with 23 additions and 19 deletions
+6 -4
View File
@@ -5,8 +5,8 @@
claude-bottle spins up an isolated container for running Claude Code with a claude-bottle spins up an isolated container for running Claude Code with a
curated set of skills and env vars. The point is to run Claude with broad curated set of skills and env vars. The point is to run Claude with broad
permissions inside a sandbox, so a misbehaving agent cannot reach the host. permissions inside a sandbox, so a misbehaving agent cannot reach the host.
Bash scripts orchestrate the container lifecycle and the copying of skills A Python CLI (entry point `cli.py`, package `claude_bottle/`) orchestrates
and env vars into it. the container lifecycle and the copying of skills and env vars into it.
## Goals ## Goals
@@ -36,8 +36,10 @@ and env vars into it.
- Product requirement docs live in `docs/prds/`. - Product requirement docs live in `docs/prds/`.
- Research notes live in `docs/research/`. - Research notes live in `docs/research/`.
- Low dependencies by default. The project is bash-first; ask before adding new - Low dependencies by default. The project is Python, stdlib-first (no
tools, runtimes, or package managers. runtime pip dependencies in the package itself; the only language
runtime is the Python 3.13 used by the CLI + sidecars). Ask before
adding new tools, runtimes, or package managers.
- Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/): - Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/):
`<type>[(scope)][!]: <description>`, where `<type>` is one of `feat`, `fix`, `<type>[(scope)][!]: <description>`, where `<type>` is one of `feat`, `fix`,
`docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`.
+2 -2
View File
@@ -407,8 +407,8 @@ cannot smuggle a stolen token through this path.
The proxy binary. Two real options: The proxy binary. Two real options:
- **Python (stdlib)** — `http.server` + `urllib`/`http.client`, - **Python (stdlib)** — `http.server` + `urllib`/`http.client`,
no new pip packages. Matches CLAUDE.md's "bash-first, low-deps" no new pip packages. Matches CLAUDE.md's "Python, stdlib-first,
posture. SSE pass-through is fiddly but doable. low-deps" posture. SSE pass-through is fiddly but doable.
- **Go single binary** — cleaner SSE story, smaller runtime, - **Go single binary** — cleaner SSE story, smaller runtime,
one static binary in a scratch/distroless image. New build one static binary in a scratch/distroless image. New build
dependency. dependency.
+1 -1
View File
@@ -197,7 +197,7 @@ differentiators:
- Bottle / agent split (manifest layer above the isolation primitive). - Bottle / agent split (manifest layer above the isolation primitive).
- gVisor auto-detection on Linux. - gVisor auto-detection on Linux.
Ideas worth considering, without abandoning the bash-first / local-Docker Ideas worth considering, without abandoning the Python-stdlib-first / local-Docker
stance: stance:
1. **Per-use SSH key confirmation** (from litterbox). Even with 1. **Per-use SSH key confirmation** (from litterbox). Even with
+3 -3
View File
@@ -2,7 +2,7 @@
## Question ## Question
Can claude-bottle grow a built-in supervisor — TUI inventory plus PR-feedback routing — without breaking the per-bottle isolation model, and without departing from the bash-first, low-dependency posture? Can claude-bottle grow a built-in supervisor — TUI inventory plus PR-feedback routing — without breaking the per-bottle isolation model, and without departing from the Python-stdlib-first, low-dependency posture?
## Context ## Context
@@ -35,7 +35,7 @@ No new daemons. No new ports. No new credentials. ~100 lines.
Same data as `status`, rendered with auto-refresh and keyboard shortcuts that shell out to the existing `cli.py attach / stop / start` commands. Same data as `status`, rendered with auto-refresh and keyboard shortcuts that shell out to the existing `cli.py attach / stop / start` commands.
Library choice: prefer the stdlib `curses` module to stay bash-first-adjacent; fall back to `rich` or `textual` only if the curses path proves painful. Both `rich` and `textual` are single-purpose, pure-Python deps with no transitive bloat, but they are still new deps and per the project conventions warrant a deliberate decision. Library choice: prefer the stdlib `curses` module to stay stdlib-first; fall back to `rich` or `textual` only if the curses path proves painful. Both `rich` and `textual` are single-purpose, pure-Python deps with no transitive bloat, but they are still new deps and per the project conventions warrant a deliberate decision.
This is the Claude Squad / tmux-agent-status pattern, applied to bottles instead of tmux sessions. The whole category exists *because* a TUI is the lightweight shape that doesn't require what the SPA tier requires. This is the Claude Squad / tmux-agent-status pattern, applied to bottles instead of tmux sessions. The whole category exists *because* a TUI is the lightweight shape that doesn't require what the SPA tier requires.
@@ -85,7 +85,7 @@ A few design defaults worth holding:
## Scope estimate ## Scope estimate
The full `status` / `watch` / `supervise` trio is plausibly ~500 lines of Python on top of the existing CLI, no new runtimes, no new daemons, no new ports, and (with `curses`) no new deps. That fits "Low dependencies by default. The project is bash-first; ask before adding new tools, runtimes, or package managers" without requiring an exception. The full `status` / `watch` / `supervise` trio is plausibly ~500 lines of Python on top of the existing CLI, no new runtimes, no new daemons, no new ports, and (with `curses`) no new deps. That fits "Low dependencies by default. The project is Python, stdlib-first; ask before adding new tools, runtimes, or package managers" without requiring an exception.
Phased: `status` first (purely additive, no design decisions), then `watch` (the design decisions are mostly UX, not architecture), then `supervise` (the only layer that introduces a new behavioral default and warrants a PRD of its own). Phased: `status` first (purely additive, no design decisions), then `watch` (the design decisions are mostly UX, not architecture), then `supervise` (the only layer that introduces a new behavioral default and warrants a PRD of its own).
@@ -25,7 +25,7 @@ manifest merge.
## Other surveyed projects ## Other surveyed projects
- **textcortex/claude-code-sandbox → spritz** — evolved toward - **textcortex/claude-code-sandbox → spritz** — evolved toward
Kubernetes-native multi-agent infra; not bash-first or local-Docker. Kubernetes-native multi-agent infra; not stdlib-first or local-Docker.
Original sandbox repo is archived. Original sandbox repo is archived.
- **trailofbits/claude-code-devcontainer** — devcontainer config for security - **trailofbits/claude-code-devcontainer** — devcontainer config for security
audits; not a general agent launcher. audits; not a general agent launcher.
@@ -58,7 +58,7 @@ None combine:
Worth publishing. Differentiators that matter to the target audience (power Worth publishing. Differentiators that matter to the target audience (power
users running parallel Claude Code sessions with distinct personas/tooling): users running parallel Claude Code sessions with distinct personas/tooling):
- The bash-first, low-dependency design — competitors are npm-based or - The Python-stdlib-first, low-dependency design — competitors are npm-based or
Kubernetes-native. Kubernetes-native.
- Named agents with distinct skills and system prompts, not just language profiles. - Named agents with distinct skills and system prompts, not just language profiles.
- SSH forwarding without key copying. - SSH forwarding without key copying.
@@ -162,7 +162,7 @@ ruamel.yaml).
- **New runtime dependency.** The project today uses zero - **New runtime dependency.** The project today uses zero
third-party Python packages for production code; YAML parsing third-party Python packages for production code; YAML parsing
pulls in PyYAML. (CLAUDE.md: "bash-first, low-deps by default.") pulls in PyYAML. (CLAUDE.md: "Python, stdlib-first; low-deps by default.")
- YAML's footguns: indentation sensitivity, the Norway problem - YAML's footguns: indentation sensitivity, the Norway problem
(`country: NO` → boolean False), implicit type coercion that's (`country: NO` → boolean False), implicit type coercion that's
surprised non-trivial production projects. surprised non-trivial production projects.
+7 -5
View File
@@ -369,7 +369,8 @@ services:
- ALL - ALL
``` ```
In a bash-first project without Docker Compose, the equivalent is: In a project without Docker Compose, the equivalent (shell or Python orchestrator
shelling out to `docker`) is:
```bash ```bash
# create isolated network # create isolated network
@@ -712,10 +713,11 @@ updates ipset without a container restart.
### Tier 1 (v1, implement first): in-container iptables + ipset + dnsmasq ### Tier 1 (v1, implement first): in-container iptables + ipset + dnsmasq
Adopt approach 2a with the dnsmasq complement from IVIJL/devbox. This is the Adopt approach 2a with the dnsmasq complement from IVIJL/devbox. This is the
pattern validated by Anthropic's own devcontainer, is bash-first, adds no new pattern validated by Anthropic's own devcontainer, configures cleanly from
runtime dependencies (iptables and ipset are standard in the base Debian/Ubuntu plain shell + standard system packages, adds no new runtime dependencies
image used by Claude Code; dnsmasq is a single `apt-get install`), and works (iptables and ipset are standard in the base Debian/Ubuntu image used by
on both macOS Docker Desktop and Linux Docker Engine. Claude Code; dnsmasq is a single `apt-get install`), and works on both
macOS Docker Desktop and Linux Docker Engine.
Key PRD scope for this work: Key PRD scope for this work:
+1 -1
View File
@@ -270,7 +270,7 @@ its own scanner. This avoids a second container but introduces the
the proxy run in the same failure domain. the proxy run in the same failure domain.
The sidecar topology is recommended for claude-bottle because it matches The sidecar topology is recommended for claude-bottle because it matches
the existing bash-orchestrated multi-container model (the SSH key agent the existing Python-orchestrated multi-container model (the SSH key agent
already uses a separate process), keeps pipelock outside the agent's kill already uses a separate process), keeps pipelock outside the agent's kill
reach, and avoids the `--best-effort` issue on macOS Docker Desktop. reach, and avoids the `--best-effort` issue on macOS Docker Desktop.