npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@azure-id/orc-cli

v0.3.0

Published

Run an ORC lane in a Claude Code session it drives itself, and watch it work.

Readme

orc-cli

Run an ORC lane and watch it work.

orc-cli is a terminal app that starts a Claude Code session, sends it an ORC slash command, and replaces the wall of scrolling conversation with a live run view: a phase rail, agents running side by side, honest progress, a running token count, and a modal when the lane needs an answer from you.

 ◒ /orc  dispatch   00:39
   subagents are writing code, in conflict-free waves

 ████████████████░░░░░░░░░░░░░░░░░░░░░░░░  2/7 tasks  wave 2 of 3

  ✔ intake         │  write Edit  src/api/limit.js  2s
  ✔ plan           │    Splitting the limiter from the route handler first.
  ● dispatch       │
  ○ review         │ agents 2/4 returned
  ○ verify         │  ⠋ orc-executor-opus-5-high    ▰▰▱▱▱▱▰▰▱▱  00:37
  ○ ship           │  ⠋ orc-executor-sonnet-5-high  ▱▰▰▱▱▱▱▰▰▱  00:31
                   │  ✔ orc-executor-opus-5-high    ──────────  01:12
                   │  ✘ orc-executor-opus-5-high    ──────────  00:48
                   │ trace
                   │  PAUSE  batch boundary after wave 2
────────────────────────────────────────────────────────────────────
 in 12  cache-w 30,995  cache-r 22,123  out 411   cost $1.41
────────────────────────────────────────────────────────────────────
 [t] transcript   [esc] stop   [q] quit

Table of contents


What you need first

Three things, and orc-cli checks all three before it lets you start:

| | What | Check it with | If it's missing | |---|---|---|---| | 1 | Node.js 18 or newer | node --version | nodejs.org | | 2 | Claude Code, signed in | claude --version | npm i -g @anthropic-ai/claude-code, then claude once to log in | | 3 | ORC, installed in the project you want to work on | orc --version | npm i -g @azure-id/orc, then orc init inside that project |

ORC is per project. Every repo carries its own .claude/ — its own config, its own tier rules, its own wiki. orc-cli reads the settings of whichever repo you point it at, exactly as if you had typed the slash command in Claude Code yourself.

Do you already use ORC in Claude Code? Then you already have everything. orc-cli does not install, change or configure anything — it just runs the lanes you already have.

CAUTION — on ORC older than 0.56.0. ORC moved from the unscoped orc package to @azure-id/orc while keeping the same orc command. npm links a command only if the file is unowned or owned by the installing package, so the old global package makes the new one unlinkable and every install fails with EEXIST on the command file — from any source. npm i -g -f "works" and is the worst outcome: it overwrites the shim and leaves the superseded package installed underneath, owning nothing and updated by nothing. The one-time fix:

npm uninstall -g orc && npm i -g @azure-id/orc && orc update

Install

npm i -g orc-cli

That is the whole install. No build step, no configuration file, zero runtime dependencies. It works on Windows, macOS and Linux because it is plain Node — npm writes a .cmd/.ps1 shim on Windows and a symlink elsewhere.

Check it worked

orc-cli --version
# orc-cli 0.1.0 · claude 2.1.246 · orc 0.55.0

Three versions on one line, on purpose: every support question starts with "which versions?", so orc-cli answers it before you have to ask.

If any of them shows —, that tool is not on your PATH. Fix that first.

Check your project

cd into the repo you want to work on and run:

orc-cli doctor

You get a report and an exit code you can script against:

| Exit | Means | What to do | |---|---|---| | 0 | ready | go | | 1 | degraded but usable | you can run; the findings tell you what is drifting | | 2 | cannot run | fix the named problem first — there is no key that skips it |

Every finding names the exact command that clears it:

project  C:\dev\my-app  (walked up from C:\dev\my-app\src)
versions orc-cli 0.1.0 · claude 2.1.246 · orc 0.55.0
log_dir  C:\dev\my-app\.claude\orc\logs
run_dir  C:\dev\my-app\.claude\orc\run
source   orc where --json

WARN   Installed payload 0.41.0 vs package 0.55.0.
       → orc update

degraded but usable

Installing from source

