---
title: ps, stop, attach
description: "Container lifecycle for heretic agents — label-based discovery, session filtering, JSON output, sidecar and network teardown, name matching rules, and detach keystrokes"
canonical: https://giglabo.com/heretic/docs/heretic-cli/commands/ps-stop-attach
locale: en
---

# ps, stop, attach

> Markdown twin of https://giglabo.com/heretic/docs/heretic-cli/commands/ps-stop-attach
> Fetch this instead of the HTML page: same content, a fraction of the bytes.
> Site structure and the full page list for agents: https://giglabo.com/llms.txt

Container lifecycle for heretic agents — label-based discovery, session filtering, JSON output, sidecar and network teardown, name matching rules, and detach keystrokes

All three commands discover containers by **label**, never by name pattern, so they only ever act on containers heretic created.

```
container name:  heretic-<agent>-<session>-<hash8>
hash8:           first 8 hex characters of sha256(<absolute project dir>)
```

| Label | Value |
|-------|-------|
| `heretic.managed` | `true` on every container and network heretic creates |
| `heretic.agent` | profile name |
| `heretic.project` | absolute project directory |
| `heretic.session` | session name (`default` unless `-s` was used) |
| `heretic.role` | `build-sidecar` on builders, `build-network` on the per-run network |
| `heretic.runtime` | `node` / `python` / `java` / `go` / `rust` on builders |
| `heretic.network` | per-run sidecar network name, on both agent and builders |

## `heretic-cli ps`

```
heretic-cli ps [--json] [-s|--session <name>]
```

Lists containers in **all** states, then removes the build sidecars from the output — builders are infrastructure, not agents.

```
$ heretic-cli ps
NAME                             AGENT   SESSION  STATUS   UPTIME  PORTS
heretic-claude-default-1a2b3c4d  claude  default  running  4m ago  3000→3000
heretic-claude-feat-x-1a2b3c4d   claude  feat-x   exited   2h ago  -
```

| Column | Source |
|--------|--------|
| NAME | container name, or a 12-character ID when unnamed |
| AGENT | `heretic.agent` label, else `unknown` |
| SESSION | `heretic.session` label, else `-` |
| STATUS | Docker state: `running`, `exited`, `created`, `paused`, `dead` |
| UPTIME | **age since creation**, not time since start |
| PORTS | published mappings, else `-` |

`--json` prints the same fields as an array of objects (`name`, `agent`, `session`, `status`, `uptime`, `ports`); `uptime` and `ports` are pre-formatted strings.

Nothing running → `No running heretic agents.` (or `[]`). Docker unreachable → `Docker is not running or not available` and exit 1.

> **Note: UPTIME is age, not uptime**
>
> A container created two days ago and restarted a minute ago still shows `2d ago`.

## `heretic-cli stop`

```
heretic-cli stop [name] [--all] [-f|--force] [--keep] [-s|--session <name>]
```

| Form | Target |
|------|--------|
| `stop` | the agent container whose `heretic.project` equals the current directory (plus session when `-s` is given), then its sibling sidecars |
| `stop <name>` | matched against agent containers only, then its sibling sidecars |
| `stop --all` | every heretic-managed container, agents **and** sidecars |

Name matching, first match wins: the container ID **starts with** the string, or a container name equals it, or a container name **contains** it as a substring. So `stop claude` matches `heretic-claude-default-1a2b3c4d`; an ambiguous substring picks an arbitrary match.

Sibling sidecars are containers with `heretic.role=build-sidecar`, the same `heretic.project`, and the same `heretic.session` when the agent has one.

Unless `--force`, you get a confirmation whose default is **yes**:

```
Stop 3 container(s): heretic-claude-default-1a2b3c4d, heretic-builder-node-default-1a2b3c4d, … ? (use --force to skip) (Y/n)
```

Per container: an already `exited`/`dead` container reports `Container X is already stopped`; otherwise it is stopped with a 10 s timeout (`Stopped X`) and, unless `--keep`, removed (`Removed X`). One failure never aborts the rest — failures are collected and reported at the end (`Errors occurred while stopping containers:`) with exit 1.

**Network cleanup:** unless `--keep`, every distinct `heretic.network` among the stopped containers is removed. **Named cache volumes are intentionally kept** so builder caches stay warm.

```bash
heretic-cli stop                  # this directory's agent + its sidecars
heretic-cli stop -f               # …without the prompt
heretic-cli stop claude --keep    # stop but keep containers and network
heretic-cli stop --all -f         # everything heretic-managed
heretic-cli stop -s feat-x -f     # only session feat-x
```

Informational messages that are **not** errors (exit 0): `No heretic containers found`, `No heretic container found matching '<name>'`, `No heretic container found for the current directory`.

> **Warning: stop with no argument matches the directory exactly**
>
> It compares `heretic.project` against the current working directory as a string. Running it from a subdirectory, or through a symlinked path, finds nothing — pass the container or agent name instead.

## `heretic-cli attach`

```
heretic-cli attach <name> [-s|--session <name>]
```

Requires a **running** container, otherwise `Container '<name>' is not running (state: <state>)` and exit 1. Matching works like `stop`, but over all heretic containers — sidecars included, where attaching just shows the exec-server's output.

```
$ heretic-cli attach claude
Attached to heretic-claude-default-1a2b3c4d. Press Ctrl+P, Ctrl+Q to detach.
```

It shells out to `docker attach`, so the **docker CLI must be on `PATH`**, and heretic exits with `docker attach`'s exit code. `Ctrl+P, Ctrl+Q` detaches without killing the session; `Ctrl+C` forwards SIGINT to PID 1 and usually ends it.

Attaching to a container started without `interactive: true` gives you output but no stdin. For a second, independent shell use `docker exec -it <name> bash` — remembering that a fresh exec does not inherit the entrypoint's `HOME` export under `--root`.

## Cleaning up completely

```bash
heretic-cli ps                                        # agents only
docker ps -a --filter label=heretic.managed=true      # agents and sidecars
heretic-cli stop --all -f                             # stop + remove, drop per-run networks
docker network ls --filter label=heretic.role=build-network
docker volume ls                                      # cache volumes are never auto-removed
```

Session state under `.heretic/temp/<session>/` — including the agent's `~/.claude` history — is never pruned. Delete those directories by hand to reset an agent.

There is no `heretic-cli logs`, `exec`, `restart` or `rm`: use `docker logs heretic-<agent>-<session>-<hash8>` and friends.

## Next Steps

- [run](https://giglabo.com/heretic/docs/heretic-cli/commands/run) — starting agents
- [Sessions](https://giglabo.com/heretic/docs/heretic-cli/configuration/sessions) — the session model
- [Sidecars](https://giglabo.com/heretic/docs/heretic-cli/configuration/sidecars) — builders, networks and teardown

## Related

- HTML version of this page: https://giglabo.com/heretic/docs/heretic-cli/commands/ps-stop-attach
- Site map for agents: https://giglabo.com/llms.txt
