cli-error 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli_error-0.1.0/.claude/scheduled_tasks.lock +1 -0
- cli_error-0.1.0/.flake8 +3 -0
- cli_error-0.1.0/.github/workflows/ci.yml +38 -0
- cli_error-0.1.0/.github/workflows/release.yml +28 -0
- cli_error-0.1.0/.gitignore +26 -0
- cli_error-0.1.0/.mcp.json +12 -0
- cli_error-0.1.0/.sandbox/.env.example +20 -0
- cli_error-0.1.0/.sandbox/README.md +114 -0
- cli_error-0.1.0/.sandbox/base/Dockerfile +42 -0
- cli_error-0.1.0/.sandbox/base/tmux.conf +112 -0
- cli_error-0.1.0/.sandbox/base/zshrc +9 -0
- cli_error-0.1.0/.sandbox/docker-compose.yml +28 -0
- cli_error-0.1.0/.sandbox/run.sh +42 -0
- cli_error-0.1.0/.sandbox/scripts/_lib.sh +23 -0
- cli_error-0.1.0/.sandbox/scripts/attach.sh +6 -0
- cli_error-0.1.0/.sandbox/scripts/build.sh +44 -0
- cli_error-0.1.0/.sandbox/scripts/claude.sh +6 -0
- cli_error-0.1.0/.sandbox/scripts/shell.sh +31 -0
- cli_error-0.1.0/.sandbox/variants/python/Dockerfile +30 -0
- cli_error-0.1.0/.sandbox/variants/python/compose.override.yml +16 -0
- cli_error-0.1.0/.sandbox/worktree_run.sh +4 -0
- cli_error-0.1.0/.sandbox/worktree_shell.sh +4 -0
- cli_error-0.1.0/.tasker/.closed +16 -0
- cli_error-0.1.0/.tasker/.gitignore +4 -0
- cli_error-0.1.0/.tasker/.recent +1 -0
- cli_error-0.1.0/.tasker/archive/.gitkeep +0 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/README.md +61 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t01-rename-package-structure-clierror-cliexit.md +43 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t02-fluent-builder-for-clierror-context.md +41 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t03-render-formatted-error.md +42 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t04-error-handler-errorreporter-context-manager.md +38 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t05-cause-chain-rendering.md +35 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t06-debug-traceback.md +36 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t07-console-creation-with-style-overrides.md +40 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t08-library-documentation-readme.md +36 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t09-deployment-setup-ci-release-workflows.md +36 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t10-unify-markup-construction-template-args.md +47 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t11-refine-detail-single-last-wins.md +44 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t12-revisit-props-markup-first-prop.md +62 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t13-store-detail-as-ready-to.md +33 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t14-handle-empty-message-cause-in.md +53 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t15-reconsider-derive-debug-stderr-console.md +42 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t16-optionally-show-locals-in-debug.md +41 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t17-test-debug-traceback-includes-the.md +32 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t18-add-debug-gated-diagnostic-helpers.md +49 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t19-consolidate-shared-test-helpers-across.md +47 -0
- cli_error-0.1.0/.tasker/s01-extract-repo-skills-error-reporting/s01t20-add-a-safe-post-construction.md +31 -0
- cli_error-0.1.0/CLAUDE.md +24 -0
- cli_error-0.1.0/CONTEXT.md +15 -0
- cli_error-0.1.0/PKG-INFO +6 -0
- cli_error-0.1.0/README.md +287 -0
- cli_error-0.1.0/docs/adr/0001-error-formatting-and-theming.md +72 -0
- cli_error-0.1.0/docs/adr/0002-cli-reporter-output-facade.md +72 -0
- cli_error-0.1.0/docs/agents/dev-loop.md +50 -0
- cli_error-0.1.0/docs/agents/task-tracker.md +53 -0
- cli_error-0.1.0/pyproject.toml +34 -0
- cli_error-0.1.0/src/cli_error/__init__.py +21 -0
- cli_error-0.1.0/src/cli_error/_console.py +40 -0
- cli_error-0.1.0/src/cli_error/_errors.py +92 -0
- cli_error-0.1.0/src/cli_error/_render.py +41 -0
- cli_error-0.1.0/src/cli_error/_reporter.py +108 -0
- cli_error-0.1.0/src/cli_error/py.typed +0 -0
- cli_error-0.1.0/tests/__init__.py +0 -0
- cli_error-0.1.0/tests/test_console.py +144 -0
- cli_error-0.1.0/tests/test_errors.py +279 -0
- cli_error-0.1.0/tests/test_render.py +292 -0
- cli_error-0.1.0/tests/test_reporter.py +355 -0
- cli_error-0.1.0/tox.ini +22 -0
- cli_error-0.1.0/uv.lock +589 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"sessionId":"d6c578d2-0e67-41a3-9ab3-6eb663d5b9d5","pid":25,"procStart":"26355062","acquiredAt":1783286663093}
|
cli_error-0.1.0/.flake8
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: tox
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
env:
|
|
17
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
18
|
+
strategy:
|
|
19
|
+
matrix:
|
|
20
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Set up uv
|
|
26
|
+
uses: astral-sh/setup-uv@v5
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
cache-dependency-glob: uv.lock
|
|
30
|
+
|
|
31
|
+
- name: Install Python ${{ matrix.python-version }}
|
|
32
|
+
run: uv python install ${{ matrix.python-version }}
|
|
33
|
+
|
|
34
|
+
- name: Sync dev dependencies
|
|
35
|
+
run: uv sync --group dev
|
|
36
|
+
|
|
37
|
+
- name: Run tox
|
|
38
|
+
run: uv run tox
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
name: Publish to PyPI
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: Production
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write
|
|
14
|
+
contents: read
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
cache-dependency-glob: uv.lock
|
|
23
|
+
|
|
24
|
+
- name: Build package
|
|
25
|
+
run: uv build
|
|
26
|
+
|
|
27
|
+
- name: Publish to PyPI
|
|
28
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.venv/
|
|
8
|
+
|
|
9
|
+
# Tools
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
|
|
13
|
+
# tox
|
|
14
|
+
.tox/
|
|
15
|
+
|
|
16
|
+
# local configs
|
|
17
|
+
.env
|
|
18
|
+
*.local.json
|
|
19
|
+
.worktrees
|
|
20
|
+
|
|
21
|
+
# packaging / testing extras
|
|
22
|
+
*$py.class
|
|
23
|
+
*.egg
|
|
24
|
+
.eggs/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Host identity — container files will be owned by this UID/GID.
|
|
2
|
+
# Populate with: id -u, id -g, echo $USER
|
|
3
|
+
HOST_UID=1000
|
|
4
|
+
HOST_GID=1000
|
|
5
|
+
USER=yourname
|
|
6
|
+
|
|
7
|
+
# Compose project namespace. Controls per-sandbox variant/volume naming:
|
|
8
|
+
# <COMPOSE_PROJECT_NAME>-<VARIANT>:latest
|
|
9
|
+
COMPOSE_PROJECT_NAME=app-error-sandbox
|
|
10
|
+
|
|
11
|
+
# Base image name. Shared across all sandboxes (independent of
|
|
12
|
+
# COMPOSE_PROJECT_NAME), so rebuilding it once updates every sandbox.
|
|
13
|
+
# Leave this alone unless you deliberately want a private, unshared base.
|
|
14
|
+
# BASE_IMAGE=agent-sandbox-base:latest
|
|
15
|
+
|
|
16
|
+
# Active variant — uncomment exactly ONE.
|
|
17
|
+
VARIANT=python
|
|
18
|
+
|
|
19
|
+
# Language versions (used only by the matching variant).
|
|
20
|
+
PYTHON_VERSION="3.10 3.11 3.12"
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Agent Sandbox
|
|
2
|
+
|
|
3
|
+
A Docker-based environment for running Claude Code with persistent authentication,
|
|
4
|
+
a shared project workspace, and a pluggable variant system for different language stacks.
|
|
5
|
+
|
|
6
|
+
See the top-level [`README.md`](../README.md) for WSL setup and a variants overview.
|
|
7
|
+
|
|
8
|
+
The shared base image ships `uv` and the `mcp-tasker` MCP server, so they're
|
|
9
|
+
available in every variant regardless of language stack.
|
|
10
|
+
|
|
11
|
+
## Layout
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
sandbox/
|
|
15
|
+
.env.example template for .env (copy and edit)
|
|
16
|
+
docker-compose.yml base compose file (variant-agnostic)
|
|
17
|
+
run.sh one-shot: build (if needed) → up → claude
|
|
18
|
+
base/
|
|
19
|
+
Dockerfile generic dev image (ubuntu + tmux/zsh/nvim/node/claude + uv/mcp-tasker)
|
|
20
|
+
zshrc, tmux.conf mounted read-only into the container
|
|
21
|
+
variants/
|
|
22
|
+
python/ Dockerfile + compose.override.yml (uv + poetry)
|
|
23
|
+
typescript/ Dockerfile + compose.override.yml (node TS toolchain)
|
|
24
|
+
php/ Dockerfile + compose.override.yml
|
|
25
|
+
minimal/ Dockerfile + compose.override.yml (no language stack)
|
|
26
|
+
scripts/
|
|
27
|
+
build.sh builds base image, then the active variant
|
|
28
|
+
attach.sh `tmux attach -t main`
|
|
29
|
+
shell.sh plain zsh shell
|
|
30
|
+
claude.sh `claude --dangerously-skip-permissions`
|
|
31
|
+
_lib.sh sourced helper (loads .env, sets COMPOSE_FILE)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## First-time setup
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cd sandbox
|
|
38
|
+
cp .env.example .env
|
|
39
|
+
# Edit .env: set HOST_UID, HOST_GID, USER, COMPOSE_PROJECT_NAME, VARIANT
|
|
40
|
+
./run.sh
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`run.sh` builds the base image + active variant on first invocation, starts
|
|
44
|
+
the container, and drops you into Claude Code. Subsequent runs skip the
|
|
45
|
+
build when the images already exist.
|
|
46
|
+
|
|
47
|
+
Claude credentials live in `~/.claude` / `~/.claude.json` on the host (bind-mounted
|
|
48
|
+
into the container), so authentication persists across container rebuilds.
|
|
49
|
+
|
|
50
|
+
The host's `~/repo-skills` is bind-mounted **read-only** at `~/repo-skills` inside
|
|
51
|
+
the container as a reference source, so the sandbox can consult it without
|
|
52
|
+
modifying it or leaking container-generated files back onto the host.
|
|
53
|
+
|
|
54
|
+
## Daily usage
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cd sandbox
|
|
58
|
+
./run.sh # ensure running, then run claude
|
|
59
|
+
./scripts/attach.sh # attach to the tmux session inside the container
|
|
60
|
+
./scripts/shell.sh # plain zsh shell inside the container
|
|
61
|
+
./scripts/claude.sh # another claude instance in the running container
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The project directory (`..` relative to `sandbox/`) is mounted at `/work` inside
|
|
65
|
+
the container and reflects host changes in real time.
|
|
66
|
+
|
|
67
|
+
Press `Ctrl+B, D` to detach from tmux without stopping the container.
|
|
68
|
+
|
|
69
|
+
## Switching variants
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Edit VARIANT in .env, then:
|
|
73
|
+
docker compose down # stop the old variant
|
|
74
|
+
./run.sh # build+start the new one
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Each variant's image is tagged `<COMPOSE_PROJECT_NAME>-<variant>:latest`, so
|
|
78
|
+
switching doesn't rebuild unnecessarily. The base image (`BASE_IMAGE`, default
|
|
79
|
+
`agent-sandbox-base:latest`) is *shared* across all sandboxes regardless of
|
|
80
|
+
`COMPOSE_PROJECT_NAME`, so it's built once and reused everywhere.
|
|
81
|
+
|
|
82
|
+
## Stopping
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
docker compose stop # pause, keep state
|
|
86
|
+
docker compose down # remove containers, keep volumes (auth + caches)
|
|
87
|
+
docker compose down -v # nuke everything including caches and venvs
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Named volumes owned by a variant (e.g. `venv`, `tox`, `uv-cache`, `uv-data`,
|
|
91
|
+
`poetry-cache`, `node-modules`, `vendor`, `composer-cache`) live under the
|
|
92
|
+
compose project namespace — you can list them
|
|
93
|
+
with `docker volume ls | grep "$COMPOSE_PROJECT_NAME"`.
|
|
94
|
+
|
|
95
|
+
## Rebuilding
|
|
96
|
+
|
|
97
|
+
After changing a `Dockerfile` or `claude_install.sh`:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
./scripts/build.sh
|
|
101
|
+
docker compose up -d --force-recreate
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`build.sh` builds the shared base image only when it's missing, then builds the
|
|
105
|
+
active variant. To refresh the shared base itself (a no-cache rebuild that
|
|
106
|
+
updates *every* sandbox using it), pass `--force`:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
./scripts/build.sh --force
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Adding a variant
|
|
113
|
+
|
|
114
|
+
See the top-level README's "Adding a new variant" section.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
FROM ubuntu:26.04
|
|
2
|
+
|
|
3
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
4
|
+
|
|
5
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
6
|
+
curl ca-certificates git zsh neovim tmux ncurses-term \
|
|
7
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
8
|
+
|
|
9
|
+
ARG UID=1000
|
|
10
|
+
ARG GID=1000
|
|
11
|
+
ARG USERNAME=agent
|
|
12
|
+
|
|
13
|
+
# Ubuntu 26.04 ships a pre-created `ubuntu` user at 1000:1000. Remove it so the
|
|
14
|
+
# host's UID/GID can be reused without collision.
|
|
15
|
+
RUN (userdel -r ubuntu && groupdel ubuntu) 2>/dev/null || true \
|
|
16
|
+
&& groupadd -g ${GID} ${USERNAME} \
|
|
17
|
+
&& useradd -m -u ${UID} -g ${GID} -s /bin/zsh ${USERNAME}
|
|
18
|
+
|
|
19
|
+
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \
|
|
20
|
+
&& apt-get install -y --no-install-recommends nodejs \
|
|
21
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
22
|
+
|
|
23
|
+
USER ${USERNAME}
|
|
24
|
+
ENV USERNAME=${USERNAME}
|
|
25
|
+
ENV PATH="/home/${USERNAME}/.local/bin:${PATH}"
|
|
26
|
+
|
|
27
|
+
RUN curl -fsSL https://claude.ai/install.sh | bash
|
|
28
|
+
|
|
29
|
+
# uv is shared across all variants — it manages Python interpreters and is also
|
|
30
|
+
# the installer for Python-based tooling (e.g. mcp-tasker), so it belongs in the
|
|
31
|
+
# base image rather than any single variant.
|
|
32
|
+
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
33
|
+
|
|
34
|
+
# mcp-tasker is the task-tracker MCP server; make it available everywhere.
|
|
35
|
+
RUN uv tool install mcp-tasker
|
|
36
|
+
|
|
37
|
+
RUN git config --global user.name "Claude Agent" \
|
|
38
|
+
&& git config --global user.email "agent@claude.ai"
|
|
39
|
+
|
|
40
|
+
WORKDIR /work
|
|
41
|
+
|
|
42
|
+
CMD ["tmux", "new-session", "-A", "-s", "main"]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# link: https://hamvocke.com/blog/a-guide-to-customizing-your-tmux-conf/
|
|
2
|
+
|
|
3
|
+
# command shortcuts:
|
|
4
|
+
# set-option -> set
|
|
5
|
+
# bind-key -> bind
|
|
6
|
+
# set-window-option -> setw "set -w"
|
|
7
|
+
# list-sessions -> ls
|
|
8
|
+
|
|
9
|
+
# useful shortcuts:
|
|
10
|
+
# C-q - prefix (default: 'C-b')
|
|
11
|
+
# r - reload tmux config
|
|
12
|
+
# windows:
|
|
13
|
+
# c - create window
|
|
14
|
+
# n - switch to next window
|
|
15
|
+
# R - rename window title (default: ',')
|
|
16
|
+
# panes:
|
|
17
|
+
# C-o - switch panes
|
|
18
|
+
# - - split horizontally (default: '"')
|
|
19
|
+
# \ or | - split vertically (default: '%')
|
|
20
|
+
# window content and history
|
|
21
|
+
# [ - enter meta mode (Shift-A will exit this mode)
|
|
22
|
+
# v - start selection
|
|
23
|
+
# y - copy selected
|
|
24
|
+
|
|
25
|
+
# add more accessible prefix 'Ctrl+Q' but keep the old one as fallback
|
|
26
|
+
set -g prefix C-b
|
|
27
|
+
bind C-b send-prefix
|
|
28
|
+
|
|
29
|
+
set -g prefix2 C-q
|
|
30
|
+
bind C-q send-prefix -2
|
|
31
|
+
|
|
32
|
+
# split panes using | and -
|
|
33
|
+
bind | split-window -h
|
|
34
|
+
bind "\\" split-window -h # don't need to press `Shift`
|
|
35
|
+
bind - split-window -v
|
|
36
|
+
|
|
37
|
+
# config reload
|
|
38
|
+
bind r source-file ~/.config/tmux/tmux.conf
|
|
39
|
+
|
|
40
|
+
# start numbering from 1
|
|
41
|
+
set -g base-index 1
|
|
42
|
+
|
|
43
|
+
# switch panes using Alt-arrow without prefix
|
|
44
|
+
bind -n M-Left select-pane -L
|
|
45
|
+
bind -n M-Right select-pane -R
|
|
46
|
+
bind -n M-Up select-pane -U
|
|
47
|
+
bind -n M-Down select-pane -D
|
|
48
|
+
|
|
49
|
+
# allow to run vim over pane content
|
|
50
|
+
setw -g mode-keys vi
|
|
51
|
+
bind -T copy-mode-vi 'v' send-keys -X begin-selection
|
|
52
|
+
bind -T copy-mode-vi 'y' send-keys -X copy-selection
|
|
53
|
+
|
|
54
|
+
# enable mouse control
|
|
55
|
+
set -g mouse on
|
|
56
|
+
|
|
57
|
+
# NVIM advices:
|
|
58
|
+
set -g focus-events on
|
|
59
|
+
# enable truecolor feature (required by NVIM's `vim.opt.termguicolors` option)
|
|
60
|
+
set -g default-terminal "xterm-256color"
|
|
61
|
+
set -as terminal-features '*:RGB'
|
|
62
|
+
|
|
63
|
+
# don't rename windows automatically (use "C-b ," or "C-q R" to rename window title)
|
|
64
|
+
set -g allow-rename off
|
|
65
|
+
bind R command-prompt -I "#W" "rename-window '%%'"
|
|
66
|
+
|
|
67
|
+
# include scrollback history
|
|
68
|
+
set -g history-limit 3000
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# DESIGN TWEAKS
|
|
72
|
+
|
|
73
|
+
# don't do anything when a 'bell' rings
|
|
74
|
+
set -g visual-activity off
|
|
75
|
+
set -g visual-bell off
|
|
76
|
+
set -g visual-silence off
|
|
77
|
+
setw -g monitor-activity off
|
|
78
|
+
set -g bell-action none
|
|
79
|
+
|
|
80
|
+
# clock mode
|
|
81
|
+
setw -g clock-mode-colour yellow
|
|
82
|
+
|
|
83
|
+
# copy mode
|
|
84
|
+
setw -g mode-style 'fg=black bg=red bold'
|
|
85
|
+
|
|
86
|
+
# panes
|
|
87
|
+
set -g pane-border-style 'fg=red'
|
|
88
|
+
set -g pane-active-border-style 'fg=yellow'
|
|
89
|
+
|
|
90
|
+
# statusbar
|
|
91
|
+
set -g status-position bottom
|
|
92
|
+
set -g status-justify left
|
|
93
|
+
set -g status-style 'fg=red'
|
|
94
|
+
|
|
95
|
+
set -g status-left ''
|
|
96
|
+
set -g status-left-length 10
|
|
97
|
+
|
|
98
|
+
set -g status-right-style 'fg=black bg=yellow'
|
|
99
|
+
set -g status-right '%Y-%m-%d %H:%M '
|
|
100
|
+
set -g status-right-length 50
|
|
101
|
+
|
|
102
|
+
setw -g window-status-current-style 'fg=black bg=red'
|
|
103
|
+
setw -g window-status-current-format ' #I #W #F '
|
|
104
|
+
|
|
105
|
+
setw -g window-status-style 'fg=red bg=black'
|
|
106
|
+
setw -g window-status-format ' #I #[fg=white]#W #[fg=yellow]#F '
|
|
107
|
+
|
|
108
|
+
setw -g window-status-bell-style 'fg=yellow bg=red bold'
|
|
109
|
+
|
|
110
|
+
# messages
|
|
111
|
+
set -g message-style 'fg=yellow bg=red bold'
|
|
112
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# PATH
|
|
2
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
3
|
+
|
|
4
|
+
# Pretty prompt: user@host:dir (branch)$
|
|
5
|
+
autoload -Uz vcs_info
|
|
6
|
+
precmd() { vcs_info }
|
|
7
|
+
zstyle ':vcs_info:git:*' formats ' (%b)'
|
|
8
|
+
setopt PROMPT_SUBST
|
|
9
|
+
PROMPT='%F{green}%n@%m%f:%F{blue}%~%f%F{yellow}${vcs_info_msg_0_}%f%# '
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
services:
|
|
2
|
+
claude-code:
|
|
3
|
+
build:
|
|
4
|
+
context: variants/${VARIANT}
|
|
5
|
+
args:
|
|
6
|
+
- BASE_IMAGE=${BASE_IMAGE}
|
|
7
|
+
- USERNAME=${USER}
|
|
8
|
+
- UID=${HOST_UID:-1000}
|
|
9
|
+
- GID=${HOST_GID:-1000}
|
|
10
|
+
- PYTHON_VERSION=${PYTHON_VERSION:-3.10 3.11 3.12}
|
|
11
|
+
- PHP_VERSION=${PHP_VERSION:-8.3}
|
|
12
|
+
image: ${COMPOSE_PROJECT_NAME}-${VARIANT}:latest
|
|
13
|
+
stdin_open: true
|
|
14
|
+
tty: true
|
|
15
|
+
volumes:
|
|
16
|
+
- ..:/work
|
|
17
|
+
- npm-cache:/home/${USER}/.npm
|
|
18
|
+
- ./base/tmux.conf:/home/${USER}/.tmux.conf:ro
|
|
19
|
+
- ./base/zshrc:/home/${USER}/.zshrc:ro
|
|
20
|
+
- ~/.claude:/home/${USER}/.claude
|
|
21
|
+
- ~/.claude.json:/home/${USER}/.claude.json
|
|
22
|
+
- ~/repo-skills:/home/${USER}/repo-skills:ro
|
|
23
|
+
working_dir: /work
|
|
24
|
+
environment:
|
|
25
|
+
- TERM=xterm-256color
|
|
26
|
+
|
|
27
|
+
volumes:
|
|
28
|
+
npm-cache:
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# One-shot: ensure images are built, container is up, then drop into claude.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
cd "$(dirname "$0")"
|
|
6
|
+
# shellcheck source=scripts/_lib.sh
|
|
7
|
+
source scripts/_lib.sh
|
|
8
|
+
|
|
9
|
+
WORKDIR=""
|
|
10
|
+
ARGS=()
|
|
11
|
+
while [[ $# -gt 0 ]]; do
|
|
12
|
+
case "$1" in
|
|
13
|
+
--workdir)
|
|
14
|
+
WORKDIR="$2"
|
|
15
|
+
shift 2
|
|
16
|
+
;;
|
|
17
|
+
--workdir=*)
|
|
18
|
+
WORKDIR="${1#*=}"
|
|
19
|
+
shift
|
|
20
|
+
;;
|
|
21
|
+
*)
|
|
22
|
+
ARGS+=("$1")
|
|
23
|
+
shift
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
WORKDIR_ARGS=()
|
|
29
|
+
if [[ -n "$WORKDIR" ]]; then
|
|
30
|
+
WORKDIR_ARGS=(--workdir "$WORKDIR")
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
VARIANT_IMAGE="${COMPOSE_PROJECT_NAME}-${VARIANT}:latest"
|
|
34
|
+
|
|
35
|
+
if ! docker image inspect "${BASE_IMAGE}" >/dev/null 2>&1 \
|
|
36
|
+
|| ! docker image inspect "${VARIANT_IMAGE}" >/dev/null 2>&1; then
|
|
37
|
+
scripts/build.sh
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
docker compose up -d
|
|
41
|
+
|
|
42
|
+
exec docker compose exec "${WORKDIR_ARGS[@]}" claude-code claude --dangerously-skip-permissions "${ARGS[@]}"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Sourced by other scripts — loads .env and exports COMPOSE_FILE.
|
|
2
|
+
# Assumes the caller has already cd'd to sandbox/.
|
|
3
|
+
|
|
4
|
+
if [[ ! -f .env ]]; then
|
|
5
|
+
echo "Error: sandbox/.env not found. Copy .env.example to .env and edit it." >&2
|
|
6
|
+
exit 1
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
set -a
|
|
10
|
+
# shellcheck disable=SC1091
|
|
11
|
+
source .env
|
|
12
|
+
set +a
|
|
13
|
+
|
|
14
|
+
: "${COMPOSE_PROJECT_NAME:?Must set COMPOSE_PROJECT_NAME in .env}"
|
|
15
|
+
: "${VARIANT:?Must set VARIANT in .env}"
|
|
16
|
+
: "${USER:?Must set USER in .env}"
|
|
17
|
+
|
|
18
|
+
# Base image name is shared across all sandboxes (independent of
|
|
19
|
+
# COMPOSE_PROJECT_NAME), so rebuilding it once updates every sandbox.
|
|
20
|
+
: "${BASE_IMAGE:=agent-sandbox-base:latest}"
|
|
21
|
+
export BASE_IMAGE
|
|
22
|
+
|
|
23
|
+
export COMPOSE_FILE="docker-compose.yml:variants/${VARIANT}/compose.override.yml"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
cd "$(dirname "$0")/.."
|
|
5
|
+
# shellcheck source=_lib.sh
|
|
6
|
+
source scripts/_lib.sh
|
|
7
|
+
|
|
8
|
+
FORCE=0
|
|
9
|
+
for arg in "$@"; do
|
|
10
|
+
case "$arg" in
|
|
11
|
+
--force) FORCE=1 ;;
|
|
12
|
+
*)
|
|
13
|
+
echo "Usage: build.sh [--force]" >&2
|
|
14
|
+
echo " --force rebuild the shared base image from scratch (--no-cache)" >&2
|
|
15
|
+
exit 1
|
|
16
|
+
;;
|
|
17
|
+
esac
|
|
18
|
+
done
|
|
19
|
+
|
|
20
|
+
# The base image is shared across all sandboxes, so leave it untouched when it
|
|
21
|
+
# already exists unless --force is given (which rebuilds it for everyone).
|
|
22
|
+
if [[ "${FORCE}" -eq 1 ]]; then
|
|
23
|
+
echo ">>> Forcing rebuild of base image: ${BASE_IMAGE}"
|
|
24
|
+
docker build \
|
|
25
|
+
--no-cache \
|
|
26
|
+
--build-arg UID="${HOST_UID:-1000}" \
|
|
27
|
+
--build-arg GID="${HOST_GID:-1000}" \
|
|
28
|
+
--build-arg USERNAME="${USER}" \
|
|
29
|
+
-t "${BASE_IMAGE}" \
|
|
30
|
+
base/
|
|
31
|
+
elif docker image inspect "${BASE_IMAGE}" >/dev/null 2>&1; then
|
|
32
|
+
echo ">>> Base image already present, skipping: ${BASE_IMAGE} (use --force to rebuild)"
|
|
33
|
+
else
|
|
34
|
+
echo ">>> Building base image: ${BASE_IMAGE}"
|
|
35
|
+
docker build \
|
|
36
|
+
--build-arg UID="${HOST_UID:-1000}" \
|
|
37
|
+
--build-arg GID="${HOST_GID:-1000}" \
|
|
38
|
+
--build-arg USERNAME="${USER}" \
|
|
39
|
+
-t "${BASE_IMAGE}" \
|
|
40
|
+
base/
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo ">>> Building variant: ${VARIANT}"
|
|
44
|
+
docker compose build
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
cd "$(dirname "$0")/.."
|
|
4
|
+
# shellcheck source=_lib.sh
|
|
5
|
+
source scripts/_lib.sh
|
|
6
|
+
|
|
7
|
+
WORKDIR=""
|
|
8
|
+
ARGS=()
|
|
9
|
+
while [[ $# -gt 0 ]]; do
|
|
10
|
+
case "$1" in
|
|
11
|
+
--workdir)
|
|
12
|
+
WORKDIR="$2"
|
|
13
|
+
shift 2
|
|
14
|
+
;;
|
|
15
|
+
--workdir=*)
|
|
16
|
+
WORKDIR="${1#*=}"
|
|
17
|
+
shift
|
|
18
|
+
;;
|
|
19
|
+
*)
|
|
20
|
+
ARGS+=("$1")
|
|
21
|
+
shift
|
|
22
|
+
;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
WORKDIR_ARGS=()
|
|
27
|
+
if [[ -n "$WORKDIR" ]]; then
|
|
28
|
+
WORKDIR_ARGS=(--workdir "$WORKDIR")
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
exec docker compose exec "${WORKDIR_ARGS[@]}" claude-code zsh "${ARGS[@]}"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# check=skip=InvalidDefaultArgInFrom
|
|
2
|
+
ARG BASE_IMAGE
|
|
3
|
+
FROM ${BASE_IMAGE}
|
|
4
|
+
|
|
5
|
+
ARG PYTHON_VERSION="3.10 3.11 3.12"
|
|
6
|
+
|
|
7
|
+
# uv ships in the base image; use it to install the requested interpreters.
|
|
8
|
+
RUN uv python install ${PYTHON_VERSION}
|
|
9
|
+
|
|
10
|
+
# Both uv- and poetry-based projects are supported, so install both package
|
|
11
|
+
# managers alongside the shared dev tooling.
|
|
12
|
+
RUN uv tool install poetry \
|
|
13
|
+
&& uv tool install tox \
|
|
14
|
+
&& uv tool install pytest \
|
|
15
|
+
&& uv tool install mypy \
|
|
16
|
+
&& uv tool install black \
|
|
17
|
+
&& uv tool install isort \
|
|
18
|
+
&& uv tool install flake8
|
|
19
|
+
|
|
20
|
+
USER root
|
|
21
|
+
RUN npm install -g pyright && rm -rf /var/lib/apt/lists/*
|
|
22
|
+
USER ${USERNAME}
|
|
23
|
+
|
|
24
|
+
# Pre-create volume mount points so named-volume init inherits user ownership
|
|
25
|
+
# instead of being created as root by the Docker daemon.
|
|
26
|
+
RUN mkdir -p /work/.venv /work/.tox
|
|
27
|
+
|
|
28
|
+
# Keep poetry's venv inside the project dir so the .venv volume-shadow works.
|
|
29
|
+
ENV POETRY_VIRTUALENVS_IN_PROJECT=true
|
|
30
|
+
ENV PATH="/work/.venv/bin:${PATH}"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
services:
|
|
2
|
+
claude-code:
|
|
3
|
+
volumes:
|
|
4
|
+
# Shadow project-local paths so container writes stay out of the host bind mount.
|
|
5
|
+
- venv:/work/.venv
|
|
6
|
+
- tox:/work/.tox
|
|
7
|
+
- uv-cache:/home/${USER}/.cache/uv
|
|
8
|
+
- uv-data:/home/${USER}/.local/share/uv
|
|
9
|
+
- poetry-cache:/home/${USER}/.cache/pypoetry
|
|
10
|
+
|
|
11
|
+
volumes:
|
|
12
|
+
venv:
|
|
13
|
+
tox:
|
|
14
|
+
uv-cache:
|
|
15
|
+
uv-data:
|
|
16
|
+
poetry-cache:
|