git clone <this repo>
cd orc-cli
npm link          # puts `orc-cli` on your PATH from the working copy
orc-cli --version

There is nothing to npm install — the dependency list is empty.


Your first run

cd ~/code/my-app     # a repo that has ORC installed
orc-cli

Then:

  1. Preflight appears. Read the findings. Press enter.
  2. Lanes appears. Move with ↑/↓. The highlighted lane expands to show what it does, its phases, and which model it will run at. Press enter.
  3. Compose appears. Type what you want done, in your own words. The line under the box shows you exactly what will be sent — /orc <your text> — because nothing is templated or rephrased on the way through. Press enter.
  4. The run view takes over. Watch it. Press t at any time to see the prose the animation is summarising.
  5. If the lane needs something, a modal interrupts and the clock stops. Answer it. The run picks up exactly where it was.
  6. Finish shows what happened, what it cost, which files moved, and asks what you want to do next.

Skipping the menu

If you already know what you want:

orc-cli --lane /orc-fast "add a subtract function next to add, same style"

On Git Bash / MSYS on Windows, a leading / gets rewritten into a path. orc-cli detects this and matches on the lane name anyway, so --lane orc-fast and --lane /orc-fast both work.

Running against a different project

orc-cli --dir ~/code/other-app

A --dir that does not exist is an error, never a silent fall back to the current directory.


The screens, in order

Every screen carries the same header: which project is resolved, and how. That is never off-screen, because it is the one piece of context that changes what every other number on the page means.

| Screen | What it is for | |---|---| | preflight | Claude Code, ORC, versions, paths, and ORC's own drift findings. Blocks at level 2. | | project | Only appears when nothing resolved. Recent projects, plus a folder browser. | | lanes | The lanes installed in this project. An unfinished run is offered here first. | | compose | Your request, plus the exact string that will be sent, plus the permission allowlist. | | running | Phase rail · progress · agent tracks · trace · live token counts. | | ask | The lane needs an answer. The clock stops. | | permission | A tool wants to do something. Shows the exact input, never a summary. | | finish | Verdict · cost · files · what now. | | error | What happened, the exact command, and what to do next. |

How the project is chosen

  1. --dir <path> if you gave one.
  2. Otherwise walk up from your current directory looking for .claude/.
  3. If nothing is found, the project screen offers your recents and a folder browser.

If you pick the wrong folder, that is your call and orc-cli obeys it — it shows you what it resolved, prominently and permanently, and does not second-guess you. The folder browser lists directory names only; it never reads a file.


Every key

| Key | Where | What it does | |---|---|---| | ↑ ↓ | menus | move | | enter | everywhere | confirm / send | | esc | most screens | back · during a run, stop the run | | 1–9 | ask modal | jump to that option (see below) | | space | ask modal | toggle, on a multi-select question | | pgup pgdn | anywhere long | scroll. Nothing is ever cut without a marker and a key that reveals it. | | / | lanes | filter, by name or blurb | | alt-m | compose | pick the model and effort for this run | | alt-enter | compose, ask | a newline, rather than send | | alt-t | running, ask, finish | toggle the transcript | | alt-c | ask modal | show what the turn said before the question | | t | running, finish | toggle the transcript | | a | lanes | show/hide lanes that are installed but untested here | | r | lanes | resume the unfinished run being offered | | o | lanes, finish | open orc ui for this project | | b | project | browse for a folder | | y / n | permission | allow once / deny | | s | finish | stay on this lane — same session, context kept | | n | finish | new lane — fresh session, full context cost again | | q | everywhere | quit | | ctrl-c | everywhere | quit, restoring your terminal |


Every command and flag

orc-cli                        resolve the project from cwd and pick a lane
orc-cli --dir <path>           run against a specific project
orc-cli --lane /orc "text"     skip the menu, go straight to a run
orc-cli --resume               go to the unfinished run in this project
orc-cli ui                     open `orc ui` for the resolved project, print the URL
orc-cli doctor                 preflight report   (exit 0 / 1 / 2)
orc-cli --replay <file>        drive the kernel from a recorded stream
orc-cli --check                render every screen headlessly and exit

