Progress and an ETA for background shells and Monitor watches, read from their real output or past runs, with no model request.
Why · Features · How it works · Install · Commands · Options · How it compares · Known limits · Privacy
The band above the prompt shows a background shell with its progress and ETA.
Claude can run shell commands, Monitor watches, subagents, and workflows in the background while you keep working. To see how far they got, you ask Claude, and that question costs a model request that reads the whole conversation again. Users ask for progress and ETAs taken from the output itself (#39131, #82741). Downrange reads that output on your machine and shows where each job stands.
- Progress from real output.
45%,[12/40], andStep 3 of 7lines, and the progress lines of tools such as Docker, cargo, pytest, and git, become a progress label, and a bar in the pane. Shell output and Monitor events both count. A ratio in another place, such asloss 12/40, counts only with thegenericRatiosoption on. A shell command that sends its output to a file, such asmake > build.log 2>&1, shows progress from that file. - ETA. From the progress rate. With no rate yet, from the usual duration of the same command in the same folder, or from a Monitor watch's deadline. In the pane, a usual-duration ETA turns yellow once the run passes 1.5 times its usual duration.
- The background tasks of your session. Shell commands (
run_in_background, ctrl+b, or a command moved at its timeout), Monitor watches, background and remote subagents, teammates, workflow runs, and the pending dynamic/loopwakeup. For background sessions, use agent view (claude agents). - Liveness flags.
quiet 3m 00sfor a shell command or subagent that stayed silent for 2 minutes or more, and3 failed calls in a rowfor a struggling subagent. - A table that prints mid-turn.
/downrangeanswers at once, even while Claude works. With theanswerStatusPromptsoption on, a prompt such asstatus?gets the same table. - In step with Claude Code. At each turn end, Downrange checks its rows against Claude Code's task list. It adds tasks it missed, and marks a job that left the list
ended without a report. Running jobs carry across/clear,/resume, and/branch. - Read-only by design. Downrange never stops or changes a task. Built-in
/tasksstops tasks.
The live pane lists a shell, a subagent, and a Monitor watch.
/downrange prints a status table at once, with no model request.
The usage guide explains every view, symbol, note, and message.
flowchart LR
L["Tool calls: launches, wakeups, stops, subagent activity"] --> S[("Jobs and wakeup in session state")]
N["Task notifications"] --> S
P["Stop and SubagentStop snapshots of tasks and crons"] --> S
T["Ticker, once a second while work is live"] -->|"output file ends, agent list"| S
N -->|"clean shell runs"| H[("Usual durations under hashed keys")]
H -->|"usual duration at launch"| S
S --> V["Band, pane, and /downrange table"]
S -->|"session end"| C["Carry for /clear, /resume, /branch"]
C --> S
- Launches. Hooks on the Bash, Monitor, Agent, and Workflow tools record each background launch with its id and title, and a shell's output file. A ScheduleWakeup result sets the wakeup. A TaskStop result marks its job stopped.
- Output. The ticker reads each running shell's output file every 2 seconds, or every 10 seconds above 256 KiB. It keeps the end of the file: the last 16,384 characters, or the last 16,384 bytes through
tailabove 4 MiB. Progress comes from the last 10 display lines of that end. - Redirect files. Some commands send their output to a file, such as
> build.logor| tee build.log. The ticker then also reads the one file that the command's text names, its redirect file, in the same way. At each read, the row shows the file that changed last: Claude Code's output file or the redirect file. On equal times, it shows Claude Code's output file. While the row shows the redirect file, it notesoutput: build.log. The end and the exit code still come from Claude Code's notification. Downrange follows no redirect file on Windows, or in a command with code that may not run, such as anifor a function. - Ends. Task notifications end jobs and carry Monitor events. The agent list updates subagent states. Stop and SubagentStop snapshots add tasks that Downrange missed, and end tasks that left the list after a 10-second grace period. A scheduled prompt that fires from 5 seconds before the wakeup's due time clears the wakeup. So does a snapshot with no one-shot schedule, after the same grace period.
- Usual durations. A clean end (exit code 0 or none) of a shell that Downrange saw start adds its run time to a history keyed by a hash of folder and command. The usual-duration ETA is the median of the last 5 runs.
- Views and carry. The band, the pane, and
/downrangedraw from the same state. The ticker stops when nothing is live. At session end, Downrange keeps the running jobs and the wakeup in memory, and restores them after/clear,/resume, or/branch.
Works with Claude Code 2.1.288 and later. Tested with Claude Code 2.1.296.
Inside Claude Code:
/plugin marketplace add dot-agi/downrange
/plugin install downrange@downrange
/reload-plugins
From a shell:
claude plugin marketplace add dot-agi/downrange
claude plugin install downrange@downrangeFrom a clone, for one session:
git clone https://github.com/dot-agi/downrange
claude --plugin-dir ./downrangeInstall from a shell with Claude Code 2.1.292 or later. The second part turns on auto-update. It runs only when the install succeeds and the marketplace entry exists:
claude plugin install downrange --marketplace dot-agi/downrange --scope user --json \
&& f="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" \
&& jq -e --arg name downrange '.extraKnownMarketplaces[$name].source' "$f" > /dev/null \
&& tmp=$(mktemp "$f.XXXXXX") \
&& { jq --arg name downrange '.extraKnownMarketplaces[$name].autoUpdate = true' "$f" > "$tmp" && mv "$tmp" "$f" || { rm -f "$tmp"; false; }; }Claude Code 2.1.288 to 2.1.291 has no --marketplace option. Add the marketplace first, then run the auto-update part above:
claude plugin marketplace add dot-agi/downrange --scope user --json \
&& claude plugin install downrange@downrange --scope user --jsonAuto-update runs only in interactive sessions. A headless job updates at its start, before the session that uses the mod:
claude plugin update downrange@downrange --jsonCheck the result. The first command prints the installed version. The second prints the latest release:
claude plugin list --json | jq -r '.[] | select(.id == "downrange@downrange") | "\(.version) enabled=\(.enabled)"' \
&& git ls-remote --tags --refs https://github.com/dot-agi/downrange 'downrange--v*' | sed 's/.*--v//' | sort -V | tail -n 1Notes:
- On a host with no SSH key for GitHub, set
CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1in the environment of everyclaudecommand and session. - New code loads in the next session, or after
/reload-plugins. An agent cannot type/reload-plugins, so it starts a new session. - Running
installagain does not update. Useupdate. - The
mvreplaces the settings file, with mode 600. Ifsettings.jsonis a symbolic link, edit its target instead.
| Command or key | What it does |
|---|---|
/downrange |
Prints the status table at once, mid-turn too |
/downrange pane |
Opens or closes the live pane |
j on the focused band |
Opens the pane |
h on the focused band |
Hides the band until a new job starts |
c in the focused pane |
Clears finished jobs |
x in the focused pane |
Closes the pane |
Focus the band or the pane with Ctrl+X, then Tab, or a click. That chord is Claude Code's own abovePrompt:focus binding. Release Ctrl before you press Tab, because many terminals read Ctrl+Tab as next tab. In Emacs vterm, eat, or ansi-term, click the band, use /downrange pane, or rebind abovePrompt:focus with /keybindings.
Set options in /config, or under pluginConfigs["downrange@downrange"].options in ~/.claude/settings.json. For a --plugin-dir load, the key is downrange@inline.
| Option | Default | Effect |
|---|---|---|
band |
true |
Shows the band while background work is live |
answerStatusPrompts |
false |
When Claude is idle and a job runs, answers an exact status prompt (status?, what's the status?, give me a status update) with the table. Send the same prompt again within 60 seconds to ask Claude |
genericRatios |
false |
Reads progress from a ratio in any place in the output, such as loss 12/40. Off, only known formats count, such as a percentage, or a ratio in brackets, at the start of a line, after a step word such as Step, or after a progress bar. On shows progress for more jobs, and more false readings, such as a date like Released 10/11 |
| Where | Band | Pane | /downrange |
Local status answer |
|---|---|---|---|---|
| Terminal, fullscreen | Yes | Docked from 110 columns, else above the prompt | Yes | When the option is on |
| Terminal, main screen | Yes | Above the prompt | Yes | When the option is on |
| Desktop app, Code tab | Yes | Yes | Yes | No |
| VS Code panel | No | Not verified; use /downrange |
Yes | No |
| Agent SDK and stream-json sessions | No | No | Yes | No |
| Mobile app through Remote Control | No | Not verified; the terminal on your machine shows it | Yes | No |
| Feature or project | What it does | What Downrange adds |
|---|---|---|
Built-in /tasks |
An interactive dialog that lists and stops background shells, subagents, Monitors, and MCP tasks | Live progress, ETA, last output, the pending /loop wakeup, and a text table that prints mid-turn and in SDK sessions. Downrange does not stop tasks |
| Built-in subagent panel | Rows of running background subagents below the prompt, with a key that stops them all | Shells, Monitors, wakeups, and progress |
Built-in /workflows |
A progress line per workflow run, and a view of its phases | Each workflow run as one row beside the other jobs. Downrange shows no phases |
Built-in agent view (claude agents) |
Background sessions with their state, and the /loop run count and countdown. A !cmd shell job shows its last output line |
The background tasks inside one session |
| rabe | A band and pane with subagents, workflows, background shells, Monitors, cron jobs, and /loop wakeups with the next run time. It can stop jobs and message agents, and it estimates cost per agent |
Progress and ETA parsed from job output, a text table, and running jobs kept across /clear, /resume, and /branch |
| Flotilla | Bash, PowerShell, and Monitor background tasks with command, run time, and end state. An agent tree with model and cost, workflow bars, a timeline, and a stuck-agent alert | The last output line, progress, and ETA for shells, the /loop wakeup, quiet flags from output silence, and the carry across /clear |
| claude-deck | Shells and agents with run time, a stop button per shell, job steps as a tree, recently ended commands, cost and usage meters, and failure toasts | Monitor watches, the /loop wakeup, progress and ETA from output, and running jobs kept across /clear |
| claude-flightdeck | A permission gate view, model vitals, and rich subagent cards and swimlanes | Progress and ETA read from the output of background shells and Monitors |
| sprout | Subagent cards with cost and a file tree. Its progress comes from a note it injects for Claude and a step tool that Claude calls | Progress read from real job output, with no tool or note for Claude |
- Progress. Percentages,
[3/7],Step 3 of 7, and the progress lines of Docker, cargo, pytest, git, and similar tools. - Jobs. Background shells, Monitor watches, subagents, teammates, workflow runs, and the dynamic
/loopwakeup. - Stalls. A shell or subagent that stays silent for 2 minutes, and a subagent with 3 failed calls in a row.
- ETAs. From the progress rate, the usual run time of the same command in the same folder, or a Monitor watch's deadline.
- Progress. Ratios outside known formats, such as
loss 12/40, unlessgenericRatiosis on. Dates such as[10/11]can still count. - Jobs. Fixed-interval loops such as
/loop 10m, other cron schedules, jobs after/backgrounduntil the next turn end, and work that goes on after its job ends.nohupalone does not end a job at once. A command that ends with&does. Downrange follows no file of a command with&ornohup. - Stalls. Stuck remote subagents, teammates in their own panes, and waiting agents. They show no
quietflag. - ETAs. Run time in the foreground before ctrl+b or a timeout move. In output silence, a rate ETA runs down to
<1mand stays there.
The list below holds the limits you can hit in daily use. docs/usage.md has every limit, also the rare ones: clashes with other schedules, size caps, races, failed reads, closed teammate panes, and unusual setups or task descriptions.
Read the limits you can hit in daily use
- Only dynamic
/loopwakeups. Only wakeups thatScheduleWakeupcreates appear. Fixed-interval loops such as/loop 10mand other cron schedules do not. - Progress read from output. Progress is a guess from the text. By default, a ratio counts only in a known progress format: in brackets, at the start of a line, after a step word such as
SteporEpoch, after a drawn bar, or in Docker Compose's[+] Running 3/7. A ratio in such a place that is not progress still counts. A short date counts, such as[10/11]or10/11 deployat the start of a line, unless a two-digit clock follows it, as in10/11 14:03. So doesN of Mafter a step word and before a space inside a path, such asPart 1 of 2 (live)/a.flac, and a percentage that is not progress, such asCPU 45%. Real progress in another place, such asloss 12/40or[12:34:56] 3/15 done, does not count. With thegenericRatiosoption on, a ratio counts in any place, soReleased 10/11,Disc 1 of 2 (live)/a.flac, and other ratios that are not progress count too. These change the bar and the rate ETA. In both modes, full dates such as2026/10/11and10/11/2026do not count. Neither doesN of Mjoined to a path or a number, such as/backups/3 of 7/,version 1.2 of 7.0, or3 of 7.5 GB. The date rule also drops real progress of 1 to 12 out of at most 31 before a clock, such as3/15 00:12. - ETAs during output silence. When output stops, the bar keeps its last reading, and the rate ETA keeps counting down from the time of that reading. Once the estimate runs out, the ETA stays at
<1mwhile the job runs. This holds afterquietshows too, so a row can show20%,<1m, andquiet 2m 00stogether. A Monitor whose events stop behaves the same way. - Monitor deadlines and rate ETAs. A Monitor watch shows its deadline only when it has no progress-based ETA. Once its progress gives a rate ETA, or reaches 100% and shows
finishing, the deadline no longer shows. A rate ETA can run past the deadline: a 30-minute watch that goes from 1% to 2% in its first minute shows~1h 38m, although its deadline is 29 minutes away. - Time in the foreground before a move to the background. A shell job's clock starts when the Bash call returns its background result. A command that first ran in the foreground, such as one moved at its timeout or with ctrl+b, loses that time. Its age, its
tooktime, and the run it adds to the duration history count only the time after the move. For example, a command moved after 2 minutes that ends 1 minute later showstook 1m 00sand records 1 minute. Runs of one command in one folder share one history, whatever the launch mode. The next run of that command that starts in the background then shows~1m usual, even when it needs 3 minutes, and in the pane it can turn yellow early. A moved run can get a usual-duration ETA that is too long. - Tasks first seen in a snapshot. Claude Code's turn-end snapshot can list a Monitor as a shell. A launch result is the result of the Bash or Monitor call that started a task. A Monitor first seen in a snapshot shows as a shell until its launch result, its first event, or its end notification arrives. Only its launch result gives it a deadline. A shell first seen in a snapshot shows no last line, progress, ETA, or
quietflag until its launch result or a notification names its output file. Only its launch result brings a usual-duration ETA. A task that started before Downrange loaded, such as one in a session moved to another process, gets no launch result. Claude Code's internal tasks also appear, under their engine labels, and a long-lived one can keep the band visible. Use Hide to dismiss the band. - Commands that subagents start. A command with the note
ends with its agentends when its subagent answers. It then showsended with its agentand no exit code, unless its own notification arrives later. - Agents that wait. A subagent that the agent list reports as pending, waiting, or idle shows that word, beside its tool count when it shows one. It shows no
quietflag and no failed-calls flag, however long it stays in that state. - Agents that run elsewhere. Downrange sees the tool calls only of agents that run in this Claude Code process. A remote subagent shows
remote, and a teammate in a terminal pane of its own showsown pane. Their rows show no tool count and noquietflag, so a stuck or dead one looks the same as a busy one. A row whose launch result Downrange missed, or that an earlier version of Downrange saved, still keeps0 toolsand showsquietfrom 2 minutes after launch, even while the agent works. Its notification or a snapshot still ends it. - A session moved to another process.
/backgroundand a hand-off to the desktop app start a fresh process that resumes the conversation. Downrange starts empty there. Running tasks return at the next turn end, with≥ages and no last line. A shell's notification can still name its output file, end it, and show its last line. The/loopwakeup returns only with the nextScheduleWakeupresult.
Downrange watches events and changes almost none of them. It makes no network request, writes no file of its own, and adds no tool for Claude.
-
What it sends, and where. Downrange itself sends nothing off your machine. Its output goes to your screen: the band, the pane, the
/downrangetable, the local status answer, anddownrange:diagnostic lines in the transcript and the debug log. Two of these go further through Claude Code itself:- The
/downrangetable enters the conversation, so Claude reads it with its next request, as it reads any command output. The table cuts last output lines to 80 characters and removes control characters. A subagent row in the table shows only the tool it last used; the pane also shows that tool's input in short. - Where a remote surface draws the band or the pane, Claude Code sends that drawing to the surface.
- The
-
The program it starts. Only
tail -c 16384 <file>, with a 2-second time limit, to read the end of a large output. It runs only when the file it reads for a background shell is over 4 MiB, the largest file Claude Code's file read accepts. It prints the last 16,384 bytes of that file. Downrange passes it as an argument list, with no shell. The file path is the only part that varies. That path must be the output file of the job's own task id (absolute, in atasksfolder, and named<task id>.output), or the job's redirect file (see the next item). An organization policy that refuses mods which run host commands also refuses this mod. -
Local data it reads. Downrange reads these files, and no other file:
- The output file of each background shell, which Claude Code writes in its
tasksfolder, with its size and change time. - The redirect file of a background shell, with its size and change time, at each read of its output file. Downrange reads the redirect file's text when that file changed last, or while the output file holds no line of the job. That is the one file that the command's own text sends its output to, such as
build.loginmake > build.log 2>&1. Downrange reads it only when it is a regular file, never a path under/dev, and never on Windows. - Its own duration store, a JSON file that Claude Code keeps for the mod in your Claude Code configuration folder.
It also reads the session's working folder, to hash it with a command and to place a relative redirect file, and the list of the session's agents and their states.
- The output file of each background shell, which Claude Code writes in its
-
Conversation data it reads. The input and result of the tool calls in the hook table below: a command or description, the task id, the output file path, and the wakeup time and reason. Downrange reads a background command's text to find its redirect file. One input field of each tool call a tracked subagent makes (see the last item). Task notifications. The
background_tasksandsession_cronslists of Stop and SubagentStop hooks. The text of a prompt you type, only whenanswerStatusPromptsis on, to compare it with the status phrases. Downrange reads no transcript file. -
What it keeps. Session state, which Claude Code holds for the session and which other mods in the session can read. It holds job titles, last output lines, notes, tool summaries, and the wakeup's reason, each cut to 200 characters or fewer. It also holds task ids, output file paths, redirect file paths, progress, exit codes, times, and the last status prompt it answered. The duration store holds run times under a hash of folder and command, with no command text, no path, and no output.
-
Permissions. Downrange never changes a permission decision. No hook denies or approves a call, rewrites a tool's input, or answers a permission prompt. Claude Code and you decide as before.
claude plugin validatelists most hooks as gating hooks with a.catch, because their events can block. Each such.catch, such as the one onclassic.SessionStartinhooks/register.tsx, only logs the failure and passes the event on unchanged withnext(e). The/downrangecommand and the Jobs pane show the failure instead. -
The
Reflect.getcall inhooks/jobs.ts. It reads one named field of a tracked subagent's tool input, from a fixed list in that file. The fields arecommandfor Bash and Monitor, the file name fromfile_pathornotebook_path, thepatternof Grep and Glob, the host of a WebFetchurl, thequeryof WebSearch, and thedescriptionof Agent. That field becomes a summary such asEdit: view.ts, cut to 60 characters. Each tool's input has its own shape, so the code reads the field by name. It calls no function and reads no other field. Other tools show only their name.
Every hook, what it does, and what it changes
| Event | What the hook does | What it changes |
|---|---|---|
session.start |
Registers /downrange, and loads the session's jobs and wakeup |
Nothing |
session.end |
Keeps the running jobs and the wakeup in memory for the next session | Nothing |
classic.SessionStart after /clear, /resume, or /branch |
Puts the kept jobs and wakeup back, and starts the once-a-second timer | Nothing. It adds no context, instructions, or settings |
command.run for downrange only |
Prints the table for /downrange, and opens or closes the pane for /downrange pane |
Only its own command |
tool.call for Bash, Monitor, Agent, Workflow, ScheduleWakeup, and TaskStop |
Lets the call run, then reads its result to record a background launch, the wakeup, or a stop | Nothing. The call's input and result stay as they were |
tool.call for any tool |
For a subagent that Downrange tracks, counts the call and keeps a short summary of it | Nothing |
session.append |
Reads a subagent's stored task notification to end jobs | Nothing |
turn.complete |
After a subagent answers, ends the commands tied to that answer and reads the agent list | Nothing |
prompt.submit |
Reads task notifications to end jobs, and a scheduled prompt to clear the wakeup | Only with answerStatusPrompts on: it drops an exact status prompt while Claude is idle and a job runs, and shows the table instead |
classic.Stop, classic.SubagentStop |
Reads the background_tasks and session_crons lists in the hook input to correct jobs and the wakeup |
Nothing. It never blocks a stop |
ui.render |
Draws the band above the prompt and the Jobs pane | Adds the band on top of what the prompt area already shows. Draws only its own pane |
Missing command, missing output, stalled ages, and debug lines
| Symptom | Cause | Fix |
|---|---|---|
/downrange is missing |
Claude Code rejected command registration | Read the cause after downrange: session.start: /downrange was not registered: in the transcript. A built-in name conflict is one possible cause. Use the band's Open button |
A job shows no output file |
The tool result named no output file | Run claude --debug and search for downrange: tool.call: |
A job shows output unavailable or tail failed |
The output file or the redirect file is gone, or tail is not on PATH (common on Windows, for outputs over 4 MiB) |
Run claude --debug and search for downrange: clock.every: |
/downrange pane prints The Jobs pane is not shown here |
Nothing draws in this session, or the surface places no panes | Use /downrange |
| Ages and output lines stop moving | Another mod refused a timer period | The next change starts the timer again; claude --debug shows the ticker stopped |
| Jobs end only when Claude's turn ends | Some Claude Code notices are plain text and carry no structured end status | No action for plain notices. If claude --debug shows a task notification held no complete or unknown notification status, report that line |
A job shows ended with its agent |
The background command of a subagent that Claude waited for ends when that subagent answers | Nothing |
| Something else looks wrong | A hook failed | The first failure of each hook shows as a dim downrange: line; claude --debug has every one |
The usage guide lists every message.
See CONTRIBUTING.md.
MIT. Copyright (c) 2026 dot-agi.
