choom: map --continue <id> to claude --resume — claude's own --continue takes no value (closes #2127)

This commit is contained in:
damocles 2026-07-02 11:52:37 +02:00 committed by mara
commit 4e7a9b93d9
3 changed files with 30 additions and 21 deletions

View file

@ -379,7 +379,7 @@ Generate the federation peer-config block for THIS hive — the nix a peer opera
Open an interactive Claude session inside an agent container. Open an interactive Claude session inside an agent container.
Runs claude as the agent user from its state dir with the harness's settings / MCP / system prompt. Bare `choom <name>` is a fresh session; `--continue <id|name>` rejoins a prior one. Never collides with the harness's live session. Requires root + a running container. See `docs/tools/hivectl.md` (Choom) for details. Runs claude as the agent user from its state dir with the harness's settings / MCP / system prompt. Bare `choom <name>` is a fresh session; `--continue <session-id>` rejoins a prior one. Never collides with the harness's live session. Requires root + a running container. See `docs/tools/hivectl.md` (Choom) for details.
**Usage:** `hivectl choom [OPTIONS] <NAME>` **Usage:** `hivectl choom [OPTIONS] <NAME>`
@ -389,7 +389,7 @@ Runs claude as the agent user from its state dir with the harness's settings / M
###### **Options:** ###### **Options:**
* `--continue <SESSION>` — Resume a prior claude session by id or display name, passed through as `claude --continue <value>`. Omit for a fresh blank session. A value is required when the flag is given * `--continue <SESSION>` — Resume a prior claude session by its session id, passed through as `claude --resume <value>` (claude's own `--continue` takes no value — it resumes the cwd's latest session, which is the harness's, so choom never uses it). Omit for a fresh blank session. A value is required when the flag is given

View file

@ -127,17 +127,19 @@ running claude from the agent's state dir. Requires root (same as all
`machinectl shell` operations). `machinectl shell` operations).
```bash ```bash
hivectl choom iris # fresh blank Claude session in iris's container hivectl choom iris # fresh blank Claude session in iris's container
hivectl choom iris --continue <id|name> # rejoin a prior session by id or display name hivectl choom iris --continue <session-id> # rejoin a prior session by id
``` ```
Bare `choom` starts a fresh blank session. `--continue <value>` is Bare `choom` starts a fresh blank session. `--continue <value>` maps to
passed straight through as `claude --continue <value>` to rejoin a `claude --resume <value>` to rejoin a prior session by its session id.
prior session — claude resolves whether the value is a session id or a (It is deliberately NOT passed as `claude --continue <value>` — claude's
display name (you can `/rename` a session in-session for an easy own `--continue` is a bare flag that takes no argument and resumes the
handle). A value is required when the flag is given. Either way choom cwd's *latest* session, i.e. the harness's; a value after it would be
never collides with the harness's live session in the same project dir: consumed as the first prompt, silently poking the live harness session.)
the harness pins its own id via `--resume`, so a blank choom session is A value is required when the flag is given. Either way choom never
collides with the harness's live session in the same project dir: the
harness pins its own id via `--resume`, so a blank choom session is
invisible to it. The container must be running. invisible to it. The container must be running.
choom reproduces the harness's own claude invocation so the operator choom reproduces the harness's own claude invocation so the operator

View file

@ -112,15 +112,18 @@ enum Cmd {
/// ///
/// Runs claude as the agent user from its state dir with the harness's /// Runs claude as the agent user from its state dir with the harness's
/// settings / MCP / system prompt. Bare `choom <name>` is a fresh /// settings / MCP / system prompt. Bare `choom <name>` is a fresh
/// session; `--continue <id|name>` rejoins a prior one. Never collides /// session; `--continue <session-id>` rejoins a prior one. Never
/// with the harness's live session. Requires root + a running /// collides with the harness's live session. Requires root + a running
/// container. See `docs/tools/hivectl.md` (Choom) for details. /// container. See `docs/tools/hivectl.md` (Choom) for details.
Choom { Choom {
/// Agent name (e.g. `damocles`, `iris`). /// Agent name (e.g. `damocles`, `iris`).
name: String, name: String,
/// Resume a prior claude session by id or display name, passed /// Resume a prior claude session by its session id, passed
/// through as `claude --continue <value>`. Omit for a fresh /// through as `claude --resume <value>` (claude's own
/// blank session. A value is required when the flag is given. /// `--continue` takes no value — it resumes the cwd's latest
/// session, which is the harness's, so choom never uses it).
/// Omit for a fresh blank session. A value is required when
/// the flag is given.
#[arg(long = "continue", value_name = "SESSION")] #[arg(long = "continue", value_name = "SESSION")]
continue_session: Option<String>, continue_session: Option<String>,
}, },
@ -1044,16 +1047,20 @@ fn choom(name: &str, continue_session: Option<&str>) -> Result<()> {
let state_dir = format!("/agents/{name}/state"); let state_dir = format!("/agents/{name}/state");
// Per-turn config the harness writes; matches `paths::config_dir()`. // Per-turn config the harness writes; matches `paths::config_dir()`.
let cfg = "/run/hive-config"; let cfg = "/run/hive-config";
// `--continue <value>` passes straight through to claude. The value is // `--continue <value>` maps to `claude --resume <value>` — claude's
// single-quoted into the shell script, so reject an embedded single // own `--continue` is a bare flag (no argument): passing a value
// quote (the only char that breaks single-quoting) to rule out // after it made claude resume the cwd's latest session and treat the
// injection — session ids / display names never contain one. // value as the first PROMPT, silently poking the harness session.
// `--resume <session-id>` is the actual rejoin-by-id surface. The
// value is single-quoted into the shell script, so reject an
// embedded single quote (the only char that breaks single-quoting)
// to rule out injection — session ids never contain one.
let session_arg = match continue_session { let session_arg = match continue_session {
Some(val) => { Some(val) => {
if val.contains('\'') { if val.contains('\'') {
bail!("invalid --continue value '{val}': must not contain a single quote"); bail!("invalid --continue value '{val}': must not contain a single quote");
} }
format!("set -- --continue '{val}';") format!("set -- --resume '{val}';")
} }
None => "set --;".to_string(), None => "set --;".to_string(),
}; };