| Flag | Effect | |---|---| | --model <row> | default · opus-4-8-high · opus-5-medium · opus-5-high. An unknown id is an error naming the rows, never a silent fall back | | --all | show lanes that are installed but not in the tested registry | | --no-motion | freeze every animation | | --ascii | ASCII glyphs only, for a legacy console | | --no-color | no colour (NO_COLOR and CI also work) | | --version | orc-cli, claude and orc versions on one line | | --help | keys and flags |

--replay and --check are real commands, not test scaffolding. --replay drives the whole event kernel from a recorded run — no model, no network, no money — and prints what the run did:

orc-cli --replay fixtures/orc-fast-fallback-to-mini.stream.jsonl
lane        /replay
end         finished
agents      1 (1 returned)
            orc-executor-sonnet-5-high · done
progress    — (no plan — nothing to count)
usage       in 58 · cache-w 237776 · cache-r 945498 · out 129
cost        $0.4360
files       .../app.js

The lanes

The lane list is derived, never hardcoded — and since 0.3.0 it is derived from ORC's own registries rather than from a directory listing:

orc lane list   --json    every lane, its command, and how many keys it reads
orc lane phases --json    each lane's own phases, its trace tier and token,
                          and which trace verbs each phase emits
orc lane config --json    what this lane resolved to: where it will STOP,
                          what it must announce, which agents it will dispatch
orc lane calls  --json    every CLI command the lane will run, free or paid

A fact ORC publishes is rendered, never re-derived. So:

  • every lane gets a real rail in ORC's own words — Q1 — LOOK, F2 — Slice build + dispatch, D5 — Outline — including the twenty-four lanes orc-cli has never tested;
  • lanes are grouped by ORC's own trace tier, which is also how you can tell at a glance which ones write no trace at all;
  • a lane ORC lists with no command of its own — orc-judge, orc-advisor, context-combiner, orc-analyze-mini — is never offered, because typing it does nothing. A directory listing would happily offer all four;
  • an untested lane is marked with a fidelity chip saying what is degraded (rail from ORC · not tested here) rather than a bare (unregistered).

If your ORC has no orc lane (below 1.0.0) nothing breaks: orc-cli falls back to its own four-lane table and the lane screen says so. It is a capability probe, not a version floor — MIN_ORC is still 0.49.5.

The four tested lanes

| Lane | What it does | Its rail | Runs at | Why that tier | |---|---|---|---|---| | /orc | The full pipeline: intake → plan → scored parallel waves → review → verify → ship | intake · plan · dispatch · review · verify · ship | opus, high | ORC's PreToolUse guard hard-blocks /orc below high effort | | /orc-mini | One executor, smoke gate, ship. No full review or verify | intake · plan · build · smoke · ship | opus, high | not gated; high recommended | | /orc-fast | Knowledge-gated single executor. Needs a fresh wiki + a cached pattern, or it hands off to /orc-mini | preflight · build · smoke · ship | sonnet, medium | it is designed to run there — never gate it up | | /orc-quick | Look → ask → do, looping. Asks which agent before every dispatch | look · ask · do | opus, medium | not gated |

Every rail is the lane's own. /orc-quick has no planning phase and never shows one; /orc-fast has no review phase and never shows one. The phase words come from src/kernel/phases.js, which is the single place both the lane menu and the run screen read.

Choosing the model

Press alt-m on the compose screen, or pass --model:

| Row | What it does | |---|---| | default — your Claude setting | reads model from settings.json (project first, then user) and shows which file it read. opus is an alias, so orc-cli reports it as one and does not claim to know which Opus that resolves to | | opus 4.8 · high | the tier ORC's effort guard is written around | | opus 5 · medium | cheaper. See the caution below | | opus 5 · high | strongest |

A model id orc-cli did not read is never printed. That is ORC's own catalogue rule, for the same reason: a shipped model id is wrong within a quarter, and wrong silently.

The choice is per run and is never written into the project. .claude/ stays untouched, as it always has.

The honest caution about opus 5 · medium

ORC's effort guard blocks /orc below high effort unless the session-model bridge at .claude/orc/session-model.json names an Opus 5 / Fable 5 model and is under 30 minutes old. That bridge is written by ORC's statusline — and a headless session has no statusline, so under orc-cli it is normally stale or absent.

orc-cli reads that bridge (reading is allowed; writing is not) and tells you the truth on the row:

