Workflow state for stateless coding agents.
Quoin is a workflow-memory toolkit for coding agents. Its core value is the
artifact-centric workflow: plans, architecture notes, review artifacts, gates,
session handoff, lessons learned, and a cost ledger stored in predictable files
under .workflow_artifacts/.
Quoin now has two runtime paths:
- Claude Code: an installable global adapter that deploys skills, scripts,
hooks, memory files, and workflow rules to
~/.claude. - Codex: a repo-local scaffold that generates
AGENTS.mdand points Codex at portable workflow contracts. It uses native Codex planning, approvals, sandboxing, repo-scoped instructions, and model/reasoning controls.
Quoin does not install global Codex commands, plugins, hooks, command files, or runtime extensions.
- Persistent workflow state: sessions share structured artifacts instead of restarting from memory alone.
- Planning rigor: architecture, planning, review, and gate phases have explicit artifact contracts.
- Runtime portability: shared workflow semantics live in
quoin/core/, while Claude and Codex behavior is isolated in adapter directories. - Session continuity: both runtime paths preserve handoff state under
.workflow_artifacts/memory/sessions/. - Cost discipline: Claude has live cost tooling; Codex writes portable cost
rows with unavailable telemetry marked honestly as
not_available.
Install the package:
pip install quoinInstall or update the Claude Code adapter:
quoin install --runtime claude
quoin doctor --runtime claudeWhen --scope is not specified, the installer prompts interactively to ask
whether to install globally (~/.claude/) or project-level (./.claude/). In
non-interactive environments (CI, pipes), it defaults to global without prompting.
quoin install and bare quoin remain backward-compatible aliases for the
Claude install path.
bash quoin/install.sh needs a Python that meets requires-python in
pyproject.toml (3.10 or newer). It looks at QUOIN_PYTHON first, then every
directory on PATH in order (any python3.N), then the usual Homebrew,
python.org, pyenv, uv, asdf, mise, conda (root and named environments, including
/opt and ~/opt installs and the Homebrew cask), Linuxbrew and MacPorts locations, and finally
uv python find. bash quoin/install.sh --print-python prints the interpreter
it would use without installing; set QUOIN_PYTHON=/path/to/python3 to choose
one yourself. A .venv synced from another computer usually has a dangling
interpreter link: recreate it on this machine, or point QUOIN_PYTHON at a
local interpreter. The affected-area test runner reports such a venv by name.
quoin install records the interpreter and source tree it ran from
(quoin-runtime.json, next to the installed .claude/ tree), so an
autonomous run's detached hand-off can relaunch the exact CLI you meant to
run instead of guessing via PATH. Run quoin install with whichever
interpreter or tool you actually use day to day — for a uv tool install
or pipx install setup, that means running that tool's own quoin install, not a system Python's. Re-run quoin install after every
upgrade, recreated virtualenv, or Python version change; a stale record
makes hand-offs refuse rather than run the wrong code. quoin doctor
checks the record and names the exact fix when it's missing or stale.
The Claude adapter can be installed at two scopes:
User scope (global) — installs to ~/.claude/. Skills, hooks, and workflow
rules are available in every Claude Code session on the machine.
quoin install --runtime claude --scope user # explicit global, no promptProject scope — installs to <project>/.claude/. Skills and hooks activate
only when Claude Code is opened in that directory. Hooks register in
<project>/.claude/settings.json instead of your personal settings file.
quoin install --runtime claude --scope project # explicit project, no prompt
quoin install --runtime claude --scope project:/path # explicit project rootPass --scope explicitly to skip the interactive prompt (useful in scripts and CI).
Note: Claude Code resolves personal scope (
~/.claude/skills/) before project scope. If you have a user-scope install, it will shadow a project-scope install for skills. Runquoin doctor --scope projectto detect conflicts.
Generate the Codex repo-local scaffold:
quoin install --runtime codex --project-root .
quoin install --runtime codex --project-root . --check
quoin doctor --runtime codex --project-root . --smokeThe Codex install path writes AGENTS.md in the selected project root. The
--check form verifies that AGENTS.md is up to date without writing files.
The doctor command verifies repo-local readiness; --smoke also runs the
deterministic Codex workflow smoke test.
Equivalent Codex setup command:
quoin codex init --project-root .
quoin codex init --project-root . --checkLegacy source install for Claude remains supported (prompts for scope interactively):
git clone https://github.com/FourthWiz/quoin
cd quoin
bash quoin/install.sh # prompts: global or project?
bash quoin/install.sh --scope user # global, no prompt
bash quoin/install.sh --scope project # project, no promptGitHub redirects the old FourthWiz/claude_dev_workflow URL to this repository.
| Runtime | Setup command | Scope | Implemented behavior |
|---|---|---|---|
| Claude Code | quoin install --runtime claude |
Global adapter under ~/.claude |
Skills, scripts, hooks, memory files, short CLAUDE.md core rules (full rules in memory/workflow-catalog.md), slash-command workflow |
| Codex | quoin install --runtime codex --project-root . |
Repo-local AGENTS.md |
Portable artifact workflow, generated root instructions, readiness/smoke checks, handoff validation, cost event writer |
The portable core is shared. Runtime-specific mechanics are adapter-owned:
- Claude adapter files live under
quoin/adapters/claude/and install to~/.claude. - Codex adapter files live under
quoin/adapters/codex/and are used from the repository. They are documentation, generated instructions, and validation scripts, not Codex command packages.
In the project where you want Codex to use Quoin:
pip install quoin
quoin install --runtime codex --project-root .
quoin doctor --runtime codex --project-root . --smokeThen ask Codex for Quoin phases in natural language:
- "Use Quoin to discover this repository and write the discovery artifacts."
- "Use Quoin to create an architecture artifact for this task."
- "Use Quoin to write a current plan under
.workflow_artifacts/." - "Use Quoin to implement the current plan."
- "Use Quoin to review this implementation against the current plan."
- "Use Quoin to run a gate before the next phase."
- "Update Quoin session handoff and lessons learned."
- "Record a Codex cost event for this task."
The practical Codex workflow loop is documented as:
discover -> plan -> implement -> review -> gate
Codex procedure docs live in quoin/adapters/codex/procedures/ and link back
to portable contracts in quoin/core/skills/.
Codex readiness and smoke:
quoin doctor --runtime codex --project-root .
quoin doctor --runtime codex --project-root . --smokeHandoff validation:
python3 quoin/adapters/codex/validate_codex_handoff.py --self-test
python3 quoin/adapters/codex/validate_codex_handoff.py --project-root . --file .workflow_artifacts/memory/sessions/<date>-<task>-codex.mdCost event writing and validation:
python3 quoin/adapters/codex/cost_event.py --self-test
python3 quoin/adapters/codex/cost_event.py write --project-root . --task <task> --phase <phase> --effort <low|medium|high|max|unknown>
python3 quoin/adapters/codex/cost_event.py validate --project-root . --task <task> --expect-codexCodex cost rows use the portable cost-ledger shape. They record known local
values such as runtime, task, phase, timestamp, session id when supplied, and
effort. Token counts, dollar cost, and telemetry source are recorded as
not_available because this repository has no verified Codex local telemetry
interface.
After installing the Claude adapter:
/init_workflow one-time project bootstrap
/architect design the solution
/thorough_plan converge on a plan with critic review
/implement write code from the plan
/review verify implementation against the plan
/gate stop for an explicit quality checkpoint
/end_of_task finalize, push, and capture lessons
Claude-specific command behavior, model assignments, hook behavior, and
cost-capture plumbing are documented in quoin/CLAUDE.md; the installed ~/.claude/CLAUDE.md carries only the short core rules, with every section kept in ~/.claude/memory/workflow-catalog.md.
These slash commands are Claude adapter commands. Codex uses the same portable workflow intent through natural-language phase requests and repo-local docs.
| Command | Model | What it does |
|---|---|---|
/architect |
Opus | Deep architectural analysis; internal critic loop |
/plan |
Opus | Detailed implementation plan |
/thorough_plan |
Opus | Triages task size; runs plan -> critic -> revise convergence |
/critic |
Opus | Reviews a plan for gaps, risks, and integration issues |
/revise |
Opus | Revises plan from critic feedback in strict/large mode |
/revise-fast |
Sonnet | Revises plan from critic feedback in medium mode |
| Command | Model | What it does |
|---|---|---|
/implement |
Sonnet | Writes code from the plan |
/review |
Opus | Verifies implementation against the plan |
/gate |
Sonnet | Automated quality checkpoint between phases; requires approval |
/rollback |
Sonnet | Safely undoes an implementation phase or specific tasks |
/pr |
Sonnet | Full pull-request lifecycle: optional version bump, push, create PR, wait for merge, switch branch |
| Command | Model | What it does |
|---|---|---|
/init_workflow |
Opus | One-time project bootstrap; creates .workflow_artifacts/ and runs discovery |
/discover |
Opus | Scans repos; maps architecture, dependencies, and git history |
/start_of_day |
Haiku | Morning briefing from daily/session memory |
/end_of_day |
Sonnet | Saves session state, dedupes and promotes insights; auto-invokes /sleep |
/end_of_task |
Sonnet | Pushes branch, captures lessons, and finalizes task state |
/continue_work |
Sonnet | Resume context from a prior session using the recent-sessions index |
/sleep |
Sonnet | Memory consolidation: promotes insights to lessons-learned, archives stale entries |
| Command | Model | What it does |
|---|---|---|
/run |
Opus | End-to-end pipeline orchestrator; pauses at gates |
/cost_snapshot |
Haiku | Live Claude cost: today, lifetime, and per-task breakdown |
/triage |
Haiku | Routes the request to the right workflow phase |
/weekly_review |
Haiku | Aggregates weekly progress into a structured review |
/capture_insight |
Haiku | Logs a pattern or discovery to daily insights |
/expand <path> |
Sonnet | Re-renders a terse workflow artifact in readable English |
/status |
Haiku | Renders the workflow pipeline graph with the active phase marked (read-only) |
/cleanup |
Sonnet | Trash-moves stale sentinels and old checkpoints into recoverable archive; standalone runs also offer a confirmed task-folder bookkeeping pass |
/next-steps |
Haiku | Append-only queue for future work items (add / list / done N) |
Quoin can route Claude Code through claude-code-router
to use open models from OpenRouter (DeepSeek V4.1, GLM-5.3, and others) when your
Anthropic quota is low. This is fully opt-in — quoin install is unchanged.
export OPENROUTER_API_KEY=sk-or-... # your OpenRouter key
quoin router setup # install CCR + scaffold configThis installs claude-code-router globally (requires Node 22 or newer) and scaffolds
~/.claude-code-router/config.json with an OpenRouter provider and a tier routing
map. Any existing config is backed up with a timestamp before changes are applied.
Check the result:
quoin router status # installed? config present? proxy running? key set?
quoin doctor # includes a one-line CCR probe| Mode | Command | When to use |
|---|---|---|
| Open models via CCR (v2) | ccr code |
Quota exhausted, cost-sensitive work |
| Open models via CCR (v3) | ccr default-claude-code |
Same, on CCR v3 — profile exists but routes nothing until configured in ccr ui |
| Native Anthropic | claude |
Normal use, full quoin skill fidelity |
On CCR v2, ccr code auto-starts the local proxy and launches the real claude
binary in your terminal — quoin's slash commands and skills work normally. CCR v3
has no code subcommand; ccr default-claude-code selects its profile, but that
profile routes nothing until you configure its models in ccr ui.
Sanity-check: inside an open-model session, type /help. The quoin skill list
should resolve. If it does not, run quoin doctor and file an issue.
Note: the model shown in the Claude Code header will still say "Sonnet 4.6" (or whatever your default Claude model is). This is expected — CCR routes requests transparently at the HTTP layer, below what the Claude Code UI can see. The actual model invoked is the one CCR maps to (e.g. DeepSeek V4 for default requests).
Run claude directly (not ccr code / ccr default-claude-code) to use native
Anthropic models. No config changes needed — the CCR config is left intact so you
can flip back by running ccr code (v2) or ccr default-claude-code (v3) again.
The tier → model mapping is seeded to ~/.config/quoin/models.json on first setup.
Edit it any time, or use the quoin models command (see below). Re-running
quoin router setup re-applies the current models.json to the CCR Router
config — it does not reset it.
View and manage the tier → open-model mapping:
quoin models # show current mapping and active launch mode
quoin models set <tier> <slug> # update one tier's slug
quoin models preset open # restore all three defaults at once
quoin models reset # document native-launch; back up CCR config (non-destructive)
quoin models reset --native # identical to reset (explicit-intent spelling)Tiers: haiku, sonnet, opus
Friendly aliases (expand to the corresponding default slug):
| Alias | Slug |
|---|---|
flash |
z-ai/glm-5.3-flash |
pro |
deepseek/deepseek-v4.1-flash |
glm |
z-ai/glm-5.3 |
Examples:
quoin models set opus glm # set opus → z-ai/glm-5.3 (alias)
quoin models set sonnet pro # set sonnet → deepseek/deepseek-v4.1-flash (alias)
quoin models set haiku anthropic/claude-3-haiku # any OpenRouter slugSlug validation is advisory: known slugs are accepted silently; unknown-but-plausible
slugs (containing a /) are accepted with a warning; only structurally malformed input
is rejected. You can always edit ~/.config/quoin/models.json directly.
Secret rule: quoin models never reads or writes OPENROUTER_API_KEY. The key lives
only in the CCR config authored by quoin router setup. set and preset update the
provider's models list in-place, leaving api_key byte-unchanged.
reset is non-destructive: quoin models reset backs up the CCR config and prints
native-launch instructions, but leaves the Router keys, provider block, and models.json
intact. Run ccr code (v2) or ccr default-claude-code (v3) again to switch back
to open models with no re-setup required.
Agentdesk is a Zellij terminal layout launcher bundled with quoin. It opens a named Zellij session with panes pre-configured for agent-assisted development — Claude Code, Codex, and a plain shell — in a single command.
Agentdesk is deployed automatically when you install the Claude adapter at user scope:
quoin install --runtime claude # or: quoin install --runtime claude --scope userThis copies agentdesk.zsh and setup-agentdesk.sh to ~/.config/agentdesk/.
Running the setup (Zellij, lazygit and fzf via Homebrew, plus a line in your
.zshrc) changes your system, so it only happens with your consent:
- In a terminal, the installer asks "Run the agentdesk setup now?" (default
No). The question lists what the script does: it installs Homebrew if
missing (and may ask for your password), installs zellij, lazygit, fzf and
the Ghostty app, writes the agentdesk helper and the zellij layout and
config (backups kept), and adds lines to
~/.zshrc(backup kept). It is not asked when~/.zshrcalready sources the helper. - For a non-interactive install, pass the flag instead:
quoin install --setup-agentdeskorbash quoin/install.sh --setup-agentdesk.
The setup runs as the last install step. If it fails, you get a warning and
quoin stays installed; the deployed script is idempotent, so re-run the install
with --setup-agentdesk to retry. After that, agentdesk is available in
every new shell session.
Agentdesk is user-scope only — it is not deployed in project-scope installs.
agentdesk # interactive picker (when run in a TTY)
agentdesk <session-name> # named session with fixed layout
agentdesk --mode solo|duo|trio # preset layout
agentdesk --mode trio --name my-sess # named preset-layout session
agentdesk claude [codex] [shell] [...] # custom layout from window-type tokens
agentdesk --name deck --panes codex,codex,shell --no-prompt # scripted launch, no questions
Preset modes:
| Mode | Panes |
|---|---|
solo |
One Claude Code pane, full-width |
duo |
Claude Code on top, a Shell strip below |
trio |
Claude Code and Codex side by side on top, a Shell strip below |
Layout: agents (Claude, Codex, CCR) sit on top and shells and status share a
strip below; repeated agents are named Claude · A, Claude · B and so on.
Token Spend is a floating pane that starts hidden (unless spend is the only
token, when it opens visible). Swap between arrangements
(even, focus-left, stacked) with Alt [ and Alt ]. --compact-bar or
AGENTDESK_COMPACT_BAR=1 replaces the tab bar and status bar with a single
one-line bar (per launch, not remembered; it applies to generated layouts and to
the generated Standard layout, while a customised agent-desk.kdl keeps its own
bars).
Standard layout: with no tokens, agentdesk opens three tabs. main has
Claude · A and Claude · B on top and a Shell strip below, with Token Spend
as a hidden floating pane. git runs lazygit when it is installed and the
project root is a repo (or holds exactly one repo); otherwise it shows a
refreshing git status, and any key drops to a shell. status runs the workflow
status graph full width, or prints an install hint when it is missing. To
customise it, edit ~/.config/zellij/layouts/agent-desk.kdl: agentdesk then
keeps your file as it is, and setup-agentdesk.sh writes the new Standard
layout beside it as agent-desk.kdl.new instead of replacing yours.
Picker choices: 1 Standard, 2-5 agent presets (claude + shell, two Claude, Claude + Codex, Claude + CCR, each with Spend), 6 claude + shell + status + spend, 7 custom comma-separated tokens. Typing a token list directly also works.
Window-type tokens (positional, mutually exclusive with --mode):
| Token | Pane contents |
|---|---|
claude |
Claude Code |
codex |
Codex |
shell |
Plain zsh shell |
Examples:
agentdesk # picker — choose a preset or enter custom tokens
agentdesk pricing # named session, fixed layout
agentdesk --mode trio # Claude + Codex + Shell
agentdesk claude codex # custom two-pane layout
agentdesk claude claude # two Claude panes side-by-sideIf Zellij is not running, agentdesk starts a new session. If a session with
the chosen name already exists, agentdesk in a terminal asks whether to attach
or start a new one; non-interactive runs start a fresh session with a numeric
suffix (foo_1, foo_2, ...). --if-exists overrides this. To resume an existing
session, use agentdesk-attach SESSION-NAME.
Find, attach to and clean up sessions from any shell (no need to have sourced the launcher):
quoin sessions # Agent Desk sessions for the current folder
quoin sessions --all # every Agent Desk session in the socket directory
quoin sessions --all --json # same, as JSON (name, age, state, live, root, match)
quoin attach [NAME] # attach; without a name, pick the one for this folder
quoin sessions kill NAME # delete an EXITED session (--force stops a running one)
quoin sessions prune [--dry-run] # delete EXITED sessions that have a launch record, plus stale records
quoin sessions prune --include-unrecorded # also delete EXITED Agent Desk sessions without a recordA session belongs to a folder when its launch record (written by the launcher
to ~/.config/agentdesk/sessions/NAME) names that folder or a parent of the
current directory. Sessions started before the launcher wrote records match by
name instead (<folder>-agents, <folder>-agents_2, ...) until they are
restarted. Live sessions show as running; the session you are inside shows
as current. prune deletes EXITED sessions only when they have a launch
record. An EXITED session without one (started before records existed) is
listed as skipped, because Zellij shares its resurrection cache across socket
directories and the name may belong to plain Zellij; delete it with
quoin sessions kill NAME, or pass --include-unrecorded to delete them all.
EXITED sessions that are not Agent Desk's are never touched and can still force
a _N suffix on a new session; remove one with zellij delete-session NAME in
a shell that has not sourced the launcher.
Inside a session, detach with Alt s, then d, and quit with Alt s, then
q (both need Ghostty macos-option-as-alt); Ctrl+Q is intentionally unbound.
The launcher prints these keys when it starts or attaches to a session, but
Zellij switches to the alternate screen right after, so the lines scroll out of
view while the session runs and show again after you detach or quit.
Keyboard seems stuck. From another terminal run quoin sessions and
quoin attach NAME to get a second client in the same session, then close the
stuck terminal tab or window; the session keeps running and the new client stays
attached. Do not detach with Alt s, then d in the new client, because that
detaches the working one; use it in the stuck tab only if the Alt keys still
work there. The full list of checks (mode
name in the status bar, a floating pane in front, Option not sent as Alt, and
reset for a garbled terminal) is the "If the keyboard seems stuck" section of
quoin/tools/agentdesk/KEYBIND-VERIFY.md.
Scripted launch:
--panes LISTstarts the session with an explicit pane set (for examplecodex,codex,shell, joined by,or+). Token Spend is still available as a hidden floating pane (toggle: Alt f, or pane mode then w), and the list is never saved to the layout memory file.--no-promptasks nothing: no dashboard question, no picker, no attach-or-new question.--if-exists attach|new|faildecides what happens when the session name is already taken. With--no-promptthe default isattach.attachneeds a terminal (Ghostty and other terminal launchers are fine).AGENTDESK_API_VERSIONinagentdesk.zshmarks the version of this interface; tools can detect it with a plain grep without sourcing the file.
An optional 15-key Stream Deck panel shows your running Claude and Codex agents (orange when one needs you, blue
while it works, green when it is done) and focuses their Zellij panes in Ghostty. It is macOS only and never
installed by pip install quoin or quoin install. You manage it yourself:
quoin streamdeck install # check prerequisites, then install the plugin, hooks and profile
quoin streamdeck upgrade # bring the installed component up to date
quoin streamdeck status # read-only health report
quoin streamdeck uninstall # remove it; --purge also removes config and statepip install --upgrade quoin updates only the package; run quoin streamdeck upgrade afterwards to update the
installed component. The guide, quoin/docs/streamdeck/README.md, covers
prerequisites, hooks and Codex trust, moving over from the prototype, and recovery. What is verified by tests and
what still needs hardware is in quoin/docs/streamdeck/VALIDATION.md.
Quoin separates portable workflow contracts from runtime adapters:
quoin/core/workflow/defines shared rules, task layout, session state, cost ledger shape, and skill metadata.quoin/core/skills/defines runtime-neutral skill intent.quoin/core/scripts/contains portable helper implementations.quoin/adapters/claude/contains Claude-specific skill bodies and runtime assumptions.quoin/adapters/codex/contains Codex-facing repo-local instructions, procedures, readiness checks, handoff validation, and cost-event tooling.
The project root containing AGENTS.md is the Quoin project root for Codex.
Codex writes .workflow_artifacts/ there even when the code being edited lives
in a nested subdirectory.
Claude and Codex expose different runtime mechanics, so Quoin keeps cost and effort handling honest:
- Claude skills declare Haiku/Sonnet/Opus model tiers and can self-dispatch through Claude's Agent/Skill behavior.
- Claude cost tooling can read Claude session logs and
ccusageoutput. - Codex adapter docs use runtime-neutral effort labels:
low,medium,high,max, andunknown. - Codex cost rows do not infer token counts or dollars from another runtime.
Unavailable telemetry is recorded as
not_available.
For runtime-neutral effort vocabulary, see
quoin/docs/effort-levels.md.
quoin/QUICKSTART.md- Claude command reference tablequoin/CLAUDE.md- Claude workflow rules, model assignments, artifacts, and cost conventionsquoin/adapters/codex/setup.md- Codex repo-local setup and readinessquoin/adapters/codex/workflow.md- Codex workflow execution guidequoin/adapters/codex/handoff.md- Codex session handoff contractquoin/adapters/codex/cost.md- Codex cost event behaviorquoin/docs/runtime-portability.md- portable core and runtime adapter boundaryquoin/docs/runtime-portability-status.md- current migration status by runtimequoin/docs/runtime-parity-matrix.md- evidence-based runtime parity matrixquoin/docs/effort-levels.md- runtime-neutral effort vocabularyquoin/docs/streamdeck/README.md- the optional Stream Deck component: install, hooks, recoveryquoin/docs/streamdeck/VALIDATION.md- what is tested, what was checked by hand, and what is not yet verified
Quoin intentionally does not:
- install global Codex commands or plugins
- guess Codex local runtime paths
- duplicate Codex approvals or sandboxing
- claim live Codex hooks
- translate Claude slash commands into Codex command files
- infer Codex token or dollar telemetry from Claude tooling
Future runtime integrations should add explicit adapter contracts instead of copying Claude-specific behavior into the portable core.
Bug reports and PRs welcome. Quoin development uses Quoin itself: keep changes artifact-aware, runtime boundaries explicit, and documentation honest about what is implemented versus planned.
PolyForm Noncommercial 1.0.0 — source-available, noncommercial use only.

