diff --git a/site/app/blog/posts.generated.ts b/site/app/blog/posts.generated.ts
index ccc3919..f55630d 100644
--- a/site/app/blog/posts.generated.ts
+++ b/site/app/blog/posts.generated.ts
@@ -1,8 +1,8 @@
// Generated from content/blog/*.md by scripts/sync-blog.ts. Do not edit.
-export const blogStatusLabel = "Latest release: v0.10.4";
+export const blogStatusLabel = "Latest release: v0.10.5";
export const blogBodies: Readonly Excalibur, or xcb, routes coding tasks across the Claude, Codex, and Devin subscriptions you already pay for. You can type tasks into its terminal or have another agent hand them over as JSON, and either way xcb runs each task in the provider's own coding tool, under your own sign-in. Paying for more than one coding agent means more than one of everything around it: a terminal for each provider, a sign-in and usage limits for each account, and sessions scattered across windows. Before each task you check which account is free, and afterward you look through those windows for the session that was fixing a flaky test. xcb reaches each model through the provider's own tool: Claude Code, Codex, or the Devin CLI. You sign in through that tool's own flow, and xcb keeps the credentials in a private profile outside your projects. It runs a private copy of the tool you installed, and only builds that pass xcb's checks. For each task, xcb picks an account that is signed in, idle, and not at a known usage limit, on a model from that provider's current model list. Known limits come from Claude's five-hour and seven-day usage windows; xcb does not infer Codex or Devin limits. While the task runs, xcb holds that account so no other task can start on it, and it releases the account only after the provider process has exited. The provider runs in an operating-system sandbox (Seatbelt on macOS, bwrap on Linux) with a private home directory and a cleared environment, and it changes your files through xcb. When the run ends, xcb records how it ended; if it cannot confirm that, it keeps the account held and does not retry. If a task from your thread stops on a usage limit that the provider reports, xcb can move it to another account that is free, along with its original instructions. xcb has no model access of its own and does not raise any account's usage limits. Each provider's terms and limits apply to every task xcb runs. Plain Each prompt becomes a task that keeps running after you close the terminal, because a background supervisor runs it. If you use one subscription, or you rely on a provider's plugins, MCP servers, skills, subagents, or web search, the provider's own tool serves you better. Those features are not available in the runs xcb starts, where the provider works on your files through xcb's own tools. Another program, usually a coding agent, can give xcb one task with xcb picks the account and model, runs exactly one provider turn, and writes one JSON result to stdout once the provider process has exited: The request can also pin a provider, account, or model, set a deadline, or ask for a dry run that shows the chosen route without reserving an account. It never accepts tools, hooks, system prompts, credentials, or provider flags. Returned text is capped at 256 KiB. A failure exits with a nonzero code and names a reason such as Applications can embed the TypeScript SDK instead. There the application names the account and model for each task and supplies the provider adapters, and Herdr keeps your coding agents running, each in its own terminal, and marks which one is waiting on you; xcb decides which of your accounts runs each task, on which model, and holds that account until the run ends. Like Pi, a minimal coding agent you adapt with extensions, xcb can be reshaped: panes change what its terminal shows, hooks run your own programs when sessions and turns start or end, and reflexes learn from your replies and can be rolled back. None of them can give a provider run more access, and because hooks run with your own permissions, each one stays off until you turn it on. The comparison page covers other tools. With the tested accounts, Claude and Devin have completed coding tasks through xcb on macOS on Apple silicon. Claude ran a failing test, fixed the code, passed the test, and checked Git status; Devin, on Devin CLI 3000.11.3, fixed a broken function and reported the change. On Linux only Claude runs, in a bwrap sandbox once xcb's checks pass, and a coding session there has not been confirmed. Codex and Devin need macOS, and Codex runs only on specific builds that xcb has checked; its last signed-in coding run used the previous supported build. The providers page lists each supported build and its status. Tests and builds run only in an offline Linux VM that you set up on macOS on Apple silicon; without it, agents can read and change files but cannot run commands. Git in that VM is read-only, so agents working through xcb do not commit or push, and native macOS or Xcode builds cannot run there. The managed harness that runs the tasks in your thread is experimental. It is being rebuilt so that it can propose routing rules, test them on labeled examples, and keep a rule only when it scores better than the current one. That work is in development, and the current build does not run self-modifying routing policies. Latest release: v0.10.4. Release binaries are built for macOS on Apple silicon and Linux x86_64, and one line installs xcb: With Claude Code installed, To build from source, or to connect Codex or Devin, follow the getting started guide. Excalibur, or xcb, routes coding tasks across the Claude, Codex, and Devin subscriptions you already pay for. You can type tasks into its terminal or have another agent hand them over as JSON, and either way xcb runs each task in the provider's own coding tool, under your own sign-in. Paying for more than one coding agent means more than one of everything around it: a terminal for each provider, a sign-in and usage limits for each account, and sessions scattered across windows. Before each task you check which account is free, and afterward you look through those windows for the session that was fixing a flaky test. xcb reaches each model through the provider's own tool: Claude Code, Codex, or the Devin CLI. You sign in through that tool's own flow, and xcb keeps the credentials in a private profile outside your projects. It runs a private copy of the tool you installed, and only builds that pass xcb's checks. For each task, xcb picks an account that is signed in, idle, and not at a known usage limit, on a model from that provider's current model list. Known limits come from Claude's five-hour and seven-day usage windows; xcb does not infer Codex or Devin limits. While the task runs, xcb holds that account so no other task can start on it, and it releases the account only after the provider process has exited. The provider runs in an operating-system sandbox (Seatbelt on macOS, bwrap on Linux) with a private home directory and a cleared environment, and it changes your files through xcb. When the run ends, xcb records how it ended; if it cannot confirm that, it keeps the account held and does not retry. If a task from your thread stops on a usage limit that the provider reports, xcb can move it to another account that is free, along with its original instructions. xcb has no model access of its own and does not raise any account's usage limits. Each provider's terms and limits apply to every task xcb runs. Plain Each prompt becomes a task that keeps running after you close the terminal, because a background supervisor runs it. If you use one subscription, or you rely on a provider's plugins, MCP servers, skills, subagents, or web search, the provider's own tool serves you better. Those features are not available in the runs xcb starts, where the provider works on your files through xcb's own tools. Another program, usually a coding agent, can give xcb one task with xcb picks the account and model, runs exactly one provider turn, and writes one JSON result to stdout once the provider process has exited: The request can also pin a provider, account, or model, set a deadline, or ask for a dry run that shows the chosen route without reserving an account. It never accepts tools, hooks, system prompts, credentials, or provider flags. Returned text is capped at 256 KiB. A failure exits with a nonzero code and names a reason such as Applications can embed the TypeScript SDK instead. There the application names the account and model for each task and supplies the provider adapters, and Herdr keeps your coding agents running, each in its own terminal, and marks which one is waiting on you; xcb decides which of your accounts runs each task, on which model, and holds that account until the run ends. Like Pi, a minimal coding agent you adapt with extensions, xcb can be reshaped: panes change what its terminal shows, hooks run your own programs when sessions and turns start or end, and reflexes learn from your replies and can be rolled back. None of them can give a provider run more access, and because hooks run with your own permissions, each one stays off until you turn it on. The comparison page covers other tools. With the tested accounts, Claude and Devin have completed coding tasks through xcb on macOS on Apple silicon. Claude ran a failing test, fixed the code, passed the test, and checked Git status; Devin, on Devin CLI 3000.11.3, fixed a broken function and reported the change. On Linux only Claude runs, in a bwrap sandbox once xcb's checks pass, and a coding session there has not been confirmed. Codex and Devin need macOS, and Codex runs only on specific builds that xcb has checked; its last signed-in coding run used the previous supported build. The providers page lists each supported build and its status. Tests and builds run only in an offline Linux VM that you set up on macOS on Apple silicon; without it, agents can read and change files but cannot run commands. Git in that VM is read-only, so agents working through xcb do not commit or push, and native macOS or Xcode builds cannot run there. The managed harness that runs the tasks in your thread is experimental. It is being rebuilt so that it can propose routing rules, test them on labeled examples, and keep a rule only when it scores better than the current one. That work is in development, and the current build does not run self-modifying routing policies. Latest release: v0.10.5. Release binaries are built for macOS on Apple silicon and Linux x86_64, and one line installs xcb: With Claude Code installed, To build from source, or to connect Codex or Devin, follow the getting started guide.How xcb runs each task
\nWork from one thread across your projects
\nxcb opens your thread, one conversation per machine that spans your projects. Type a task and xcb picks the project directory it runs in and says why, for example “Started Fix the parser in app · named app · /workspace to move”. When it cannot tell which project you mean, it asks and saves nothing until you pick./tasks lists the work, /attention collects the questions and approvals your agents are waiting on, and /steer <task-id> <guidance> queues guidance for a task's next turn. Tasks in different projects can run at the same time on different accounts; tasks in the same project run one at a time.Hand tasks to xcb from another program
\nxcb --json route. It writes one JSON request to stdin:
\n{ "version": 1, "workspace": "/absolute/path/to/project", "task": "Fix the failing parser test and show the diff" }\n
\n{\n "version": 1,\n "status": "completed",\n "requestId": "route_…",\n "session": "s_…",\n "route": { "provider": "claude", "account": "a_…", "model": "claude/sonnet/low", "label": "Sonnet · low", "reason": "…" },\n "state": "idle",\n "outcome": { "terminal": "completed", "joined": true, "effects": "settled", "pending_attention": false, "failure": null },\n "text": "…"\n}\nbusy or needs_input; custody_unproven means xcb could not confirm how the run ended, so the account stays held and the caller should not retry blindly.createSubscriptionRouter holds that account while the task runs. The route documentation covers both.How xcb compares
\nLimits
\nInstall xcb and connect Claude
\n
\ncurl -fsSL https://xcb.sh/install.sh | sh\nxcb setup claude adds a Claude account or reuses yours, checks your Claude Code build, opens the browser sign-in, and loads the account's models. Then plain xcb opens your thread:
\nxcb setup claude\nxcb\nHow xcb runs each task
\nWork from one thread across your projects
\nxcb opens your thread, one conversation per machine that spans your projects. Type a task and xcb picks the project directory it runs in and says why, for example “Started Fix the parser in app · named app · /workspace to move”. When it cannot tell which project you mean, it asks and saves nothing until you pick./tasks lists the work, /attention collects the questions and approvals your agents are waiting on, and /steer <task-id> <guidance> queues guidance for a task's next turn. Tasks in different projects can run at the same time on different accounts; tasks in the same project run one at a time.Hand tasks to xcb from another program
\nxcb --json route. It writes one JSON request to stdin:
\n{ "version": 1, "workspace": "/absolute/path/to/project", "task": "Fix the failing parser test and show the diff" }\n
\n{\n "version": 1,\n "status": "completed",\n "requestId": "route_…",\n "session": "s_…",\n "route": { "provider": "claude", "account": "a_…", "model": "claude/sonnet/low", "label": "Sonnet · low", "reason": "…" },\n "state": "idle",\n "outcome": { "terminal": "completed", "joined": true, "effects": "settled", "pending_attention": false, "failure": null },\n "text": "…"\n}\nbusy or needs_input; custody_unproven means xcb could not confirm how the run ended, so the account stays held and the caller should not retry blindly.createSubscriptionRouter holds that account while the task runs. The route documentation covers both.How xcb compares
\nLimits
\nInstall xcb and connect Claude
\n
\ncurl -fsSL https://xcb.sh/install.sh | sh\nxcb setup claude adds a Claude account or reuses yours, checks your Claude Code build, opens the browser sign-in, and loads the account's models. Then plain xcb opens your thread:
\nxcb setup claude\nxcb\n
xcb handles this for Claude Code and Codex sessions with Gobstopper. When a session grows past a threshold, xcb applies Gobstopper's elision policy to the prompt it is about to send: old tool results become a one-line marker, the recent work stays word for word, and the original output stays in xcb's local history. It is on by default. Latest release: v0.10.4.
\nA coding agent works by reading. It opens files, runs searches, lists directories and runs tests, and each result lands in the conversation. The model usually needs the conclusion from that output, which it already wrote down in its own reply, and rarely needs the raw text again.
\nThat raw text still counts against the context window. As a session grows, something has to be cut or summarized, and the parts worth keeping are what you asked for, what was decided, and the last few results.
\nGobstopper is a tool for inspecting Claude Code, Codex and Devin sessions and preparing smaller copies of them. Its simplest rule is called elide: replace old tool outputs with a short stub, oldest first, and leave the newest ones alone. It needs no model to decide what to cut, so the same session and the same settings always give the same plan.
\nUsed on its own, Gobstopper also keeps a local archive of each transcript before it prepares a compacted copy, so you can search old sessions and read back a specific archived record when a summary leaves it out.
\nxcb uses a smaller piece: Gobstopper's core library, pinned to one commit in xcb's build, and its elide strategy. On this path xcb neither runs the Gobstopper program nor uses its archive; it keeps its own history.
\nxcb stores every message of a session locally and builds each turn's prompt from that history. Gobstopper runs during that build, in these steps:
\nIn outline, the rule looks like this:
\nif estimated_size < trigger:\n send the history as it is\nelse:\n candidates = tool results, oldest first, except the newest 8\n stub candidates until estimated_size <= floor\n use the plan only if it saves at least min_savings\n never touch the last 8 messages, or any user or assistant text\n\nIn place of each old result, the model sees this marker:
\n[output elided by gobstopper: <bytes> bytes; original retained in local history]\n\nWhen xcb elides anything, it tells you in the session: "Gobstopper elided N stale tool outputs in the prompt; history is retained."
\nSome old results still matter, such as an exact error message or a file the agent is about to edit again. If you have turned on xcb's optional judge (off by default), xcb asks it one yes-or-no question per candidate: does the next turn still need this output word for word, where running the tool again would not do? The judge is an external judgment service, which is why it is opt-in. It sees the tool's name, the output's size, your current task and a limited excerpt of the recent conversation. It does not see the tool output itself. An output it wants to keep, with a probability of 0.5 or more, stays in the prompt.
\nThe judge can only keep things. It never adds a candidate Gobstopper did not choose. xcb asks about at most 64 candidates per turn, and any beyond that stay in full. If the judge is unavailable, fails or leaves an answer out, xcb says so in the session and falls back to Gobstopper's plan on its own. After the judge answers, xcb checks the minimum saving again and trims nothing if what remains falls below it.
\nThe settings sit under extensions.gobstopper in xcb's config.json, and xcb config shows the values in effect. The defaults are:
{\n "extensions": {\n "gobstopper": {\n "enabled": true,\n "trigger_tokens": 250000,\n "floor_tokens": 40000,\n "min_interval_ms": 300000,\n "min_savings_tokens": 4096\n }\n }\n}\n\nxcb rejects out-of-range values: for example, a floor below 1,024 or not below the trigger, or a trigger above 1,000,000. To switch the feature off, or back on:
\nxcb plugins disable gobstopper\nxcb plugins enable gobstopper\n\nThis covers Claude Code and Codex sessions that xcb runs. Devin sessions are sent without elision. The sizes are estimates from byte counts, not the provider's token counts, and xcb reports no measured savings or effect on your bill. A smaller prompt is also not proof that the next turn goes better; Gobstopper's own documentation makes the same point about its compaction. Once an output is elided, the model sees only the marker, so if it needs that text again it has to run the tool again. You can still read the original in xcb's history.
\nThe rest of xcb is covered in Introducing Excalibur, and Gobstopper as a standalone tool at gobstopper.sh.
\n", + "html": "Two hours into a refactor, much of what your coding agent receives each turn can be old tool output: the files it read at the start, a search it has already acted on, the log from a test run it fixed an hour ago. The instructions you gave and the decisions it made are still in there, but they share the space with pages of text nobody needs again.
\nxcb handles this for Claude Code and Codex sessions with Gobstopper. When a session grows past a threshold, xcb applies Gobstopper's elision policy to the prompt it is about to send: old tool results become a one-line marker, the recent work stays word for word, and the original output stays in xcb's local history. It is on by default. Latest release: v0.10.5.
\nA coding agent works by reading. It opens files, runs searches, lists directories and runs tests, and each result lands in the conversation. The model usually needs the conclusion from that output, which it already wrote down in its own reply, and rarely needs the raw text again.
\nThat raw text still counts against the context window. As a session grows, something has to be cut or summarized, and the parts worth keeping are what you asked for, what was decided, and the last few results.
\nGobstopper is a tool for inspecting Claude Code, Codex and Devin sessions and preparing smaller copies of them. Its simplest rule is called elide: replace old tool outputs with a short stub, oldest first, and leave the newest ones alone. It needs no model to decide what to cut, so the same session and the same settings always give the same plan.
\nUsed on its own, Gobstopper also keeps a local archive of each transcript before it prepares a compacted copy, so you can search old sessions and read back a specific archived record when a summary leaves it out.
\nxcb uses a smaller piece: Gobstopper's core library, pinned to one commit in xcb's build, and its elide strategy. On this path xcb neither runs the Gobstopper program nor uses its archive; it keeps its own history.
\nxcb stores every message of a session locally and builds each turn's prompt from that history. Gobstopper runs during that build, in these steps:
\nIn outline, the rule looks like this:
\nif estimated_size < trigger:\n send the history as it is\nelse:\n candidates = tool results, oldest first, except the newest 8\n stub candidates until estimated_size <= floor\n use the plan only if it saves at least min_savings\n never touch the last 8 messages, or any user or assistant text\n\nIn place of each old result, the model sees this marker:
\n[output elided by gobstopper: <bytes> bytes; original retained in local history]\n\nWhen xcb elides anything, it tells you in the session: "Gobstopper elided N stale tool outputs in the prompt; history is retained."
\nSome old results still matter, such as an exact error message or a file the agent is about to edit again. If you have turned on xcb's optional judge (off by default), xcb asks it one yes-or-no question per candidate: does the next turn still need this output word for word, where running the tool again would not do? The judge is an external judgment service, which is why it is opt-in. It sees the tool's name, the output's size, your current task and a limited excerpt of the recent conversation. It does not see the tool output itself. An output it wants to keep, with a probability of 0.5 or more, stays in the prompt.
\nThe judge can only keep things. It never adds a candidate Gobstopper did not choose. xcb asks about at most 64 candidates per turn, and any beyond that stay in full. If the judge is unavailable, fails or leaves an answer out, xcb says so in the session and falls back to Gobstopper's plan on its own. After the judge answers, xcb checks the minimum saving again and trims nothing if what remains falls below it.
\nThe settings sit under extensions.gobstopper in xcb's config.json, and xcb config shows the values in effect. The defaults are:
{\n "extensions": {\n "gobstopper": {\n "enabled": true,\n "trigger_tokens": 250000,\n "floor_tokens": 40000,\n "min_interval_ms": 300000,\n "min_savings_tokens": 4096\n }\n }\n}\n\nxcb rejects out-of-range values: for example, a floor below 1,024 or not below the trigger, or a trigger above 1,000,000. To switch the feature off, or back on:
\nxcb plugins disable gobstopper\nxcb plugins enable gobstopper\n\nThis covers Claude Code and Codex sessions that xcb runs. Devin sessions are sent without elision. The sizes are estimates from byte counts, not the provider's token counts, and xcb reports no measured savings or effect on your bill. A smaller prompt is also not proof that the next turn goes better; Gobstopper's own documentation makes the same point about its compaction. Once an output is elided, the model sees only the marker, so if it needs that text again it has to run the tool again. You can still read the original in xcb's history.
\nThe rest of xcb is covered in Introducing Excalibur, and Gobstopper as a standalone tool at gobstopper.sh.
\n", "toc": [ { "href": "#most-of-a-long-prompt-is-old-tool-output", @@ -114,7 +114,7 @@ export const blogBodies: ReadonlyA coding agent starts every task knowing the code and nothing else. The reasons behind the code live in a notes folder, an Obsidian vault or a plans directory, so the agent either rediscovers a decision the hard way or undoes it. Pasting notes into each prompt works until the pasted copy goes stale, and then nobody can tell which note a claim came from.
\nIf you run your agents through xcb, you can point a project at that folder once. Its agents can then search your notes while they work, and every result names the note it came from, so you and the agent can open the file and check.
\nWordcell is a command-line tool for a knowledge base kept as ordinary Markdown files. Your files stay the record. Wordcell adds search and a link graph on top of them, and recent versions can search an existing folder or Obsidian vault without converting it. Its exact search mode reads the current Markdown, with no model download and no network request, and each hit comes back with the path of the note that matched.
\nxcb never finds or connects a vault on its own. You bind one vault and one installed copy of Wordcell to a project directory, check the binding, and from then on every worker task in that directory can search it:
\nxcb memory configure <dir> --vault /absolute/project/vault --wordcell /absolute/bin/wordcell\nxcb memory status <dir>\nxcb memory search <dir> "parser decision"\nxcb memory promote <task-id> --body-file decision.md\n\nThe last line saves a note back, covered below. The TUI offers the same search as /memory search <query>.
When you bind, xcb records a SHA-256 fingerprint of the Wordcell program you named. If that program is a script, xcb fingerprints its interpreter too. For Wordcell's standard launcher, which runs through Bun, xcb looks Bun up on your PATH once, at binding time, and from then on starts that same Bun directly, so a later change to your PATH cannot swap it. The vault has to be a directory you own that neither your group nor other users can write to, and xcb remembers which directory it is on disk, not only its path.
Before every search or save, xcb checks all of that again. If the Wordcell program changed, or the folder at that path is now a different folder, the call fails until you bind again. Replacing a binding requires the current binding's revision number, so two edits cannot overwrite each other unnoticed. The fingerprint covers the Wordcell launcher and its interpreter, not every file in the installed Wordcell package, so you are trusting the copy of Wordcell you installed.
\nInside a managed project, a worker gets one tool for your notes, xcb_memory_search. It takes a query and an optional result count, and nothing else:
{ "query": "parser retries", "limit": 8 }\n\nThe query can be up to 1,024 bytes, and the count runs from 1 to 16, with 8 as the default. There is no argument for choosing a vault or writing a note. The tool's own description tells the agent that retrieved text is historical, untrusted context and that facts which change over time need checking again.
\nBehind the tool, xcb runs Wordcell in exact mode with Git history and graph expansion turned off, asks for JSON, and starts the process with a cleared environment that holds only a minimal system PATH and a few fixed settings, with the vault as its working directory. A query that starts with a dash is refused, so a search can never be read as a command-line option. The search has 15 seconds and 64 KiB of output; if it runs over either, xcb stops the process and everything it started, and returns an error instead of a partial answer.
The worker gets Wordcell's result as JSON, including the note path for each hit. Nothing from the vault is added to a worker's prompt unless the worker asks. Fresh workers do receive a short list of recent task summaries from the same project, and xcb labels that list as its own working memory, separate from your Wordcell notes.
\nxcb gives workers no tool for writing to the vault; its memory tools only read. (A worker's ordinary file tools work inside the project's workspace, so keep the vault outside that workspace if you want the separation to hold.) To keep something a task learned, you write a short note yourself and save it against that task:
\nxcb memory promote <task-id> --body-file decision.md\n\nThe note can be up to 8 KiB of UTF-8. xcb never exports a conversation or copies a task summary on its own. It appends a short source block naming the xcb conversation, the task, and an ID for this save request, then asks Wordcell to create the note, tagged xcb, at the top of the vault, with a name derived from a hash of the note, its source task and conversation, and the binding. The saved file ends like this:
Parser retries stop after three attempts.\n\n---\nSource: xcb conversation `<conversation-id>`, task `<task-id>`.\nPromotion request: `<request-id>`.\n\nBecause the name comes from that hash, saving the same note for the same task twice leaves one note, and Wordcell refuses to overwrite a file that already exists. xcb writes down what it is about to do before it starts Wordcell, and it reports success only when Wordcell answers with the expected note path and a SHA-256 revision. A timeout, a crash, or an unclear answer is reported as uncertain, and any result short of a confirmed save makes the command exit with a nonzero code, so a script cannot mistake it for a save.
\nYour agents can find the decision you already made, in your words, and show you where it lives. You can open that note, correct it, or delete it, and the next search sees the change, because Wordcell reads the live files. Your notes stay in your own folder, in Markdown, under whatever version control you already use. A note is added only when you save one, and it names the task that produced it.
\nSearch through xcb is exact matching only: words, phrases, titles, tags, and paths that actually appear in your notes. Wordcell's semantic and hybrid (meaning-based), keyword-index, graph-expansion, Git-history, and hosted reranking options are not available through xcb. Each project binds one vault, and the search tool belongs to managed project workers; direct sessions do not get it. The fingerprint covers the Wordcell launcher and its interpreter, not every file in the installed Wordcell package, so you are trusting the copy of Wordcell you installed. Latest release: v0.10.4.
\nTo go further, read Introducing Excalibur for the router itself, Introducing Wordcell for the knowledge base, or how to check an xcb task history offline for the task records a saved note points back to.
\n", + "html": "Last month you wrote down why the parser gives up after three retries. The note sits in a Markdown folder, next to the reasons you chose one test runner over another and the list of commands that must never run on the release branch. Today an agent is about to change the parser, and it has never seen any of it.
\nA coding agent starts every task knowing the code and nothing else. The reasons behind the code live in a notes folder, an Obsidian vault or a plans directory, so the agent either rediscovers a decision the hard way or undoes it. Pasting notes into each prompt works until the pasted copy goes stale, and then nobody can tell which note a claim came from.
\nIf you run your agents through xcb, you can point a project at that folder once. Its agents can then search your notes while they work, and every result names the note it came from, so you and the agent can open the file and check.
\nWordcell is a command-line tool for a knowledge base kept as ordinary Markdown files. Your files stay the record. Wordcell adds search and a link graph on top of them, and recent versions can search an existing folder or Obsidian vault without converting it. Its exact search mode reads the current Markdown, with no model download and no network request, and each hit comes back with the path of the note that matched.
\nxcb never finds or connects a vault on its own. You bind one vault and one installed copy of Wordcell to a project directory, check the binding, and from then on every worker task in that directory can search it:
\nxcb memory configure <dir> --vault /absolute/project/vault --wordcell /absolute/bin/wordcell\nxcb memory status <dir>\nxcb memory search <dir> "parser decision"\nxcb memory promote <task-id> --body-file decision.md\n\nThe last line saves a note back, covered below. The TUI offers the same search as /memory search <query>.
When you bind, xcb records a SHA-256 fingerprint of the Wordcell program you named. If that program is a script, xcb fingerprints its interpreter too. For Wordcell's standard launcher, which runs through Bun, xcb looks Bun up on your PATH once, at binding time, and from then on starts that same Bun directly, so a later change to your PATH cannot swap it. The vault has to be a directory you own that neither your group nor other users can write to, and xcb remembers which directory it is on disk, not only its path.
Before every search or save, xcb checks all of that again. If the Wordcell program changed, or the folder at that path is now a different folder, the call fails until you bind again. Replacing a binding requires the current binding's revision number, so two edits cannot overwrite each other unnoticed. The fingerprint covers the Wordcell launcher and its interpreter, not every file in the installed Wordcell package, so you are trusting the copy of Wordcell you installed.
\nInside a managed project, a worker gets one tool for your notes, xcb_memory_search. It takes a query and an optional result count, and nothing else:
{ "query": "parser retries", "limit": 8 }\n\nThe query can be up to 1,024 bytes, and the count runs from 1 to 16, with 8 as the default. There is no argument for choosing a vault or writing a note. The tool's own description tells the agent that retrieved text is historical, untrusted context and that facts which change over time need checking again.
\nBehind the tool, xcb runs Wordcell in exact mode with Git history and graph expansion turned off, asks for JSON, and starts the process with a cleared environment that holds only a minimal system PATH and a few fixed settings, with the vault as its working directory. A query that starts with a dash is refused, so a search can never be read as a command-line option. The search has 15 seconds and 64 KiB of output; if it runs over either, xcb stops the process and everything it started, and returns an error instead of a partial answer.
The worker gets Wordcell's result as JSON, including the note path for each hit. Nothing from the vault is added to a worker's prompt unless the worker asks. Fresh workers do receive a short list of recent task summaries from the same project, and xcb labels that list as its own working memory, separate from your Wordcell notes.
\nxcb gives workers no tool for writing to the vault; its memory tools only read. (A worker's ordinary file tools work inside the project's workspace, so keep the vault outside that workspace if you want the separation to hold.) To keep something a task learned, you write a short note yourself and save it against that task:
\nxcb memory promote <task-id> --body-file decision.md\n\nThe note can be up to 8 KiB of UTF-8. xcb never exports a conversation or copies a task summary on its own. It appends a short source block naming the xcb conversation, the task, and an ID for this save request, then asks Wordcell to create the note, tagged xcb, at the top of the vault, with a name derived from a hash of the note, its source task and conversation, and the binding. The saved file ends like this:
Parser retries stop after three attempts.\n\n---\nSource: xcb conversation `<conversation-id>`, task `<task-id>`.\nPromotion request: `<request-id>`.\n\nBecause the name comes from that hash, saving the same note for the same task twice leaves one note, and Wordcell refuses to overwrite a file that already exists. xcb writes down what it is about to do before it starts Wordcell, and it reports success only when Wordcell answers with the expected note path and a SHA-256 revision. A timeout, a crash, or an unclear answer is reported as uncertain, and any result short of a confirmed save makes the command exit with a nonzero code, so a script cannot mistake it for a save.
\nYour agents can find the decision you already made, in your words, and show you where it lives. You can open that note, correct it, or delete it, and the next search sees the change, because Wordcell reads the live files. Your notes stay in your own folder, in Markdown, under whatever version control you already use. A note is added only when you save one, and it names the task that produced it.
\nSearch through xcb is exact matching only: words, phrases, titles, tags, and paths that actually appear in your notes. Wordcell's semantic and hybrid (meaning-based), keyword-index, graph-expansion, Git-history, and hosted reranking options are not available through xcb. Each project binds one vault, and the search tool belongs to managed project workers; direct sessions do not get it. The fingerprint covers the Wordcell launcher and its interpreter, not every file in the installed Wordcell package, so you are trusting the copy of Wordcell you installed. Latest release: v0.10.5.
\nTo go further, read Introducing Excalibur for the router itself, Introducing Wordcell for the knowledge base, or how to check an xcb task history offline for the task records a saved note points back to.
\n", "toc": [ { "href": "#your-notes-are-where-the-decisions-live", diff --git a/site/app/readme.generated.ts b/site/app/readme.generated.ts index d92aa49..c955785 100644 --- a/site/app/readme.generated.ts +++ b/site/app/readme.generated.ts @@ -1,5 +1,5 @@ // Generated from ../README.md by scripts/sync-readme.ts. Do not edit. export const readmeTitle = "xcb"; export const readmeLead = "xcb routes coding tasks across the Claude, Codex, and Devin subscriptions you already pay for. Each task runs on an account that is signed in, idle, and not at a known usage limit, on a model that fits the work. Type work into xcb's terminal thread, where tasks keep running after you close the terminal, or hand it one task at a time from another agent or your own code."; -export const readmeHtml = "xcb routes coding tasks across the Claude, Codex, and Devin subscriptions you\nalready pay for. Each task runs on an account that is signed in, idle, and not\nat a known usage limit, on a model that fits the work. Type work into xcb's\nterminal thread, where tasks keep running after you close the terminal, or\nhand it one task at a time from another agent or your own code.
\nStatus: Latest release\nfor macOS ARM64 and Linux x86_64; other hosts build from source. MIT licensed.
\nSite · Docs ·\nGetting started ·\nRoute contract · TypeScript SDK ·\nCompare · Changelog
\nLatest verified release: v0.10.4 · public verification run · release notes.
\nOn macOS with Apple silicon or Linux x86_64 (glibc 2.34 or newer), one command\ndownloads the latest release for your platform, checks its SHA-256 checksum,\nand installs ~/.local/bin/xcb:
curl -fsSL https://xcb.sh/install.sh | sh\n\nxcb upgrade installs later releases the same way. XCB_VERSION installs one\nexact version, XCB_INSTALL_PREFIX replaces ~/.local, and XCB_ADD_PATH=yes\nadds the bin folder to your shell profile.
On other hosts, build with Git, Rust 1.97.1, and the platform's build tools:
\ngit clone https://github.com/hraness/xcb.git && cd xcb\nrustup toolchain install 1.97.1 --profile minimal\n./scripts/install-native.sh\n\nUpgrade and uninstall covers\nupdates and removal.
\nInstall Claude Code 2.1.268 or later, then connect an account and open your\nthread:
\nxcb setup claude\nxcb\n\nxcb setup adds an account, checks the Claude Code build, opens the browser\nsign-in, and loads the account's models. xcb keeps that sign-in in its own\nstate folder, apart from your usual Claude Code login. xcb setup codex\nworks the same way; Devin connects by importing the Devin CLI's sign-in\n(accounts and models).
Plain xcb opens your thread, one conversation for all your projects. Type a\ntask such as “fix the failing test in ~/src/app”. xcb picks the project folder\nand says why (“Started Fix the failing test in app · named app ·\n/workspace to move”), picks an account and model, and runs the task there. If\na turn stops at a usage limit, xcb continues the task on another account or\nmodel that can take it. Closing the terminal detaches without cancelling\nanything; the next xcb shows the results.
/tasks lists running and finished work; /cancel <task-id> stops a task./steer <task-id> <guidance> adds guidance for a task's next turn.Use Claude, Use Codex, or Use Devin to choose the\nprovider. /help lists every command, and the\nterminal guide covers keys and search.From an agent or script, xcb --json route reads one JSON task on stdin,\npicks an account and model that can take it, runs one turn, and prints one\nJSON result:
echo '{"version":1,"workspace":"/absolute/path/to/project","task":"Fix the failing parser test"}' \\\n | xcb --json route\n\n{"version":1,"status":"completed","requestId":"route_…","session":"s_…",\n "route":{"provider":"claude","account":"a_…","model":"claude/sonnet/low","label":"Sonnet · low","reason":"…"},\n "state":"idle","outcome":{"terminal":"completed","joined":true,"effects":"settled","pending_attention":false,"failure":null},\n "text":"…"}\n\nAdd "dryRun": true to see the chosen route without running anything, or pin\nprovider, account, or model. A failure exits 1 with a code such as\nunavailable, busy, or needs_input. The route contract\nlists every field.
From your own app, the TypeScript SDK's createSubscriptionRouter runs a\ntask on the account and model your app names, and holds that account until\nthe provider process exits; it does not choose them for you. Install it with\nnpm install @hraness/xcb; the SDK quickstart has a complete\nexample.
| Provider | Supported builds | Status |
|---|---|---|
| Claude | Claude Code 2.1.268 or later within version 2 | Coding workflow passed on macOS ARM64 with the tested account. On Linux, Claude runs after you run xcb's sandbox checks on that machine. |
| Codex | Codex CLI 0.157.1 or 0.156.1 on macOS ARM64 | Passes xcb's sandbox and tool checks. The recorded signed-in coding run used the previous supported build. |
| Devin | Devin CLI 3000.11.3, 3000.11.1, or 3000.10.31 on macOS ARM64 | Coding workflow passed on macOS ARM64 with the tested account and Devin CLI 3000.11.3. |
xcb checks each provider executable's version, and for Codex and Devin its\nexact SHA-256, before it runs anything. xcb doctor shows what it found.
How routing works covers each step.
\nxcb # your thread, from any directory\nxcb chat --new # a project view for this directory\nxcb run -p "Explain this repository" # one task here; prints the answer\nxcb tasks # managed tasks across projects\nxcb attention # questions and approvals waiting on you\nxcb accounts # accounts, usage, and which need you\nxcb doctor # provider builds and unfinished runs\nxcb upgrade # install the latest verified release\nxcb help advanced # remote devices, project agents, extensions\n\nAccounts, credentials, and task history live in ~/.local/share/xcb, outside\nyour projects (--state or XCB_STATE moves it). The\nCLI and configuration reference lists every\ncommand, setting, and exit code.
xcb link needs a relay deployed from this repository's convex/ folder (remote operations).xcb was formerly AgentMixer: xcb accounts import-agentmixer --source <path>\ncopies one Claude credential (migrating).\nThe compatibility reference covers the TypeScript\npackage and its xcb-compat CLI. Contributing ·\nSecurity · MIT license
xcb routes coding tasks across the Claude, Codex, and Devin subscriptions you\nalready pay for. Each task runs on an account that is signed in, idle, and not\nat a known usage limit, on a model that fits the work. Type work into xcb's\nterminal thread, where tasks keep running after you close the terminal, or\nhand it one task at a time from another agent or your own code.
\nStatus: Latest release\nfor macOS ARM64 and Linux x86_64; other hosts build from source. MIT licensed.
\nSite · Docs ·\nGetting started ·\nRoute contract · TypeScript SDK ·\nCompare · Changelog
\nLatest verified release: v0.10.5 · public verification run · release notes.
\nOn macOS with Apple silicon or Linux x86_64 (glibc 2.34 or newer), one command\ndownloads the latest release for your platform, checks its SHA-256 checksum,\nand installs ~/.local/bin/xcb:
curl -fsSL https://xcb.sh/install.sh | sh\n\nxcb upgrade installs later releases the same way. XCB_VERSION installs one\nexact version, XCB_INSTALL_PREFIX replaces ~/.local, and XCB_ADD_PATH=yes\nadds the bin folder to your shell profile.
On other hosts, build with Git, Rust 1.97.1, and the platform's build tools:
\ngit clone https://github.com/hraness/xcb.git && cd xcb\nrustup toolchain install 1.97.1 --profile minimal\n./scripts/install-native.sh\n\nUpgrade and uninstall covers\nupdates and removal.
\nInstall Claude Code 2.1.268 or later, then connect an account and open your\nthread:
\nxcb setup claude\nxcb\n\nxcb setup adds an account, checks the Claude Code build, opens the browser\nsign-in, and loads the account's models. xcb keeps that sign-in in its own\nstate folder, apart from your usual Claude Code login. xcb setup codex\nworks the same way; Devin connects by importing the Devin CLI's sign-in\n(accounts and models).
Plain xcb opens your thread, one conversation for all your projects. Type a\ntask such as “fix the failing test in ~/src/app”. xcb picks the project folder\nand says why (“Started Fix the failing test in app · named app ·\n/workspace to move”), picks an account and model, and runs the task there. If\na turn stops at a usage limit, xcb continues the task on another account or\nmodel that can take it. Closing the terminal detaches without cancelling\nanything; the next xcb shows the results.
/tasks lists running and finished work; /cancel <task-id> stops a task./steer <task-id> <guidance> adds guidance for a task's next turn.Use Claude, Use Codex, or Use Devin to choose the\nprovider. /help lists every command, and the\nterminal guide covers keys and search.From an agent or script, xcb --json route reads one JSON task on stdin,\npicks an account and model that can take it, runs one turn, and prints one\nJSON result:
echo '{"version":1,"workspace":"/absolute/path/to/project","task":"Fix the failing parser test"}' \\\n | xcb --json route\n\n{"version":1,"status":"completed","requestId":"route_…","session":"s_…",\n "route":{"provider":"claude","account":"a_…","model":"claude/sonnet/low","label":"Sonnet · low","reason":"…"},\n "state":"idle","outcome":{"terminal":"completed","joined":true,"effects":"settled","pending_attention":false,"failure":null},\n "text":"…"}\n\nAdd "dryRun": true to see the chosen route without running anything, or pin\nprovider, account, or model. A failure exits 1 with a code such as\nunavailable, busy, or needs_input. The route contract\nlists every field.
From your own app, the TypeScript SDK's createSubscriptionRouter runs a\ntask on the account and model your app names, and holds that account until\nthe provider process exits; it does not choose them for you. Install it with\nnpm install @hraness/xcb; the SDK quickstart has a complete\nexample.
| Provider | Supported builds | Status |
|---|---|---|
| Claude | Claude Code 2.1.268 or later within version 2 | Coding workflow passed on macOS ARM64 with the tested account. On Linux, Claude runs after you run xcb's sandbox checks on that machine. |
| Codex | Codex CLI 0.157.1 or 0.156.1 on macOS ARM64 | Passes xcb's sandbox and tool checks. The recorded signed-in coding run used the previous supported build. |
| Devin | Devin CLI 3000.11.3, 3000.11.1, or 3000.10.31 on macOS ARM64 | Coding workflow passed on macOS ARM64 with the tested account and Devin CLI 3000.11.3. |
xcb checks each provider executable's version, and for Codex and Devin its\nexact SHA-256, before it runs anything. xcb doctor shows what it found.
How routing works covers each step.
\nxcb # your thread, from any directory\nxcb chat --new # a project view for this directory\nxcb run -p "Explain this repository" # one task here; prints the answer\nxcb tasks # managed tasks across projects\nxcb attention # questions and approvals waiting on you\nxcb accounts # accounts, usage, and which need you\nxcb doctor # provider builds and unfinished runs\nxcb upgrade # install the latest verified release\nxcb help advanced # remote devices, project agents, extensions\n\nAccounts, credentials, and task history live in ~/.local/share/xcb, outside\nyour projects (--state or XCB_STATE moves it). The\nCLI and configuration reference lists every\ncommand, setting, and exit code.
xcb link needs a relay deployed from this repository's convex/ folder (remote operations).xcb was formerly AgentMixer: xcb accounts import-agentmixer --source <path>\ncopies one Claude credential (migrating).\nThe compatibility reference covers the TypeScript\npackage and its xcb-compat CLI. Contributing ·\nSecurity · MIT license