I have a code agent—Claude Code—that interacts with Linear, my task management tool, about 800 times a month: listing tasks, creating issues, changing states, leaving comments. I reviewed 165 of its sessions and counted more than 500 errors and over 370 retries.
None of these were caused by issues in Linear’s API. All were interface errors: the agent communicated with the command line, and the command line didn’t understand it.
A conservative estimate of the cost puts it at about 700,000 tokens per month, burned just on wrestling with the tool: retrying, reading error messages, correcting, and trying again. It’s a hidden toll you’ll never see on an invoice, but it gets paid in every session.
Context: The Agent Is Now the Primary User
A CLI—a command-line tool—is traditionally designed for humans. And humans are, surprisingly, resilient users. If a command fails, they’ll read the help text using --help. If an error message is cryptic, they’ll search for answers. If the tool has quirks, they’ll learn them and avoid the same mistakes in the future.
An AI agent does almost none of this. It doesn’t carry experience between sessions as a human would. It skim-reads documentation less carefully than you’d hope. And when something goes wrong, it doesn’t stop to investigate; instead, it improvises with what it deems plausible.
This changes who your tool’s primary client is. If an agent invokes it 800 times a month and you use it manually just three, the agent becomes the main consumer of that interface. Designing it for a human and expecting the agent to adapt is optimizing for the minority user.
Deliberately designing for this user has a name: agentic experience. It’s to agents what user experience (UX) is to humans and developer experience (DX) is to those working with your API. And the best measurement tool for this needs no additional setup: you’re already generating it. It’s the agent’s error log.
Errors Reveal Patterns
I defined an error as any CLI invocation that resulted in a non-zero exit code. With that criterion, the more than 500 errors weren’t random: almost all fell into three patterns.
| Pattern | What the agent did | What it reveals about the design |
|---|---|---|
| Invented flags | Typed --status instead of --state; --priority urgent instead of --priority 1 | The real flag wasn’t what would intuitively come to mind |
| Missing operations | Tried to search text, filter by project, or assign a project when creating—unsupported | Tool didn’t match the actual workflow |
| Forgotten required flags | Missed --sort, --no-pager, --no-interactive | Mandatory decisions the tool could’ve handled itself |
When the agent typed --status, it wasn’t hallucinating; it was guessing the most plausible interface. --status is, objectively, as reasonable a name as --state. The agent chose the likeliest option, and my design didn’t align with that probability.
There’s a fourth issue not captured here because it never generates failed commands: verbose output. The CLI returned lists in JSON format—roughly 50 tokens per task. These commands succeeded—exit code zero—so they don’t count as errors; but when multiplied by long lists and 800 monthly invocations, they add up to the other half of those 700,000 tokens. A cost that goes unnoticed precisely because nothing breaks.
Rethinking Errors
The easy interpretation of those 500 errors is straightforward: the agent is using the tool wrong. But that’s the wrong conclusion, and it’s worth breaking that mindset.
An invented flag means the real one wasn’t the obvious choice. Forgetting a mandatory flag means that flag shouldn’t have been mandatory; if the tool can infer a sensible default, requiring it needlessly offloads work onto the caller. Verbose output burning tokens means the format was designed with the wrong consumer in mind.
An agent’s error log isn’t a list of the agent’s shortcomings. It’s a specification: each error describes, in reverse, a piece of the interface you should have implemented. And it’s also the most brutally honest feedback you’ll get: it’s free, massive in scale, and devoid of the courtesy humans might extend to obscure a tool’s flaws. A person stumbling over a poorly designed CLI stays quiet and finds workarounds. An agent doesn’t adapt. It keeps making the same mistakes tomorrow, and the day after, leaving a record of every stumble.
This ties into a principle I covered in another article: the wrong path should be impossible, not forbidden. Forbidding something is documentation—“don’t use --status”); documentation assumes someone’s going to pay close attention to it. Making an error impossible? That’s design.
The solution, then, wasn’t better documentation. It was a better tool.
The Redesign
I rewrote the CLI—called lql—using that principle. Four design decisions accounted for most of the improvement.
Tolerance instead of rejection. If the agent typed --status, the tool accepted it as an alias for --state and proceeded. If it typed --priority urgent, the tool translated it to --priority 1 and communicated the assumption back. The most common “wrong” path simply became a correct one. The tool didn’t punish reasonable guesses; it absorbed them.
Errors as teaching opportunities. If something truly didn’t exist, the error message wasn’t unknown flag. Instead, it included guidance: --filter doesn’t exist. To filter by state, use --state <state>. To search, use: lql search "text". The error message became documentation, delivered at the one moment the agent would pay full attention: right after failing.
Zero required flags. lql list works without any arguments: it sorts by priority, filters for active states, and detects the team from the working directory. There’s no --sort for the agent to forget because there’s no --sort to provide. A flag the agent can’t forget is one that simply isn’t required.
Output designed for the real consumer. The output reader is an LLM that pays for every token. The tool switched to TOON (Token-Oriented Object Notation), a compact format that encodes the schema once in a header and outputs positional values thereafter.
| Format | Tokens per task | 50 tasks |
|---|---|---|
| XML | ~70 | ~3,500 |
| JSON | ~50 | ~2,500 |
| TOON | ~25 | ~1,250 |
TOON isn’t my invention; it’s an open format (toonformat.dev) that the tool simply adopted. The --json flag is still there for standard scripts and pipelines, where a traditional machine consumer might prefer it.
The Test
The most convincing measure isn’t a performance benchmark. It’s this:
The agent needs an instruction file to work with Linear—in Claude Code, it’s called a “skill.” For the old CLI, this file was 246 lines long. 150 of those were workarounds: “if this happens, do this instead,” “remember to add this flag,” and “don’t use this form.” Defensive documentation written to patch gaps in the tool.
After the redesign, that file shrank to 205 lines and had zero workarounds. A tolerant tool doesn’t need any excuses. Those 150 lines didn’t simply vanish because I deleted them—they vanished because there was nothing left to add.
The Limits of This Analysis
For the sake of transparency, here are the boundaries. This analysis involved one agent—Claude Code—and one API—Linear. The exact distribution of these three error patterns will vary with another agent or another API. The nature of the problem—the agent guesses the most plausible interface and fails when the design doesn’t match its probability—seems consistent, but that’s a hypothesis, not a certainty.
The count of over 500 errors and 370 retries comes from parsing the JSON files of 165 of Claude Code’s sessions. The error definition is straightforward—a non-zero exit code—while a retry is logged as re-executing the same command after a prior failure. The criteria are mechanical and repeatable: exit codes don’t lie, even though the data comes from real-world usage rather than a controlled experiment.
Try It Out
lql is open source under the MIT license. The code is available at github.com/frr149/lql.
brew install frr149/tools/lql
lql list --team PROD --state Todo --priority urgent
What to Do When Connecting an Agent to a CLI
If you have an AI agent interacting with a command-line tool—yours or a third party’s—you already have the data you need to improve it: the errors the agent makes when using it.
Don’t dismiss these as noise in the logs or blame them on the agent. Extract them, classify them by pattern, and read them for what they really are: the blueprint for your interface, drawn in reverse. Every flag the agent invents is a suggestion for what the real flag should be called; every operation it tries that doesn’t exist is a feature request.
This article was originally published in Spanish and translated with the help of AI.