Skip to content

Two styles, one engine ​

Argot lets you declare a command line two ways: Kotlin delegates, or annotations on a data class with a parser generated by KSP. Libraries that offer two front ends usually end up with two behaviours, because each front end grows its own parsing code and the two drift. Argot avoids that by structure rather than by care.

Where the two paths meet ​

Both styles converge on one type before any parsing happens:

delegates ─────┐
               ├──▶ CommandSpec ──▶ ArgotEngine ──▶ ParsedValues
annotations ───┘

CommandSpec is the whole description of a command: its name, its description, and the options, flags and positionals it accepts. Delegates build one as you read properties. The generated parser builds one in its init. Neither contains parsing logic — they only describe.

Everything a user can observe happens after that meeting point. ArgotEngine does all the tokenising, matching, conversion and error reporting. CommandSpec.renderHelp produces the entire --help screen. Both styles reach the same code because there is only one copy of it.

Why this matters more than it sounds ​

Two implementations would not diverge on the obvious things — everyone remembers that --name Ada sets name. They diverge on the edges: whether --count=2 is accepted alongside --count 2, whether a repeated single-valued option is an error or a silent overwrite, exactly how a converter failure is worded, how many spaces separate a flag from its help text.

Those are the details your own tests will assert on. With one engine, there is nothing that could disagree.

What the processor is, and is not ​

The KSP processor is not a parser. It reads your annotated constructor, resolves each parameter's type to a converter, and emits a function that builds a CommandSpec, calls ArgotEngine, and hands the results to your constructor. It is a transcription step, not a second implementation.

That is also why the processor never reaches your runtime classpath. Its entire output is ordinary Kotlin calling argot-core.

What this means for mixing versions ​

Because generated code calls CommandSpec, the ParamSpec constructors, ArgotEngine.parse and the ParsedValues accessors directly, those declarations are a public contract even though nobody types them. You can end up with argot-processor from one version and argot-core from another in the same build — a transitive dependency is enough — and code emitted by the older processor still has to compile against the newer core.

Argot therefore treats that set of declarations as a versioned surface in its own right. See what counts as a breaking change.

See also ​

Released under the Apache License 2.0.