{"slug": "cli-tools-have-always-had-a-problem", "title": "CLI tools have always had a problem", "summary": "A developer with twenty years of shell experience argues that the CLI ecosystem suffers from a systemic flaw: every tool independently reimplements the same concerns—parsing, I/O formatting, compression, streaming, exit codes, and introspection—producing inconsistent interfaces that break down at scale, especially as AI systems come to depend on large libraries of CLI tools. The developer cites inconsistent self-description via --help, the lack of a shared format for structured data, duplicated dependencies, and the absence of a viable plugin or feature-subtraction mechanism as core problems.", "body_md": "As one who has lived in the shell for twenty years, I've been happy (though not surprised) to see how naturally the CLI fit into the AI world.\n\nHowever, there is a systemic flaw in the CLI ecosystem: 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, leading to an ecosystem where each interface is unique. Since all tools vary 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 isn't a problem when we use CLI tools interactively, but it bites the moment we start using CLIs at scale and directly use their results. But it bites the moment we start using them in more complex cases. 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 tools in an environment and assemble a manifest of what each one does. CLI tools are supposed to be self-describing, so you should just be able to loop over all tools and call them with `--help`. But this fails. Some tools have no `--help` at all, some take seconds to produce it (looking at you, Python), and formatting is idiosyncratic. `--help` is prose meant for humans, not a machine-readable contract derived from the tool itself. If you want to make a system of tools machine-accessible, you must maintain wrappers around the tools and update them as the tools change.\n\n**Sharing complex data requires agreement on format**. The UNIX philosophy that everything is a file makes interoperability easy: any tool can take anything as input. But if the input can be anything, only the most general operations are well formed. We can count bytes, grep for patterns, or concatenate streams; for anything more, the tool must know what the bytes mean. Passing structured data is trivial within a native program, but in a system of CLI tools this freedom is lost. A few special cases, like mp3 and png, get dedicated formats but structured data in general cannot be shared. Serializing to JSON is lossy (integer widths vanish) while serializing with Protobuf or Parquet requires agreeing on a heavy framework. Either way, tools depend on shared conventions and implicit knowledge of the upstream producers.\n\n**Duplication of concerns multiplies dependencies and prevents innovation**. Much of what a CLI tool does is orthogonal to its core job: compression, parallelism, serialization, argument handling. None of this is hard -- the algorithms are well established. But each algorithm is re-wrapped in each language and each tool must independently decide which to import, which settings to use, and what flags to expose. In cases like compression, where downstream tools must understand the chosen format, the best option is often not the most efficient algorithm but the one most likely to be widely supported. These independent choices multiply dependencies, can break compatibility, resists evolution, and cannot be set for the whole system.\n\n**Feature addition or subtraction**. Every maintainer must balance between feature addition and weight reduction. New features may add dependencies, add distracting complexity, slow compile time, and increase maintenance costs. The user wants two powers the monolith denies: addition and subtraction. A compile-time plugin system could offer addition, but that's heavy. In twenty years of writing CLI tools, I've never written a CLI plugin system. Subtraction, the removal of unwanted or problematic features, has no clear solution short of rewriting the program. So the creator chooses a set of features and the user must accept it.\n\n**Commits to one face**. A CLI tool is made of internal functions and an interface that exposes them. The functions are stable, testable and inherently compatible with many interfaces: a human at the shell types flags, a program passes values directly, a network client sends JSON, a model picks a tool and fills in arguments. The callers change but the functions do not. But when all we have is a CLI, every other interface must be laid over it. Network clients, library bindings, and MCPs all must generate system calls, follow human-oriented flag conventions, and pass stringified types. The connectivity is backwards -- the CLI should be just one interface among equals.\n\nThe purpose of my post here is to describe the problem, not the solution. We need to accept that the problem exists before we can solve it. That said, I do have a solution.\n\nThe solution is NOT a new best practices document (or AI skill). The solution is also NOT to wrap every CLI tool in yet another layer. What I propose is that we strip the CLIs down to their cores and distribute the functions. Then we generate the CLIs deterministally from the functions given their types. And no, I'm suggesting AI here, that would only change how conventions are defined. We need something we can trust. A compiler.\n\nFor the last decade, I have been building the Morloc compiler for this purpose. Morloc generates rich CLIs (and other interfaces) from functions sourced from many languages and unified under a common type system. The CLI is projected from the type. You can find links to more info on [GitHub](https://github.com/morloc-project/morloc) or a post with examples [here](https://dev.to/arendsee/making-clis-mcps-and-apis-from-one-source-with-morloc-1c87). If you disagree with the problem, let me know in the comments.\n\nImage: mismatching gears by MidJourney", "url": "https://wpnews.pro/news/cli-tools-have-always-had-a-problem", "canonical_source": "https://dev.to/arendsee/cli-tools-have-always-had-a-problem-2hga", "published_at": "2026-09-18 12:38:19+00:00", "updated_at": "2026-09-18 12:52:51.192904+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Python"], "alternates": {"html": "https://wpnews.pro/news/cli-tools-have-always-had-a-problem", "markdown": "https://wpnews.pro/news/cli-tools-have-always-had-a-problem.md", "text": "https://wpnews.pro/news/cli-tools-have-always-had-a-problem.txt", "jsonld": "https://wpnews.pro/news/cli-tools-have-always-had-a-problem.jsonld"}}