Talk it through with a technical project manager, get a Linear project you approved, then let Forman turn it into tickets.
Red is a local, single-user tool that sits one level above Forman. Forman closes the loop between a ticket and a pull request; Red closes the one between an idea and the tickets. You describe what you want in plain language; it drafts a Linear project. You review that project in Linear and move it on. Later Red walks it a slice at a time and hands each slice to Forman to become tickets.
Red never answers on your behalf. When Forman's agent has a question, that question reaches your terminal exactly as it was asked. Every run ends at a human, twice over.
Push. red push talks it through with you first. It checks your description
against the six things a project needs before it can be sliced, asks only for the
gaps, then shows you the draft. Nothing is filed until you say so. The project
lands in Backlog.
Review. You read it in Linear and edit it there. The scope section is the part that matters: each slice becomes one handover to Forman. Move the project to Planned when you want Red to start on it.
Pull. red pull reads the project back out of Linear, so whatever you edited
wins. It walks the slices blockers-first and runs Forman's push on each one.
Every question and every set of drafts comes to you. At the end it comments on
the project saying what it filed, and stops.
Linear only ever sees projects and the issues Forman creates. Red never moves a project: Backlog to Planned is you saying you have read it, and anything after that is yours too.
you <-> red push -> Linear project (Backlog)
[you review and edit it in Linear, move it to Planned]
you <-> red pull -> forman push -> Linear issues, in that project
[then, unchanged: forman pull -> a pull request]
Red's job ends when the tickets exist: execution is Forman's, and you run
forman pull yourself, when you are ready.
Everything Red files carries Forman's provenance label, which is what forman pull requires before it will start a ticket on its own. That is the handoff
working as intended: a project you approved through Red is work Forman will
pick up, and twenty tickets someone else filed on you in the meantime are not.
Needs Python 3.11+, git, and the Claude Code CLI on your PATH. Forman is pulled in as a dependency; you do not need to install it separately.
git clone https://github.com/asinglesprinkle/Red && cd Red
python3 -m venv .venv && source .venv/bin/activate
pip install -e .Red has no configuration of its own, on purpose. It reads Forman's, so if
forman already works here, so does red. If it does not:
forman initThat asks for a personal API key, checks it against the API before saving
anything, and writes ~/.config/forman/.env with mode 0600. One key serves
both tools, because the key belongs to you and not to a tool.
Run it from inside the repo the project is about. Red reads that repo to
ground what it drafts, the same way Forman does, and never writes to it beyond
.red/.
red push # talk through a project, then file it
red push "add rate limiting" # or start from a line of prose
red push --dry-run "..." # print the draft, create nothing
red pull # the one project in Planned
red pull "Rate limiting" # or a specific one, by name or slug id
red pull --verbatim # no proposed answers, just the pipe
red status # what has been pulled in this repoAdd --linear stub to any of them to run the whole thing offline against
.red/linear-stub.json, with no account and no network.
red push is a short conversation, not a one-shot command:
$ red push
What do you want to achieve? (a paragraph is fine)
> the api falls over when one client hammers it
Two things I want to pin down:
- Should the limit be per API key, per IP, or both?
- Is the existing middleware in src/api the right place, or is this a
new layer in front of it?
> per key, and yes, extend the existing middleware
---
name: Rate limiting
summary: Stop one client from taking the API down for everyone.
---
## Outcome
...
## Scope
<!-- red:scope -->
### 1. Add a token bucket
...
[c]reate the project, [e]dit, [q]uit, or type feedback:
The questions come from what the next stage needs in order to run: the outcome, checkable success criteria, the independently shippable slices, what is out of scope, the ordering between slices, and the constraints. The agent reads your repo to answer what it can and asks only about the rest.
Each slice is handed to a fresh Forman session that has never seen this conversation, and the tickets it writes are executed by agents that have never seen it either. Nobody downstream can come back and ask. That is why this step is a conversation.
e opens the draft in $EDITOR; anything else you type is feedback and
redrafts.
During a pull, every question Forman raises is printed exactly as it was asked. Red proposes a reply drawn from the project brief, labelled as its own, and waits:
=== slice 1 of 3: Add a token bucket (1/3) ===
-- forman asks (Rate limiting, slice 1 of 3) --
Which endpoints should the limit apply to, and what is the limit?
red is drafting a reply from the project brief...
red > Everything under src/api, 100 requests a minute, per API key.
[enter] to send that, or type your own:
Press enter and Red's sentence goes to Forman. Type anything and yours does
instead. --verbatim removes the proposal entirely and leaves a byte-for-byte
pipe.
At the gate, Red proposes nothing:
-- forman drafted 2 issue(s) for Rate limiting --
[c]reate 2 issue(s), [e]dit, [s]kip this slice, or type feedback to redraft:
s skips the slice and files nothing for it. Red then asks whether to carry on
with the rest.
- Picks the project in Planned, or the one you named. More than one candidate and it lists them rather than guessing, because a project is a big thing to start by accident.
- Re-reads the project's content from Linear, so an edit you made there wins over anything Red remembers.
- Reconciles that against
.red/<slug>/state.json, matching slices on their title rather than their position. - Orders the slices blockers-first and hands each pending one to Forman's push, with the project brief in front of it so a fresh session can stand on its own.
- Records what was filed after every slice, so an interrupted run never loses one that really happened.
- Records cross-slice ordering as blocking relations in Linear.
- Comments on the project saying what it filed, and stops.
Run it again and it skips whatever already finished. Skipping a slice is a real answer and is remembered as one; re-filing it means editing the project in Linear or asking for it by hand.
None of its own. Red calls Forman's settings loader, so the same environment
variables, the same .env in the target repo, and the same
~/.config/forman/.env apply, with the same precedence. See
Forman's configuration table.
Red keeps its bookkeeping in .red/ inside the target repo and adds that to
.git/info/exclude, so it cannot be committed by accident. This is tooling's
business, not a change to your tracked files.
| what | where | why |
|---|---|---|
| the project | Linear, in its content field |
so you can edit it, and your edit wins |
| what has been filed | .red/<slug>/state.json |
so re-running never files a slice twice |
| a readable summary | .red/<slug>/manifest.md |
rendered on every write, never parsed back |
| your API key | ~/.config/forman/.env |
it is yours, and it is Forman's already |
- Red is a pipe, not a proxy. It could answer most of Forman's questions correctly most of the time. That is exactly the problem: you cannot correct a plausible answer you never saw. So every question surfaces, and the only thing Red adds is a first draft you can accept with one keystroke.
- Red drafts answers, never approvals. A tool that proposed "yes, create them" would be approving its own work, and the gate would be theatre.
- The project lives in Linear. Keeping it on disk would be faster and would
quietly diverge from the one you edited. Linear is the source of truth for what
the work is;
.red/is the source of truth only for what has already been done. - Slices reconcile on title, not position. Inserting a slice in Linear renumbers everything below it. Matching on position would file the wrong slice twice. A retitled slice therefore looks new, and Red says so rather than assuming.
- Issues are attached to their project by id. Forman attaches by name, taking the first case-insensitive match out of the first hundred projects. Red holds the real id, so it uses it.
- Nothing here can move a project's status. There is no mutation in the codebase that can, so there is no call to forget to leave out.
- The pull loop performs no I/O. Every side effect sits behind a port on a
Depsdataclass, which is what lets the whole thing be tested without a network, a repo, a terminal, or a model.
Alpha. The whole loop runs end to end offline; the parts that touch real Linear have been exercised against a real workspace, but it has not been through much mileage.
- Built for one person on one machine, one repository at a time. No locking, no concurrency story, by design.
- Considers at most 50 projects when selecting or searching by name. More than that needs real pagination.
- Two projects with the same name are refused rather than resolved. Pass the slug id, which is the last part of the project URL.
- Every proposed answer costs one extra agent call.
--verbatimis there for when you would rather just answer. - It does not drive
forman pull. Forman's execution phase has no human seam by design: blockers surface asynchronously instate.json,manifest.mdand Linear comments, andforman resumeis you saying you have dealt with one. - Forman is pinned to a commit in
pyproject.toml. It exports nothing from__init__and promises no API stability, so an unpinned dependency would be a dependency on whatever happened to be pushed. Bumping it is a deliberate act.
pip install -e ".[dev]"
pytestThe suite runs both halves against a stubbed Linear and a stubbed model,
asserting the round trip through a hand-edited project, the relay's promise that
your words are the ones sent, and that no run ever moves a project. No network,
no account, no model calls: an autouse fixture in tests/conftest.py makes any
attempt to start a real agent session fail loudly rather than hang. Fakes are
hand-written; there is no mocking library.