opus 5 · medium — will clear (bridge 4 min old)
opus 5 · medium — blocked for /orc here — ORC's effort guard needs a fresh
                  statusline bridge, and a headless session writes none.
                  Every other lane is unaffected.

A blocked row is shown, disabled, with the reason — never hidden, and never silently swapped for a different tier. The guard matches the exact skill names orc and orc-diy and nothing else, so every other lane is free at any effort.

Writing that bridge ourselves would unlock the row and would break the zero-writes boundary, so we do not. The real fix belongs in ORC.

Why /orc is pinned to high

This is not a guess — it was measured before any UI was written (spike/M0-RESULT.md). ORC installs a PreToolUse hook that reads effort.level and blocks /orc unless the session is at high effort or stronger. Opus 5 can clear at medium, but only through a session-model bridge that ORC's statusline writes — and a headless session has no statusline, so that bridge is stale or absent. orc-cli therefore launches /orc at high and never leans on the medium allowance.

Driving the real guard, for the record:

effort=<none> -> blocked      effort=high  -> allowed
effort=low    -> blocked      effort=xhigh -> allowed

orc-cli never bypasses this guard. If a lane is blocked, that is ORC telling you something true.


What you see while it runs

The run screen answers one question: what is happening right now. It answers it in four places, and each one is honest about where it got its answer.

1 · The phase, and what that phase means

 ✦ /orc-quick  do  entry 2   00:38
   the agent you picked is working

The second line is not decoration. dispatch, smoke and preflight are ORC's words, and a word is not an explanation — so every phase of every lane carries one line of plain English saying what is going on inside it.

entry 2 appears only after orc-cli has watched the rail go backwards: a second request in /orc-quick, a repair round in /orc. It is a count of what happened, never a prediction of how many there will be.

2 · A rail fed by two sources, which do not rank equally

ORC writes PHASE-EDGE <family> into its trace whenever the role family of a dispatched agent changes. That is authoritative — but it only exists when a pinned orc-* agent is dispatched. /orc-quick dispatches its recon ad-hoc by model and effort on purpose, so a whole /orc-quick run emits one edge, near the end, called execution. A rail that can only ever light one lamp is not a rail, and that is exactly what earlier versions showed.

So the rail is also fed by what Claude Code is observably doing — a dispatch, a write, a test run, a commit — and it says which it is:

| Marker | Meaning | |---|---| | ● | ORC named this phase itself. Authoritative. | | ▸ plus an inferred label | orc-cli worked it out from the stream. | | ✔ | done · ○ upcoming · ✘ skipped (a skipped phase keeps its slot) |

Since 0.3.0 there is a third source, and it is deterministic. orc lane phases publishes the trace verbs each phase emits:

orc-quick   Q0 [GATE] · Q1 [GATE, WIKI-CONSULT] · Q3 [FINISH, OUTCOME, VERIFY]
orc-fast    F2 [GATE, VERIFY, WIKI-CONSULT] · F4 [FINISH, OUTCOME]

A verb belonging to exactly one phase places the rail outright — a WIKI-CONSULT line in a /orc-quick run can only be Q1. A verb belonging to several narrows the candidates and moves nothing: an ambiguous signal is still a signal, but it is not a decision. Certainty only ever goes up — when ORC names a phase we already inferred, the lamp stays where it is and the marker becomes authoritative.

That is what retires the limitation this README used to describe as permanent.

Two rules keep the inference honest, and both are tested:

  1. A tool call may only ever move the rail forward. Reading a file during ship does not mean the run went back to look.
  2. Only a dispatch, a question, or a real PHASE-EDGE may move it backward — and when one does, that is a new round, so the counter goes up and the screen says so.

Because the same Bash tool is a knowledge probe, a test run, a build and a commit, the command itself is classified before anything is drawn — which is also where the read / write / test / build / ship / probe word on the NOW line comes from.

3 · The NOW line

  write Edit  src/api/limit.js  2s
    Splitting the limiter from the route handler first.

The tool that is running and the one thing it is about, plus the last thing ORC actually said. A spinner tells you a process is alive; this tells you what it is doing. If a tool has been running a while, its age is shown — a slow step and a stuck step should not look the same.

4 · Each lane's own animation

