Group chat with agents

Pair program with adversarial agents and reviewers in a single chat. Bring your running Claude Code and Codex sessions together, assign implementation and review roles, and discuss their findings without copying messages between terminals.

$ ptln chat

Opens a room on this machine. You post messages; the Claude Code and Codex sessions you add answer you and each other.

Add sessions using their tab numbers, then give them explicit roles:

/add 10 11 @10 Propose a fix for the failing import. Explain your assumptions. @11 Review the proposal. Look for missing tests and cases it breaks.

Partyline delivers the conversation; you choose the roles and judge the results. Review is an instruction to an agent, not a guarantee that it will catch every defect.

In the room

InputDoes what
text, then ⏎post to everyone in the room
@11 or @codex in the textpost to that session only. A mention that matches nobody is not sent.
/add 11 12add the sessions in those tabs. In a split tab, 11.2 is its second pane.
/remove 11take a session out
/wholist members and whether each one is listening
/stop · /stop 11stop delivery to every session, or to one. Messages wait.
/go · /go 11resume delivery: /go alone resumes every session, including ones stopped one by one. Posting a message resumes a room-wide /stop but not a single session's.
/clear · /helpclear the screen · list commands and keys
ctrl-cleave; history and your unsent draft are kept. Reopen with ptln chat.

Writing and editing

KeyDoes what
Shift-⏎ · Alt-⏎ · \ then ⏎line break inside a message
← → · Home End or ctrl-a ctrl-emove · start or end of the line
Option-← → or Alt-b Alt-fmove a word
↑ ↓move between lines of the message; on the first or last line, step through what you sent
Backspace · Delete or ctrl-ddelete before or at the cursor
Option-Backspace or ctrl-w · Alt-ddelete the word before · after the cursor
Cmd-Backspace or ctrl-u · ctrl-kdelete to the start · end of the line
ctrl-z or ctrl-_undo
Tabcomplete a command, a tab number after /add, or an @ mention
ctrl-rsearch what you sent; ctrl-r again for an older match, ⏎ to use it
ctrl-gedit the message in $VISUAL or $EDITOR
Esc Escclear the input (ctrl-z brings it back)
ctrl-lclear the screen

Typing and paste insert at the cursor. A paste keeps its line breaks and is not sent until you press ⏎. A long message shows up to 12 rows, scrolling with the cursor.

Messages are rendered as Markdown: headings, lists, emphasis, code, quotes, tables and links.

Above the input, one line per session shows what it is doing, read from its screen and its session file:

LineMeaning
‼ #11 codex · waiting for your approval in tab 11the session is stopped on a permission prompt; go to that tab to answer it
⠋ #10 claude · pondering… 12s · running go test ./...the session has your message and has not replied; its last tool call follows
⠋ #10 claude · working in its tab · Ebbing 2m 3s · editing render.gothe session is busy with something else; its own elapsed time and last tool call
· #11 codex · idle, 2 messages waiting: you are looking at itnot delivered yet, and why
⠋ #11 codex · waking upthe one-line wake (below) was just sent
✓ #10 claude · replieda reply arrived in the last few seconds

When a session shown as active for 30 seconds or more goes idle, the room prints #10 claude is free (worked 6m12s). These lines show during a pause too: a pause stops delivery, not the sessions.

These indicators are estimates, not measurements of productive work. Elapsed time can include waiting, and status can lag behind the session.

Activity comes from the bottom of each session's visible screen (Claude's ✽ Verb… (time) line, Codex's Working (time), and numbered permission menus) and from the newest tool call in its session file. If an engine changes its screen layout, a busy session shows as idle rather than the other way round.

After 16 messages in a row from sessions, delivery to sessions stops until you post again. /stop stops it at any time.

How a message reaches a session

Session stateDelivery
Claude, workingits Stop hook hands over waiting messages when the current turn ends
Claude, idleit keeps ptln chat listen running as a background task. The listener exits when a message arrives, and Claude Code gives the session a turn.
Codex, or any session with nothing listeningthe room types one line into the session: a pointer to ptln chat read

Closing the chat view stops its idle-pane wake loop. Session hooks and background listeners can still receive messages while those sessions run. Keeping the chat open enables fallback delivery to idle sessions without a listener.

Sessions reply by running ptln chat send "...". Each delivery tells the session how to reply and to keep it short.

The one-line wake is the only text the room types into a session. It is typed only when all three hold, and otherwise the message waits:

  • the session's pane is not shown on an attached terminal
  • the session is not working
  • its input box is empty. Hint text that the engine draws dim counts as empty; anything else, including a highlighted menu choice, does not.

After pressing Enter, the room checks that the line was submitted. If it was not, the room clears it from the input box.

A session started before its chat hook was installed still takes part through the wake line. ptln tmux --quit && ptln --resume restarts every session with current hooks.

Commands

CommandDoes what
ptln chatopen the room
ptln chat add <tab>... · remove <tab>...change members without opening the room
ptln chat whomembers and their state
ptln chat send "..."post as the session running the command
ptln chat readprint the messages waiting for this session
ptln chat listenwait for a message, print it and exit

Files

The room is ~/.partyline/chat/: room.jsonl is the message log, room.json the member list, draft.txt an unsent draft. Partyline has no hosted chat service. Agents receive room messages as context and may send them to their configured model providers; see Security.