acpmux: ls PATTERN, dotted --fields, session info --fields, send --tag
Leo, 2026-10-30: "i feel like there's still a big mess of uncategorized stuff in the sidebar + various not as ergonomic/one-command-y as i'd liike set of commands for interacting with stuff" / "good intuitive stuff" / "and also docs!"
No HQ design issue covers acpmux CLI ergonomics. The closest is HQ#5197 (team tools), which covers pr-owner, not acpmux; the pr-owner who change from this audit goes to that tool in a separate PR.
What lanes actually ran
I went through every Bash call in the agent transcripts on one lane host: 16,464 calls in 159 transcripts, read only. I counted the command shapes that take several commands, a qualified id, or --json piped into python/jq/grep. Each count is calls / transcripts it shows up in.
| # | Pattern lanes run today | Calls / transcripts | One command | Where |
|---|---|---|---|---|
| 1 | lane-gh call search_issues … | python3 -c 'json…items' | 134 / 47 | (outside acpmux) | lane-gh owner |
| 2 | lane-gh call get … | python3 or | head -c | 142 / 27 | (outside acpmux) | lane-gh owner |
| 3 | acpmux --json ls | python3 -c '… if r["name"]==X …' or acpmux ls | grep NAME / awk '$1==s' | 63 / 21 | acpmux ls 'review-27*' --fields name,status,turn.activity | this PR |
| 4 | for n in …; do pr-owner who $n | head -3; done | 27 / 19 (59 / 34 for any pr-owner who) | pr-owner who N N… | leo-workspace PR |
| 5 | for n in …; do glaeda-gh status pr …#$n | head/grep; done, and status --json | python3 | 42 / 19, 25 / 9 | (outside acpmux) | glaeda-gh owner |
| 6 | acpmux last X | tail -c 400 | 37 / 10 | already one command (last -n); not changed | — |
| 7 | acpmux --json info X | python3 -c 'print(d["state"], d["turn"]["activity"])', often in a loop after acpmux wait | 12 / 5 | acpmux session info X --fields status,turn.activity | this PR |
| 8 | for s in $(acpmux ls --tag area=X --json | python3 …); do acpmux send --no-wait $s "$msg"; done | 5 / 2 | acpmux send --tag area=X "$msg" | this PR |
Rows 3, 7 and 8 are acpmux's own. Row 8 is rare in calls, but it is how a coordinator tells a whole area something (base fixed, rebase now), and each instance is a loop with an inline python step.
What changes
acpmux ls PATTERN…: a name glob (*,?) matched against the bare name on any host, ahost/globfor one peer's sessions, or an exact session id. It combines with--status,--tag,--fieldsand--limit.--fieldstakes dotted paths (turn.activity,lane.state,pullRequests.0.number). The longest key that matches wins, so a tag key with a dot in it (tags.lane.reportsTo) still resolves. Top-level fields behave exactly as before.acpmux session info NAME --fields a,b: a single field prints its bare value (--fields sessionIdgives just the id); several fields printfield<TAB>valuelines; with--jsonyou get an object of only those fields.acpmux ls --name PATTERN(repeatable) is the same as a positional PATTERN. It keeps the--namefrom #27813 working, because thelsrework drops that PR's regex filter in favour of this glob, so there is one way to filter by name. A pattern also matches a session id, bare or a peer'shost/id.acpmux send --tag K=V [--dry-run] MSG: sends one--no-waitmessage, with its own prompt id, to every non-closed session on any host that carries the tag. A local session is targeted by its id, a peer's by itspeer/id(qualifiedSessionId, the key the outbox routes by). It skips the sender's own local session, but not a peer's session that happens to share the bare id. It prints one line per target with its delivery id. It exits 1 when any send failed and 4 (no such session) when no live session carries the tag.--attachis refused with--tag.ls,session infoandsendgainedExamples:in--help. The README's script section and the agent guide (skills/acpmux/SKILL.md) list the new forms.- The selection helpers (
parse_fields,project_fields,text_field, the tag match) move out ofcli/run/standard.rsintocli/select.rs. Thesend --tagandinfo --fieldshandlers live incli/run/fanout.rs.standard.rsgets 39 lines shorter.
Coordination: the ls compact-default PR (which supersedes #27813) moves the Ls flags and handler into src/cli/run/ls.rs. Whichever of the two PRs lands second carries this PR's pattern clause into that retain, and adds || !patterns.is_empty() to its explicit-selection check, so acpmux ls 'review-*' still shows reviewer rows. The bare-name resolution PR does not touch run/standard.rs; both PRs add an after_help to Send, and the second to land merges the example lists.
Tests
Red/green: cargo test -p acpmux --test one_command runs against a real daemon with the stand-in agent. It covers name globs, a dotted --fields, session info --fields in all three output shapes, send --tag --dry-run, and a send --tag that reaches both tagged sessions (each replies echo: …) but not the session with another tag, which keeps 1 turn. It also checks exit 4 when no session carries the tag, --name, and a dotted field in text mode.
- Red at 3c3991539e3 (the test commit, base source):
error: unexpected argument 'review-27*' found. - Green at 560330fe324, and still green at the head after the review fixes. Each fix commit's red is recorded in its commit message: the peer-target and
host/idunit tests panic with the old lines.
Unit tests: select:: (dotted paths including dotted keys, tag match, globs, ids) and fanout:: (targets skip closed sessions, other tags and the sender), 12 passed. ls_selection, cli_ergonomics and cli_help_reload still pass, and cargo clippy -p acpmux --lib --tests is clean.
Observed / Not run
- Observed: everything listed above, on Linux in the acpmux crate (
cargo test/clippyfor the crate alone). - Not run: a send to a real peer over the mesh. The peer path goes through the same
_acpmux/send_queueoutbox assend --no-wait PEER/NAME, targeted by session id, whichresolve_remotematches; I read that path but did not run it.
Changelog
- Added:
acpmux ls PATTERN, dotted--fieldspaths,acpmux session info --fieldsandacpmux send --tagfor scripts and coordinators.
Agent session: agent session
🤖 Generated with Claude Code