{"slug": "cli-tools-have-always-been-broken", "title": "CLI tools have always been broken", "summary": "A 20-year shell veteran argues that CLI tools suffer from a systemic design flaw: each tool independently reimplements the same concerns — parsing, I/O formatting, compression, streaming, exit codes, and introspection — making consistency impossible without universal conventions. The problem, the author writes, is especially acute as AIs begin to require large libraries of CLI tools, since `--help` output is human-oriented prose rather than a machine-readable contract, structured data cannot be shared without lossy JSON or heavy Protobuf/Parquet frameworks, and feature addition or subtraction has no clear solution short of rewriting the program.", "body_md": "September 15, 2026\n\nAs one who’s lived in the shell for twenty years, I’m happy (though not surprised) to see how naturally the CLI fit into the AI world.\n\nHowever, there is a systemic flaw in CLI tooling. Each tool reimplements the same concerns: parsing, I/O formatting, compression, streaming, exit codes, introspection, and more. The builders, whether human or AI, navigate this design space independently. The only path to consistency is for all creators to agree on a wide range of conventions and write their code accordingly.\n\nNow this flaw is easy to miss when we use only a few CLI tools and only interactively. It bites when we use CLIs at scale and when we use them as functions in workflows. The problem is especially acute now as AIs begin to require large libraries of these CLI tools.\n\nI describe specific instances of this problem below:\n\n**Tool self-descriptions are inconsistent**. Suppose you want to loop over the\ntools in an environment and assemble a manifest of what each one does. CLI tools\nare supposed to be self-describing, so you should just be able to loop over all\ntools and call them with `--help`. But this fails. Some tools have no `--help`\nat all, some take seconds to produce it (looking at you, Python), and many are\ncreatively formatted. `--help` is prose meant for humans and (usually) written\nby hand, not a machine-readable contract derived from the tool itself. If you\nwant to make a system of tools machine-accessible, you must maintain wrappers\naround the tools and update them as the tools change.\n\n**Sharing complex data requires agreement on format**. The UNIX philosophy that\neverything is a file makes interoperability easy: any tool can take anything as\ninput. But if the input can be anything, only the most general operations are\nwell formed. We can count bytes, grep for patterns, or concatenate streams; for\nanything more, the tool must know what the bytes mean. Passing structured data\nis trivial within a native program, but in a system of CLI tools this freedom is\nlost. A few special cases, like mp3 and png, get dedicated formats but\nstructured data in general cannot be shared. Serializing to JSON is lossy\n(integer widths vanish) while serializing with Protobuf or Parquet requires\nagreeing on a heavy framework. Either way, tools depend on shared conventions\nand implicit knowledge of the upstream producers.\n\n**Duplication of concerns multiplies dependencies and slows innovation**. Much\nof what a CLI tool does is orthogonal to its core job, including compression,\nparallelism, serialization, argument handling. None of this is hard – the\nalgorithms are well established. But each algorithm is re-wrapped in each\nlanguage and each tool must independently decide which to import, which settings\nto use, and what flags to expose. In cases like compression, where downstream\ntools must understand the chosen format, the best option is often not the most\nefficient algorithm but the one most likely to be widely supported. These\nindependent choices multiply dependencies, can break compatibility, resist\nevolution, and cannot be set for the whole system.\n\n**Feature addition or subtraction**. Every maintainer must balance between\nfeature addition and weight reduction. New features may add dependencies, add\ndistracting complexity, slow compile time, and increase maintenance costs. The\nuser wants two powers the monolith denies: addition and subtraction. A\ncompile-time plugin system could offer addition, but that’s heavy. In twenty\nyears of writing CLI tools, I’ve never written a CLI plugin system. Subtraction,\nthe removal of unwanted or problematic features, has no clear solution short of\nrewriting the program. So the creator chooses a set of features and the user\nmust accept it.\n\n**Commits to one face**. A CLI tool is made of internal functions and an\ninterface that exposes them. The functions are stable, testable and inherently\ncompatible with many interfaces: a human at the shell types flags, a program\npasses values directly, a network client sends JSON, a model picks a tool and\nfills in arguments. The callers change but the functions do not. But when all we\nhave is a CLI, every other interface must be laid over it. Network clients,\nlibrary bindings, and MCPs all must generate system calls, follow human-oriented\nflag conventions, and pass stringified types. The connectivity is backwards –\nthe CLI should be just one interface among equals.\n\nThe solution is *not* a new best practices document (or AI skill). The solution is\nalso *not* to wrap every CLI tool in yet another layer. We do not need yet another\nframework layered on a flimsy foundation. I propose the opposite, rather than\nadding a layer, we subtract them. I propose we strip the CLIs down to their\nbasal functions, annotate the functions, and distribute them directly. Then we\nautomatically generate the CLIs and other concerns when needed. And no, I’m not\nsuggesting an AI solution here, that would only change where and how conventions\nare defined. We need something we can trust. A compiler.\n\nI built the Morloc compiler (https://github.com/morloc-project/morloc) for this purpose. It allows functional composition across language under a common type system with CLIs, APIs, MCP models, and usage statements generated deterministically. This provides consistency by construction.", "url": "https://wpnews.pro/news/cli-tools-have-always-been-broken", "canonical_source": "https://www.zebulun-arendsee.com/series/morloc/2026-09-15-clis-have-always-been-broken.html", "published_at": "2026-09-22 10:45:55+00:00", "updated_at": "2026-09-22 10:54:15.934417+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Python", "JSON", "Protobuf", "Parquet"], "alternates": {"html": "https://wpnews.pro/news/cli-tools-have-always-been-broken", "markdown": "https://wpnews.pro/news/cli-tools-have-always-been-broken.md", "text": "https://wpnews.pro/news/cli-tools-have-always-been-broken.txt", "jsonld": "https://wpnews.pro/news/cli-tools-have-always-been-broken.jsonld"}}