Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

queue - defer a message to Claude's next stopping point

Send a long prompt to a working Claude Code agent and walk away. Instead of interrupting mid-task - which makes the agent double-take, react, log a "we'll get to it later," and lose its flow - the message is held and delivered at the agent's first natural stopping point: the moment it would otherwise say "alright, I think I'm done here." It arrives wrapped in context about when you sent it and what the agent was doing at the time, so the conversation stays sensible and in-order.

Why this shape

The insight is that Claude Code already fires a Stop hook at exactly the moment you care about - when the agent finishes emitting work and yields the turn. That's a deterministic, free "the agent is done" signal; there's no need to poll or ask a cheap model "are you done yet?" So the design is just:

  1. Capture - a marker-prefixed message is intercepted, stamped with the send time and a snapshot of what the agent was doing, written to disk, and swallowed so the model never sees it now.
  2. Deliver - on the next Stop, if items are queued, it delivers exactly one (injected as the agent's next instruction via decision: block, which tells Claude to continue rather than stop), moves it to history, and leaves the rest on deck for later stops.

The delivered message carries an envelope: when it was sent (with an elapsed-time delta - the model gets no per-message timestamps otherwise), where the agent was at the time, and an instruction to reconcile staleness - if the request was overtaken by something the agent did in the meantime, push back and confirm rather than blindly executing. That last part is handled by the main model's own judgment against its full context, not a side-call.

Sequential delivery and items

Each queued item delivers on its own, one per stop, oldest first: item 1 lands, the agent works it to completion, stops, item 2 lands, and so on until the queue drains. This is for peppering a running agent with a series of things ("fix this bug", "now this one") and having it work them in order rather than getting all five dumped at once. The queue draining one-item-per-stop is what terminates the loop.

An item can hold more than one part. /qa (append) adds context to the most recent item instead of starting a new one, and all its parts are delivered together in that item's envelope. So: queue item 1, queue item 2, then /qa ... to tack extra context onto item 2 before it goes out.

Usage

Same queue and delivery whether you go in-session or out-of-band.

In-session (/q, /qa, /qs) - type it into the session, even while the agent is working:

/q  migrate the v1 db once the refactor is done and tests pass, then tag the release
/qa oh and bump the version in package.json in the same commit   # append to the last item
/qs                                                              # show queue status

Claude Code holds the input and surfaces it at a natural boundary; the hook catches it there, confirms "✓ Queued", and the agent never sees it until it stops. /qs prints the status to you (items on deck, last delivered, how long ago) without sending anything to the agent.

Out-of-band (qmsg) - from a second shell / SSH pane, the fire-and-walk-away path. Because it runs independently of the turn loop it stamps the true send-time and lands on disk immediately, even mid-task:

qmsg "run the full deploy pipeline after CI is green, then post the version to #ops"
echo "long multi-line prompt ..." | qmsg      # or pipe via stdin
qmsg --append "and also clear the CDN cache"  # append to the last item
qmsg --status                                 # items on deck / last delivered
qmsg --sessions                               # list active sessions to pick from
qmsg --session 4f3a "..."                     # target a session (id prefix is enough)
qmsg --clear [--session <id>]                 # drop the queue

Targeting when multiple sessions run at once. Queues are per-session, but qmsg runs outside any session, so it has to figure out which one you mean. With no --session it targets the single recently-active session; if more than one has been active in the last 30 minutes it refuses to guess and lists them with their cwd and last line, so a message never lands in the wrong session. Run qmsg --sessions to see the ids, then qmsg --session <id-prefix> "...". In the common phone case (one session at a time) the bare form just works.

Install

It installs globally for the user (every Claude Code session, all projects) into ~/.claude, so run it once per machine you use Claude Code on - each machine is a separate install.

git clone https://github.com/fisherevans/claude-code-queue.git
cd claude-code-queue
./install.sh

To upgrade later, git pull and re-run ./install.sh (idempotent).

Idempotent. It copies the hook scripts + qmsg into ~/.claude/hooks/, the /q command into ~/.claude/commands/, puts qmsg on PATH, and merges the UserPromptSubmit + Stop hook entries into ~/.claude/settings.json (adding only what's missing - existing hooks are preserved). Re-run after editing any script.

Existing sessions won't pick up the hooks until they restart - start a fresh session after installing.

Notes and current limits

  • The /q timing depends on when the harness surfaces queued input. Claude Code appears to surface a typed-while-busy message at a natural boundary mid-task, which is what makes /q land on disk before the final Stop. If a future version only surfaces at turn-end, /q still works but the captured send-time drifts toward delivery-time; qmsg is the deterministic fallback with an accurate send-time. This is the one behavior to sanity-check on a live session.
  • Queued while idle: if nothing is running when you queue, there's no upcoming Stop, so it delivers the next time the agent stops after doing anything. Queue while it's working, or just send normally when it's idle.
  • Sequential delivery relies on repeated Stop-hook continuations. Each queued item is one continuation. If a Claude Code version caps how many times a Stop hook may force-continue, a long queue would deliver up to that cap per sitting and the rest on later natural stops - worth checking if you routinely queue many items.
  • If the agent stops to ask you something while items are still queued, the next item is delivered anyway (a stop is a stop). The envelope tells it to reconcile, but in a fully walk-away flow you won't be there to answer the question first. For strictly-ordered work where each step may need you, queue fewer at a time.
  • State lives in ~/.claude/queue/<session_id>.json, one file per session: items on deck (FIFO) plus a history of delivered items for --status. Deleted only via --clear; drained items move to history.
  • Stdlib Python 3 only, no dependencies. Hooks are silent no-ops except on a marker match (capture) or a pending queue (deliver), so they add no noise to normal turns.

About

Queue a message to a working Claude Code agent; it's delivered at the agent's next natural stopping point instead of interrupting mid-task.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages