# dsh-docker Docker tools for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): the agent can list containers, read logs, inspect images, and drive Compose projects, all through the Docker CLI already on the machine. No daemon is required at install time. Reachability is probed per call, so a machine without Docker simply reports an unreachable engine instead of failing to load. ## Install ```bash dsh plugin --profile web add github:DevViking-Persike/dsh-docker ``` Restart `dsh` afterwards. The read-only tools are active immediately; see [Compose](#compose-lifecycle) to enable the lifecycle tools. ## Tools | Tool | What it does | |---|---| | `docker_ps` | List containers. Running only by default; `all: true` includes stopped ones, `project` filters to one Compose project. | | `docker_images` | List local images with their tags and sizes. | | `docker_logs` | Read a container's recent output. Keeps the newest text and reports when older entries were dropped. | | `docker_compose_up` | Start a Compose project detached and wait for its containers. **Opt-in.** | | `docker_compose_down` | Stop and remove a Compose project's containers. **Opt-in.** | ## Compose lifecycle Starting and stopping containers changes machine state, so those two tools are off by default. A deployment that wants them sets `compose: true`: ```yaml - id: dsh-docker name: 'dsh-docker' config: compose: true ``` ## Configuration Every field is optional. | Field | Default | Meaning | |---|---|---| | `cli` | `docker` | Executable name or absolute path. | | `projectRoot` | process cwd | Working directory for invocations, and the root relative compose paths resolve against. | | `compose` | `false` | Register the Compose lifecycle tools. | | `inspectTimeoutMs` | `30000` | Budget for one read (`ps`, `images`, `logs`). | | `composeTimeoutMs` | `600000` | Budget for one Compose call; pulling images and waiting on health checks routinely outlasts a read by an order of magnitude. | | `maxOutputBytes` | `2000000` | Cap on collected output of one invocation. | | `maxLogChars` | `40000` | Cap on characters `docker_logs` emits. | | `defaultLogTail` | `200` | Trailing lines read when a request states no `tail`. | | `graceMs` | `5000` | Termination grace handed to the subprocess seam. | ## Model Experience The read-only tools put one line per container or image into the model's context, not raw CLI output: state, status, image, Compose project/service, and published ports. An empty result says so explicitly rather than returning blank text, so the model does not read silence as failure. `docker_logs` keeps the **newest** characters when it must cut, because the tail of a log is what explains a failure that just happened, and it marks the cut so the model knows it is not seeing the whole file. Compose results lead with the settled project state and put the backend's own output after it — the model reasons about which containers are up, and reads the CLI text only when diagnosing. ## Safety - Arguments reach the executable as a fixed argv and are never shell-interpreted, so a container name cannot become a flag or a shell fragment. - `--` terminates flag parsing before every container operand, so a container literally named `--follow` stays an operand. - `docker_compose_down` never forwards a service filter: `down` removes the whole project, and a filtered call would read as narrower than it is. - Compose lifecycle calls are marked concurrency-unsafe, because two lifecycle calls on one project race inside the engine. ## Known Limitations and Deferred Work - Image sizes are parsed from the CLI's display string (`1.09GB`), because `docker images --format json` exposes no machine-readable size. An unparseable value reads as `0` rather than failing the listing. - Engine reachability is probed on every call rather than cached, so a stopped daemon is noticed immediately at the cost of one extra invocation per tool call. - Compose project state is read back with a second `docker ps` after each lifecycle call, since the CLI's own output does not report the settled containers. - There is no `docker exec`, no image build, and no registry operation. Each would widen the blast radius considerably and none has a current consumer. ## License MIT — see [LICENSE](LICENSE) and [NOTICE.md](NOTICE.md) for attribution to the DeepSeek Harness project whose plugin conventions this follows.