Tickets in. Reviewed pull requests out. Nobody watching.
A Claude Code skill that runs your Linear board overnight and hands you a morning of pull requests to read.
There is an experimental second generation, linear-orchestrator2, which moves the whole conversation — body, screenshots, review, steering — into the Linear ticket. This repo is the maintained generation: the conversation lives on the pull request, and you steer by commenting there.
The whole interface is two surfaces. A ticket in Linear, and a comment on a pull request. There is no chat window to steer, no label to remember, no plan to approve inside the tracker. To change the direction of the work you comment on the PR, and the loop reads it, answers it by name, and acts on it. That works on a merged PR too, which is where it earns its keep: merge, note the follow-up work the PR spotted but left out of scope, and the loop turns your note into the next ticket by itself. Teammates can steer it the same way, on purpose.
Draft means something. A PR leaves draft only on evidence — a review, a fix pass over that review, a screenshot when a user can see the change, no merge conflict, and CI green on the head commit. It never leaves draft on an agent's say-so. So what is not a draft in the morning is what is actually ready, and the drafts are the pile you can ignore until you have time.
Idle costs nothing. Nothing runs until there is work. The gate probes capacity, your open PRs and the Linear ready column every 60 seconds in pure shell — no model call, no turn started. A night with an empty board costs nothing at all, not "not much".
It runs on your machine, on the Claude you already pay for. No hosted sandbox,
no per-session credit, no repo access granted to a third party. That also means it
uses whatever the machine has: it measures free memory every probe and runs as many
agents as will really fit — one per LO_RAM_PER_AGENT gigabytes — so parallelism
is bounded by your laptop rather than by a plan.
It never merges. Author and reviewer are the same model, so "no blockers left" is evidence about care, not proof of correctness. The loop stops at a reviewed, non-draft PR and leaves the merge to a person. That is a choice, not a missing feature.
- The gate blocks until there is work — capacity, your PRs, the Linear ready column.
- An agent takes the ticket, moves it to In Progress, branches, opens the draft PR before it writes any code, writes the code, tests it in a real browser, and stops.
- A second agent reviews that PR in a fresh context — it has the diff, the ticket and your repo's agent guide, and none of the reasoning the author talked itself into. It posts the review as one comment, makes exactly one fix pass over what it found, and corrects the PR body where the diff moved past it.
- The PR leaves draft on the five gates, or stays a draft.
- Your comment re-opens any of it. An unanswered comment on one of its PRs outranks every other kind of work on the next tick.
Every agent gets its own repo clone, Terminal window and real Chrome, which is why front-end tickets work here: the screenshots on the PR come from driving the actual screen, not from a headless guess.
The mechanics — the shell gate, and how the loop tells your comments from its own —
are in HOW-IT-WORKS.md.
People who already run coding agents locally and would rather spend a machine than a per-seat credit. The install is honestly longer than "connect GitHub", and the constraints are real:
- macOS only, and Claude only. It drives Claude (Opus) agents through
multiclaudeand reads Linear throughagent-codemode, which reads its OAuth from the macOS Keychain. - One machine. Parallelism caps at what one laptop's memory holds.
--dangerously-skip-permissionsis required. The agents run unattended with permission prompts off. That is the point, and it is also the risk — give the loop a repo where the worst an agent can do is open a bad PR.- Your repo needs its own agent guide (
CLAUDE.md). The prompt handed to each agent is short because the guide supplies branching, local checks, the PR template, testing and security.
You also need gh (authenticated), jq, and the
Linear MCP server connected to the session that runs
the loop.
git clone https://github.com/janwilmake/linear-orchestrator.git \
~/.claude/skills/linear-orchestrator
cd ~/.claude/skills/linear-orchestrator
cp .env.example .env && $EDITOR .envThen start it from your repo:
cd /path/to/your/repo
claude --dangerously-skip-permissions/linear-orchestrator
status reports what is running; stop ends the loop and lists the PRs it
produced.
All the values that change per user live in .env beside SKILL.md. It is
gitignored, so your paths and names never reach the repo — which is what lets the
skill be a symlink to a checkout:
LO_REPO="/path/to/your/repo" # local checkout the agents clone from
LO_BASE="dev" # base branch
LO_TEAM="Your Team" # Linear team name
LO_PREFIX="ENG" # ticket id prefix
LO_TIER1="Preferred Assignee" # display name. ONLY these assignees are taken
LO_TIER2="Fallback Assignee" # display name
LO_ALLOW_UNASSIGNED="0" # 1 = also take unassigned tickets (default off)
LO_ALLOW_BACKLOG="0" # 1 = fall to Backlog when Todo is empty (default off)
LO_OWNER="" # this instance's tag; required if two share a repo
LO_RAM_PER_AGENT="2.5" # GB per agent (claude + vite + chrome)
LO_MAX_AGENTS="0" # ceiling on concurrent agents; 0 = unlimited
LO_FEEDBACK_SINCE="2026-08-18T17:00:00Z" # the day you installed thisLO_RAM_PER_AGENT is the one people get wrong: it is a divisor, not a threshold.
Every tick spawns free memory / LO_RAM_PER_AGENT agents — all of them, not one,
so the loop reaches full capacity on its first tick with work. LO_MAX_AGENTS
defaults to 0, meaning memory is the only ceiling; set a positive number to
hold part of the machine back. The default divisor assumes one Claude session,
one dev server and one Chrome — a stack with Docker or a local database costs far
more, and the probe cannot detect that.
What it takes is narrow on purpose. Only tickets in Todo assigned to
LO_TIER1 or LO_TIER2. Unassigned tickets and the Backlog column are each
one switch away (LO_ALLOW_UNASSIGNED, LO_ALLOW_BACKLOG) and both default to
off — a name on a ticket in a ready column is somebody deciding it is ready, and
that decision is the signal the loop runs on.
Running two orchestrators against one repo — say one per laptop — needs
LO_OWNER set to a different tag in each. Splitting by assignee separates them
on the tracker, but not on GitHub: every instance writes the same 🌙 marker,
and that marker is what decides which PRs a loop reviews, promotes and answers
comments on. With LO_OWNER the marker becomes 🌙 lo:<owner> and the two stop
overlapping. Both instances must run this version for the split to hold — an
older one matches the bare marker and still adopts everything.
Both gate.sh and the tick read these, so there is nothing to edit inside
SKILL.md.