An indeterminate bar is drawn when — and only when — there is genuinely nothing to count, and the caption always says why. But the motion belongs to the lane, so you can tell which lane is running from across the room:

| Lane | Motion | Reads as | |---|---|---| | /orc | ▰▰▱▱▱▱▰▰▱▱ a conveyor flowing right | a pipeline moving work through in waves | | /orc-mini | ····˙˙˙▪··· one small walker, one small step | one executor, no parallelism | | /orc-fast | ─────═»»▸── a streak crossing the width in under a second | speed | | /orc-quick | ···✧✦✧······ a repair head that darts, sparks, darts again | short bursts of fixing | | anything else | ░░░████░░░░ the plain sweep | we have not tested this lane's shape |

The same motif appears as a single character next to the lane in the menu and on the compose screen, so the animation is introduced before you commit to it.

--no-motion removes the motion rather than slowing it, and every motif has a still frame designed as a still frame — a capped animation freezes mid-cycle, and a frozen cycle looks exactly like a hang. --ascii swaps every glyph.


When ORC asks you something

Knowing that the lane is waiting is deterministic — it comes from facts, not from reading the prose:

turn ended  +  .current present              ->  ASK      (the lane wants something)
turn ended  +  .current SEEN, now gone       ->  FINISH   (ORC's own FINISH removed it)
turn ended  +  .current NEVER seen           ->  ASK      (no run started, so none finished)
turn ended  +  RESUME.md present             ->  PAUSED   (a wave stop; resumable)

That third line matters more than it looks. ORC creates .current on its first dispatch, and every lane asks its intake questions before it dispatches anything — so "no .current" means "hasn't started yet", not "already done". A run that never started cannot have finished.

Note also that a turn ending is not the child process exiting: in stream-json mode the session stays alive and accepts the next turn. Only ORC's own state says a run is over.

Only the shape of the question is a guess, and it degrades in three steps:

  1. The lane used a structured question → you get the real options as a list. No guessing at all.
  2. The lane wrote prose ending in a numbered list → you get the prose exactly as written, and the numbers work as shortcuts.
  3. Anything else → you get the assistant's message verbatim and a text box.

A question is never hidden because parsing failed, and an option the lane did not offer is never invented. The worst case is "here is exactly what ORC said, type your reply" — which is the normal terminal experience, not a broken one.

The number keys

Pressing 3 moves the highlight to option 3 and types 3 into the box, so you can always see what you pressed. On enter:

  • if the box contains only a digit naming an option → that option is sent;
  • otherwise → whatever you typed is sent, exactly as typed.

So 2 enter picks option 2, and 22 requests per second enter sends that sentence. Typing an answer that starts with a number never loses characters.

The clock stops

While a modal is up, the elapsed timer freezes and the header says waiting on you. Your thinking time is not counted as run time. Answering resumes it exactly where it was.


Permissions

A headless Claude Code session resolves permissions from the settings already on disk — there is no interactive prompt to hook into (measured in M0). So orc-cli uses an explicit allowlist, shown to you on the compose screen before the first run:

Read  Glob  Grep  Write  Edit  Task  Skill  TodoWrite  Bash
  • orc-cli never passes --dangerously-skip-permissions or any blanket bypass. A test asserts this against the actual argv.
  • Anything Claude Code refused is reported on the finish screen. A denial is a fact you must see, not something to quietly swallow.
  • If a future Claude Code version does offer an interactive callback, the modal is already implemented and nothing else changes.

Where your run is written down

Every run writes a full transcript to disk, regardless of what is on screen. The animation is a view over the truth, never the only copy of it. There is deliberately no setting to turn this off — a record you can switch off is off on the run you needed it for.

| | Location | |---|---| | Windows | %APPDATA%\orc-cli\transcripts\ | | macOS | ~/Library/Application Support/orc-cli/transcripts/ | | Linux | ~/.config/orc-cli/transcripts/ |

Two files per run:

  • run-<lane>-<slug>-<DDMMYY>-<HHMMSS>.md — readable: the prompt, the prose, the dispatches, the questions and your answers, the ending.
  • …​.stream.jsonl — every raw event, so a run can be replayed later with orc-cli --replay.

The path is printed on the finish screen and again when you quit.

Transcripts live in your config directory, not in your repo. orc-cli writes nothing into a project, ever. The same directory holds recents.json — the list of projects you have opened. That is the only file orc-cli owns.


Changing settings

It doesn't. That is deliberate.

ORC's configuration belongs to the repo it lives in, and is edited by the tools that already own it — the orc config CLI and orc ui. A second place to change a setting is a second source of truth, and the losing one is always the one you last used.

So when you want to change something, press o on the lane or finish screen (or run orc-cli ui). That shells out to the real orc ui for the resolved project and prints the URL. orc-cli never starts the server itself, never manages its lock, never passes it a port and never renders any part of it.

If the panel cannot be launched, you get the exact command to paste and exit code 0 — a door that could not open is not an error page.


Troubleshooting

orc-cli: command not found The global npm bin directory is not on your PATH. npm bin -g prints it; add that to your PATH and open a new shell.

orc-cli needs a terminal. You piped or redirected the output. The interactive app owns the terminal. For a non-interactive report use orc-cli doctor, which is built for exactly this.

No .claude/ found walking up from … You are not inside an ORC project. Either cd into one, pass --dir, or let the project screen browse for one. If the repo genuinely has no ORC, run orc init there — orc-cli will not do it for you.

Preflight says "cannot run" and enter does nothing. That is correct. Level 2 means a prerequisite is missing, and the finding names the command that fixes it. There is no key that skips a level 2.

/orc is blocked by the effort guard. It should not be — orc-cli launches it at high. If you see this, ORC's guard is telling you something real about that project's configuration; run orc doctor there.

The lane I picked is not the lane that ran. Look for a HANDOFF line in the trace. /orc-fast is knowledge-gated: with no wiki or no cached code-pattern it hands off to /orc-mini by design. orc-cli surfaces the handoff and ORC's own stated reason rather than hiding it.

Progress shows a sweeping bar instead of a percentage. There is no plan yet, so there is no denominator. The bar says no plan yet — nothing to count rather than inventing a number. Once the planner returns you get a real percentage.

A number shows as —. That value could not be computed. Unknown is not zero, and orc-cli will not print a figure it did not get from a real source.

The screen went blank when I resized the terminal. Fixed in 0.2.0. A resize erases the terminal and the painter's memory of what was on it; the old build stopped there, and since a resize is not a state change, nothing was redrawn until the next key press. Clearing and repainting now always happen together, coalesced on a short timer so dragging a VS Code panel does not fire a full repaint per pixel. Rows are also clipped to the terminal's real width, so a window narrower than the 52-column floor no longer wraps a row and desynchronises every row below it.

A question was cut off, or the options were missing. Fixed in 0.3.0, and it was four separate bugs. The option parser scanned backwards from the last line and stopped at the first non-list line — so any question ending with a recommendation, or with the RESUME.md pointer ORC's hard rule 16 makes mandatory at every question, parsed as having no options at all. The body was hard-truncated at 14 wrapped lines with no marker and no scroll. The option list was unbounded on top of that, so on a short terminal the options were what got cut. And an option longer than 120 characters was dropped rather than clipped. The question now scrolls, the options and the answer box are reserved before the prose gets a row, and clipping belongs to the renderer rather than the parser.

I pasted a multi-line request and only the first line was sent. Fixed in 0.3.0, and this was the worst one. Every was read as enter, and enter on the compose screen submits. orc-cli now asks the terminal for bracketed paste, so a pasted block arrives as one event, is inserted verbatim, and never submits. On a terminal without bracketed paste, a draft that already has a newline needs a blank last line to send.

PageUp typed [5~ into my prompt. Fixed in 0.3.0. The key reader recognised only the four arrow sequences and shredded everything else into its literal bytes, so PageUp fired esc (which is "back") and then typed the rest. A CSI sequence is now consumed whole even when it is unmapped: an unknown key does nothing, rather than something wrong.

/orc-explain sat on an empty question box forever. Fixed in 0.3.0. orc-cli's completion signal is ".current was seen, and now it is gone" — and ORC publishes trace_tier: null for orc-explain, orc-wait and orc-retro, which write no trace at all. That transition could never happen, so every turn end was read as a question. orc-cli now asks ORC which lanes trace, and treats the turn ending as the whole signal on the ones that do not.

The run went quiet for half an hour. Check for a WAIT line. ORC v1.1.0 can stop a run — nothing runs during a wait, by design, and a hand-back is written before it starts. orc-cli reports it and stops the clock, the same way it does for a question. Before 0.3.0 a wait was indistinguishable from a dead session.

The rail says inferred. ORC has not named a phase for this run yet, so the current phase was worked out from what the session is observably doing. This is much rarer since 0.3.0: ORC publishes, per phase, the trace verbs that phase emits, so a verb belonging to exactly one phase places the rail with no inference at all. /orc-quick used to be the worst case — it dispatches its recon ad-hoc on purpose, so a whole run emits one PHASE-EDGE near the end — and it now lights its rail from WIKI-CONSULT, FINISH and OUTCOME instead. See What you see while it runs.

The question box has no question in it. Fixed in 0.2.0 — it took only the last text block of the turn, which is empty when a lane ends its turn on a tool call. The whole turn is used now, and if the turn genuinely said nothing, the last thing that was said is shown instead.

The display looks scrambled in an old Windows console. Use --ascii, and consider Windows Terminal. Legacy conhost is detect and degrade, not a supported target.

Colours or animation are distracting. --no-motion, --no-color, --ascii. NO_COLOR=1 and CI=1 are honoured too. Motion is removed rather than slowed, because a capped animation freezes mid-cycle.


What it is, and what it is not

It is a lane runner.

It is not a Claude Code replacement. No plan mode, no IDE integration, no @-file completion, no image paste, no /compact, no MCP management, no general chat. If you want those, open Claude Code — that fence is what makes this finishable, and the surface re-implemented here is the surface owned forever.

The two hard boundaries

| Boundary | Rule | |---|---| | ORC | Zero modifications. No file under .claude/ is ever written — not config, not settings, not the manifest, not a run folder. A test asserts this by snapshotting the directory around a full command run. If a feature needs an ORC change, that is a separate ORC release. | | Claude Code | The documented headless interface only. No PTY-wrapping the interactive TUI, no scraping rendered output, no patching settings.json, no writing a credential. |

Deliberately not in v1

Written down so nobody re-proposes them as oversights: any lane beyond the four · writing anything into .claude/, including "just fixing" a doctor finding · any settings editor of its own · guarding you against choosing the "wrong" project · a switch that disables the transcript · multi-run or multi-project dashboards · a web UI (orc ui exists and is the right surface) · its own auth (claude login is the only path, forever).


Nine rules the code is held to

  1. A number the CLI did not compute is never rendered. Unknown is an em dash. --check fails the build if any screen renders the word undefined.
  2. A fact is never relayed through a model's memory. Every displayed fact comes from the event stream, from a file on disk, or from orc <cmd> --json. The orchestrator is never asked to report its own state for the UI's benefit.
  3. The animation is a view over the truth, never the only copy of it.
  4. A state the CLI cannot determine is shown as unknown, never guessed.
  5. A path ORC can compute is never hardcoded. log_dir and run_dir come from orc where --json; if it cannot answer, the documented defaults are used and the screen says so.
  6. Degradation is designed, not discovered. Narrow terminal, no colour, no TTY, no motion, missing orc, missing claude — each has a defined rendering and a defined next action.
  7. A fact ORC publishes is rendered, never re-derived. The lane list, each lane's rail, its trace tier, its stops and its agents are ORC's, read through orc lane. A second copy of someone else's table is a copy that drifts.
  8. A question is never silently truncated. It scrolls, and the options and the answer box are reserved before the prose gets a single row.
  9. Nothing is ever cut without a marker and a key that reveals it. One viewport, everywhere. "Widen the terminal" is not an answer.

For contributors

Layout

bin/orc-cli.js         entry, flags, TTY guard, exit trap, non-interactive commands
src/kernel/
  bus.js               normalise the three sources into ONE event vocabulary
  phases.js            per-lane phase model: the rail, the trace->lane word map,
                       the VERB->phase resolver, and the stream inference with
                       its two honesty rules
  question.js          reading a question out of prose: the option-block parser,
                       the markdown pass, and the turn split
  reducer.js           (state, event) -> state          PURE
  state.js             the shape, one place
src/sources/
  claude.js            THE ONLY FILE that knows how the session is driven
  trace.js             resolve log_dir, follow .current, incremental tail  (read-only)
  orccli.js            `orc <cmd> --json`, with a timeout and the exit code honoured
  lanes.js             ORC's own registries, READ not copied: `orc lane`
                       list/phases/calls, and the capability probe behind them
  model.js             the four model rows, the settings read, and the honest
                       report on ORC's session-model bridge
src/preflight/
  doctor.js            claude · orc · paths · drift, and the exit-code contract
  project.js           resolution, recents, the directory-name-only picker
src/ui/
  ansi.js screen.js theme.js ui.js layout.js openpanel.js
  input.js             full CSI parsing + bracketed paste, chunk-safe
  textinput.js         a text field with a CURSOR: {text,cursor} in, out
  viewport.js          THE one place a long thing is made short
  motifs.js            one animation per lane; exact-width, ascii + still twins
  screens/*.js         one per screen, PURE: (state, size) -> string[]
src/log/transcript.js  the always-on disk log
src/registry.js        lane metadata; discovery is from .claude/commands
src/app.js             the state machine — tick / onKey / render are exported
fixtures/              RECORDED real runs. Never hand-authored.
spike/                 M0-RESULT.md, plus the harnesses that produced it
test/                  node:test, zero-dep

The architecture in one paragraph

Three inbound sources — Claude Code's stream-json, ORC's trace file, and orc <cmd> --json — are normalised into one event vocabulary before anything else sees them. That normalisation is the most important decision in the codebase: the reducer, the renderer, the fixtures and the tests all speak our vocabulary, never Claude's and never ORC's. The reducer and every screen are pure functions, which is what lets the whole app be driven from a recorded fixture with no terminal and no model.

The stream is primary for agents and progress — it has real ids and real timing. The trace is supplementary: phase edges, decisions, and the deterministic run-finished signal. Its narration is written by a dispatched agent per phase packet and therefore lags by a whole phase, so it never drives a progress indicator.

Phase is the one thing both sources speak to, and they are not equals: a PHASE-EDGE is ORC's own word and wins outright, while an inference from the stream is labelled as one and may only ever move the rail forward. That split lives entirely in src/kernel/phases.js, so the two halves cannot drift.

Working on it

npm test        # the kernel, preflight, the state machine and the CLI contracts
npm run check   # 496 renders: 31 screens x 4 modes x 4 terminal sizes
npm run verify  # both. Runs automatically on prepack.

ORC_BIN=/path/to/orc points the orc <cmd> --json reads at a specific ORC — which is how the derived-registry path is exercised against a known version rather than whichever one happens to be global.

--check is not a smoke test. It asserts contracts: no row overflows its width, no screen renders undefined or NaN, every screen keeps a visible way out, and the finish screen's "what now" choice is never what gets cut as the terminal shrinks.

Fixtures are recorded, never written by hand

A hand-authored fixture tests our idea of the stream, which is exactly the failure mode being guarded against. Every fixture in fixtures/ is a real run.

This is not theoretical. The recorded run in fixtures/orc-fast-fallback-to-mini.* caught three bugs that all four of the plan's own descriptions had missed:

  • the dispatch tool is called Agent in Claude Code 2.1.246, not Task — so a run that plainly dispatched an executor was displaying zero agents;
  • PHASE-EDGE execution :: first=orc-executor-sonnet-5-high put the entire tail into the phase rail;
  • the real trace filename had no lane and no slug in it.

There are now regression tests for each, written against the recording.

Recording a new fixture

spike/e2e-drive.js runs the real app against a real lane with no terminal:

node spike/e2e-drive.js --dir <project> --lane orc-fast --answer "yes" "do the thing"

It prints each screen as plain text and dumps the final kernel state. The raw stream lands in your transcripts directory; copy it into fixtures/ to keep it.

Adding a lane to the registry

Add an entry to REGISTRY in src/registry.js (title, blurb, phases, whether it asks questions, its shape) and a row to LANE_TIER in src/sources/claude.js if it needs a model or effort other than the default. Discovery already finds the command file; the registry is only the metadata that makes the run view specific. An unregistered lane still runs — press a.

License

MIT.