Skip to main content
An agent runs these commands unattended, so a failure has to be legible to a program as well as to a person.

Exit codes

By default there are two. A caller branches on worked or did not, and which kind of failure it was is in the message. Hook modes always exit 0 whatever happens: a refresh does not get a vote on whether your work proceeds. to11 code exits with the agent’s exit code, so wrapping your agent in it changes nothing about what your shell sees. The exception is a session that never starts. A missing code.claude.yaml, or a project, environment or provider in it that cannot serve the session, exits 1 — the agent was not launched, so there is no exit code of its own to pass on.

Detailed exit codes

to11 skill sync --detailed-exitcode splits the outcome. Only when asked for — without the flag, sync exits 0 or 1 like everything else. 3 exists because 2 took the meaning 1 used to carry for it. With detailed codes on, a non-zero exit is ambiguous unless a bad flag is distinguishable from a real failure — a script that treats 2 as drift and 1 as broken would otherwise read a typo as a broken platform and start alerting. Paired with --dry-run this is a drift check, which is what a build step wants:
0 means the machine matches its configuration, 2 means it does not, 1 means the question could not be answered — which is different from the answer being no. Skipped counts as differing, not as failing. A skill you edited on disk means the machine does not match its configuration, and a build step should see that; but nothing went wrong, so it is not 1. It is opt-in because a non-zero exit on a successful run breaks every script that does not know about it.

What an error says

Every error names three things: what failed, where, and what to do about it.
No stack traces, ever. A panic is a defect in this CLI, and printing our internals at you asks you to debug our code. Everything above goes to stderr, so --format json on a failing command still parses.

Partial failure

Locations fail independently. One location reconciling while the other cannot is reported as exactly that, and the exit code is 1 because something was asked for and did not happen:
Each file is written whole or not at all. Files land in place, one at a time, so nothing ever reads half of one. A skill that fails part-way can leave the destination holding some files from the new version and some from the old. The run records what is there and reports the skill as failed, so running to11 skill sync again repairs it — no --force, and nothing calls your own directory a conflict. A location that fails leaves every skill it had not yet reached untouched.

Degraded beats broken

When something breaks, less still beats nothing — so failure has a shape:
  • A fetch that fails changes nothing. Previously installed skills stay exactly as they are, and your agent keeps working with what it has.
  • An expired credential is an error, not a wipe. Nothing is removed on the assumption that access was lost: that is indistinguishable from a network problem, and the recovery from guessing wrong is losing every skill at once.
  • An unreadable repository config is an error, not a fallback. Silently resolving from your own config alone would install fewer skills than the repository intends, without saying so.
  • A skill you edited is left alone, not overwritten — the row reads dirty. The recovery is yours to choose: commit it with to11 skill store, or discard it with to11 skill sync --force.
  • Two projects publishing one skill name installs neither, and exits 1. One directory cannot hold both, and picking one would depend on the order the projects appear in your config. The run names both projects and the skill, leaves whatever is already at that directory alone, and every other skill in the run is installed normally. Anything already installed there stays installed, and stays recorded — a skill your config asks for twice is not a skill your config dropped. This is the one warning-shaped outcome that still fails the run: no flag resolves it, so a 0 would report success for a sync that installed fewer skills than you asked for. Give one project a different prefix, or name the skill under one project.

Failing while nobody is watching

An automatic refresh has no terminal to print to. So:
  • It exits 0 regardless.
  • It says nothing, and leaves no note behind. Nothing about a failed refresh is stored for a later command to report.
  • It never retries in a loop.
to11 skill sync performs the same reconciliation and reports what that run finds — what is in sync, what conflicted, what it updated, and anything it could not do.

Common failures