Releases: forfudan/argmojo
Release list
ArgMojo v0.8.0
20260812 (v0.8.0)
ArgMojo v0.8.0 moves to the first stable Mojo release, v1.0.0. It also fixes a dozen parser bugs, repairs an FFI signature clash that could break the build of a project using ArgMojo, cuts compile time by about a fifth, and fills several gaps in the declarative API.
ArgMojo v0.8.0 targets Mojo v1.0.0.
⭐️ New in v0.8.0
-
Add
ParseResult.was_provided(name)— True only when the user really supplied the argument, be it on the command line, at a prompt, or through animplies()rule.has(name)cannot tell you that, because a default is stored in the same place as a parsed value. Group constraints use it internally, and it is often what you want in your own code too:if result.was_provided("format"): print("the user asked for " + result.get_string("format")) else: print("falling back to the default format")
-
Add
ParseResult.get_float(name)— reads a value asFloat64, the wayget_int()reads it asInt. -
Option[Float64]andPositional[Float64]now work end to end, defaults included.has_rangestays integer-only, so combining it withFloat64is rejected at compile time instead of failing later with a puzzling "expected an integer". -
Countcan be unwrapped withInt(), asFlagcan withBool(). WriteInt(args.verbose)instead ofargs.verbose.value. -
The four declarative wrappers now take a consistent set of parameters.
alias_namewas onOptiononly anddeprecatedonOptionandFlagonly; both are now available wherever they make sense.Positionalgainshidden,deprecated,prompt,prompt_text,password, and the range parameters (has_range,range_min,range_max,clamp) thatOptionalready had. Until now, needing any one of these on a positional meant dropping to the builder API for that one field.
🔧 Fixes in v0.8.0
- Fix an FFI signature clash in
_read_password_asterisk(). Theread(2)binding passed its buffer asInt(ptr), which declaresreadas(Int, Int, Int) -> Int, while the standard library declares the same symbol as(Int, Pointer, Int) -> Int. Any module linking both failed to lower to LLVM IR. The buffer is passed as a real pointer now (PR #59). - Group constraints counted a default as user input.
mutually_exclusive(),required_together(),one_required()andrequired_if()all askedhas(), which is True for an argument that merely carries a.default()— so--jsonalone could conflict with a--formatnobody had typed. All four consultwas_provided()now. - A required positional could be satisfied by a later positional's default. Filling the later slot pads the positional list up to that index, and the earlier, still-empty slot then looked provided:
src(required) followed bydst(with a default) parsed happily with no arguments at all. A default on the required argument itself still satisfies it, as in clap. app --=helloused to set the first positional. The empty name left after splitting on=matched_long_name == "", which is true for every positional and every short-only option. It is now reported asInvalid option '--=hello': missing option name.- An argument carrying both
.prompt()and.default()was never prompted, because defaults were applied first and prompting skips anything that already holds a value. Prompting runs first now, and the default is what an empty answer — or a non-interactive stdin — falls back to, which is what the "(default)" hint always promised. - Defaults now reach the accessor that belongs to the argument. Every default used to be written to the string store, so
.flag().default["true"]()was invisible toget_flag(), and.append().default["x"]()leftget_list()empty whileget_string()returned the value. Defaults go to_flags,_counts,_lists,_mapsor_valuesaccording to the kind of the argument. - Bad defaults are caught at registration instead of reaching the user.
add_argument()now raises if a default is not one of the declared.choice[…]()values, if a.flag()default is not a boolean literal, if a.count()default is not an integer, or if a.map_option()default is not inkey=valueform;default_if_no_valueis checked against the choices too. The declarative wrappers already checked the choices at compile time; the builder API checked nothing. - An option no longer swallows another option as its value:
--output --verboseused to store the literal string"--verbose". Only registered options and the--marker are refused, so--offset -5and--pattern -foostill work, and.allow_hyphen_values()opts out entirely. The guard covers short options and.number_of_values[N]()as well. - Persistent-argument conflicts were detected in only one registration order. The check lived in
add_subcommand(), so callingadd_subcommand()first andadd_argument(... .persistent())afterwards slipped past it, and the flag then appeared twice in the child's help.add_argument()runs the mirror check now, and the injection at dispatch time skips an argument the child already owns. - Non-ASCII passwords came back corrupted.
_read_password_asterisk()rebuilt the typed string byte by byte throughchr(), which treats each byte as a code point and re-encodes it, soé(C3 A9) arrived asé(C3 83 C2 A9) and never matched. The bytes are decoded as UTF-8 in one step now, and the buffer is zeroed before it is freed. - An
implies()rule could fire from a default value — fix 2 again, by another route. The trigger washas(trigger), so a--modethat merely defaults tofaststill set--parallel; since an implied argument counts as user input, typing only--quietthen reported a conflict with a--parallelnobody had asked for. Implications fire onwas_provided(trigger). parse_known_arguments()applied defaults before implications, the reverse ofparse_arguments(), so the two entry points could disagree about which arguments counted as supplied. Both run implications first now.
🔄 Mojo v1.0.0 migration (PR #59)
- Bump the Mojo dependency from
==1.0.0b2to>=1.0.0, <1.1.0inpixi.toml, so ArgMojo builds against any Mojo v1.0.x release. - Replace the removed
_constrained_field_conforms_tohelper withcomptime assert conforms_to(...)plus_field_conforms_to_error, which is how the standard library now writes reflection-driven trait defaults. - Drop the
trait_downcast[…]()calls inParsable. Acomptime if conforms_to(...)guard (or acomptime assert) is now enough for the compiler to resolve trait methods on a reflected field. - Replace
__struct_field_ref(i, x)with the publicreflect[Self].field_ref[i](x). - Rename
ImplicitlyDestructibletoDeinitable, and dropMovablefrom theDefaultable & Copyable & Movablebounds, which the compiler now reports as redundant. - Rename the move constructor argument from
deinit take:todeinit move:inArgument,Command,ParseResult, and the four argument wrappers. - Replace
UnsafePointer(to=f).init_pointee_move(v)withPointer(to=f).unsafe_write(v). - Give
CommandandParseResultan explicit, empty__deinit__(). Both hold aList[Self]field, and deducingList[Command]: Deinitablenow requiresCommand: Deinitable— exactly what is being deduced. Declaring the destructor breaks the cycle, and the fields are still destroyed automatically. - Introduce temporary variables where a
Stringis rebuilt from a slice of itself (e.g.key = String(key[byte=:eq])), because such statements now trip the exclusivity checker. - Dispatch the declarative wrappers on
reflect[T].name()compared against the reflected names of the supported types, instead of against string literals. In Mojo v1.0.0,Intbecame an alias ofScalar[DType.int]and so reflects as"SIMD[DType.int, 1]"; comparing one reflected name with another keeps working across such renames. - Update
tests/test_wrappers.mojoto call the move constructor as(move=a^).
⚡️ Performance in v0.8.0
-
Compiling a program that uses ArgMojo is about 20% faster. Measured cold, with the compiler cache wiped, over the eight examples: 50.2 s down to 39.8 s in total, or 6.08 s down to 4.81 s per example. Nothing about the behaviour changes — help text, the three completion scripts and the error messages are byte-for-byte identical before and after. Four changes get there:
examples/build.shbuilds against theargmojo.mojocit already produces (-I .) instead of recompilingsrc/argmojointo all eight binaries (-I src). This is the bulk of it: 50.2 s → 42.7 s on its own. Its timing summary now reports hundredths of a second and a total, rather than whole seconds fromdate +%s.- Argument lookup returns an index instead of a copy.
_find_by_long()and_find_by_short()deep-copied the wholeArgumenton each option token, only to read two or three fields; they are_find_index_by_long()/_find_index_by_short()now, and callers bind the result withref. The per-loop copies in_prompt_missing_arguments(),_validate(),_apply_defaults()and the three completion generators becamerefbindings too. This is the largest runtime saving in the release as well: dozens of heap allocations disappear from every parse. - Long
+chains becameString(...)calls. Building a message as"a" + x + "b" + y + ...emits a separate inlined concatenation for every+; passing the same pieces toString(...)emits one call. 154 sites acrosscommand.mojo. _looks_like_number()and_levenshtein()compare bytes rather than one-character strings.token[byte=j:j+1] >= "0"writes out a full string comparison per character; aUInt8against a byte constant is one instruction._looks_like_number()alone dropped from 17,850 lines of unoptimised IR to 4,431.
Together the source changes take th...
ArgMojo v0.7.0
20260618 (v0.7.0)
ArgMojo v0.7.0 migrates the codebase to Mojo v1.0.0b2.
ArgMojo v0.6.0
20260510 (v0.6.0)
ArgMojo v0.6.0 introduces auto-dispatch for subcommands, improves parser behavior for mathematical-expression tokens, and migrates the codebase to Mojo v1.0.0b1.
⭐️ New in v0.6.0
- Add
allow_negative_expressions()onCommand— treats single-hyphen tokens as positional arguments when they do not conflict with registered short options. Handles mathematical expressions like-1/3*pi,-sin(2), and-e^2. This is a strict superset ofallow_negative_numbers(). This is useful for mathematical tools, e.g., decimo CLI. (PR #52) - Add auto-dispatch —
set_run_function(handler)registers adef(ParseResult) thin raises -> Nonehandler on aCommand;execute()parses and auto-dispatches to the matching handler, eliminating manualif/elifsubcommand chains._execute_with_arguments(args)provides equivalent dispatch for tests with explicit argument lists. Works with nested subcommands, aliases, and persistent flags. (PR #53)
🔄 Mojo v1.0.0b1 migration
- Bump Mojo dependency to
==1.0.0b1inpixi.toml. (PR #54) - Migrate stored function pointers for run handlers to use the
thinfunction effect (def(ParseResult) thin raises -> None). (PR #54) - Add explicit
ImplicitlyDestructiblebounds where required (Defaultable,Movable,Parsablegenerics), because trait hierarchy implications changed. (PR #54) - Replace deprecated
len(String)usage withString.byte_length(). (PR #54) - Replace deprecated reflection helpers (
get_type_name[T](),struct_field_*[T]()) with the unifiedreflect[T]()API. (PR #54)
What's Changed
- [core][declarative] Add compile time validation on flag length, choices, and range by @forfudan in #45
- [test][github] Split the current Github tests into 5 jobs by @forfudan in #46
- [core] Add
preludemodule for convenient import by @forfudan in #47 - [core] Add
allow_negative_expressions()method forCommandby @forfudan in #48 - [core] Add auto dispatch + Extend the
mgitexample by @forfudan in #49 - [core] 80-character help formatting + Fix CJK description issues in zsh and fish by @forfudan in #50
- [example][doc] Split subcommands from
main()into their own buiding functions + Update user manual by @forfudan in #51 - [core] Enhance help and error messages with improved formatting by @forfudan in #52
- [core] Change "arg"/"cmd" to "argument"/"command" + Change "args" from
argvto "tokens" by @forfudan in #53 - [doc] Update the user manual by @forfudan in #54
- [core][doc] Refactor parse-related functions by adding new shared helpers and a unified parse loop + Add a document on parsing algorithm by @forfudan in #55
- [mojo][core] Update the codebase to Mojo v1.0.0b1 by @forfudan in #56
- [doc] Update documents for release by @forfudan in #57
Full Changelog: v0.5.0...v0.6.0
ArgMojo v0.5.0
20260404 (v0.5.0)
ArgMojo v0.5.0 introduces the struct-based declarative API — define a Parsable struct with typed wrapper fields (Option, Flag, Positional, Count), call MyArgs.parse(), and get typed results. The declarative API coexists with the existing builder API and bridges between them via to_command() / parse_from_command(). This release also adds asterisk-masked password input and reorganises internal modules.
ArgMojo v0.5.0 targets Mojo v0.26.2.
⭐️ New in v0.5.0
- Declarative API core —
Parsabletrait with compile-time reflection over wrapper-typed fields. Conforming structs need only declare fields and providedescription(); all parsing, Command-building, and write-back methods are provided as trait defaults (PR #34, #35, #37, #40). - Wrapper types —
Option[T, ...],Flag[...],Positional[T, ...],Count[...]parametric structs encode CLI metadata as compile-time parameters. Conform to the internalArgumentLiketrait withadd_to_command()/read_from_result()hooks (PR #34). - Auto-initialisation —
Parsable.__init__usesmark_initialized+comptime for+UnsafePointer.init_pointee_moveto default-construct all fields via reflection. Users never need to write__init__(PR #37). - Hybrid bridge —
to_command()reflects aParsablestruct into an ownedCommandfor builder-level customisation;parse_from_command(cmd^)parses back into a typed struct (PR #35, #40). - Dual-return parsing —
parse_full()returnsTuple[Self, ParseResult]for workflows that need both typed struct fields and rawParseResultaccess (e.g. subcommand dispatch).parse_full_from_command(cmd^)does the same from a pre-configured Command (PR #40, #42). - Subcommand support —
subcommands()hook onParsablereturnsList[Command]; called automatically byto_command()for recursive tree assembly.run(self)method for leaf command execution.from_parse_result()for typed write-back from subcommand results (PR #39, #43). - Asterisk-masked password input —
.password[True]()onArgumentechoes*for each keystroke (sudo-rs style), complementing the existing.password()which hides input entirely. Uses raw terminal mode with ICANON/ISIG disabled (PR #32).
🦋 Changed in v0.5.0
- Remove
Argalias forArgumentand dropArgumentLikefrom the public export list. The public API now exports:Argument,Command,ParseResult,Parsable,Option,Flag,Positional,Count(PR #42). subcommands()hook signature changed fromsubcommands(mut cmd: Command) raisestosubcommands() raises -> List[Command]— returns a list of Commands instead of mutating one (PR #43).- Reorganise
ArgumentandCommandsource files into clearly grouped internal sections for readability (PR #33).
📚 Documentation and testing in v0.5.0
- Add
docs/declarative_api_planning.md— comprehensive design document for the declarative API (PR #33). - Add 4 declarative examples:
search.mojo(pure declarative),deploy.mojo(hybrid),convert.mojo(full parse + extra builder args),jomo.mojo(subcommands with hybrid tree) (PR #41). - Add 4 test modules:
test_wrappers.mojo(wrapper defaults, copy/move,Flag.__bool__),test_declarative.mojo(to_command, parse_args, from_parse_result),test_hybrid.mojo(builder customisation, mutually exclusive, implies, configure pattern),test_subcommands_declarative.mojo(flat/nested subcommands, run() dispatch, dual return) (PR #38, #39). - Add
test_interactive.mojofor interactive prompting edge cases (PR #33). - Add method matrix table to README, user manual, and planning doc.
- Update README with declarative API Quick Start, examples table, and project structure.
- Update user manual with declarative API usage guide, full-parse section, and API summary.
What's Changed
- [core] Enable asterisk-masked password input by @forfudan in #32
- [doc] Add a planning file for declarative API + Reorganize
Command/Argumentinternals for clearer grouping by @forfudan in #33 - [core][declarative] Implement argument wrappers and Parsable trait by @forfudan in #34
- [core][declarative] Implement
Parsablereflection, trait methods, and example by @forfudan in #35 - [core][declarative] Use
rebindinstead ofUnsafePointerin argument wrapper write-back logic by @forfudan in #36 - [core][declarative] Default-initialize all fields via reflection in
__init__ofParsabletrait by @forfudan in #37 - [core][declarative] Add tests for hybrid declarative + builder API by @forfudan in #38
- [core][declarative] Add tests for declarative subcommands by @forfudan in #39
- [core][declarative] Consolidate Parsable trait functions into methods by @forfudan in #40
- [core][declarative] Add more examples of declarative API by @forfudan in #41
- [core][declarative][doc] Enrich documentation and rename some methods by @forfudan in #42
- [core][declarative] Update
to_commandso that it returns a list ofCommandby @forfudan in #43 - [declarative][doc] Standardize
Parsableparsing method names + Update documents for release by @forfudan in #44
Full Changelog: v0.4.0...v0.5.0
ArgMojo v0.4.0
20260321 (v0.4.0)
ArgMojo v0.4.0 adds interactive prompting, password input, confirmation, argument parents, custom usage lines, response files, CJK-aware help formatting, and full-width auto-correction. The builder API is fully parameterised with compile-time StringLiteral parameters. The codebase is migrated to Mojo v0.26.2 and the test suite consolidated from 20 files into 8 modules.
ArgMojo v0.4.0 targets Mojo v0.26.2.
⭐️ New in v0.4.0
- Add
.default_if_no_value["value"]()— option appears without a value to use the fallback; with=valueto override. For long options, implies.require_equals()(PR #12). - Add
.require_equals()— long options reject space-separated values and require--key=valuesyntax. Help output adapts:--key=<value>for require-equals,--key[=<value>]for default-if-no-value (PR #12). - Add
response_file_prefix()onCommandfor@args.txtexpansion with comments, escaping, and recursive nesting. (Temporarily disabled due to a Mojo compiler deadlock under-D ASSERT=all.) (PR #12). - Add
.remainder()onArgument— consume all remaining tokens (including dash-prefixed) as a single positional, similar to argparsenargs=REMAINDER(PR #13). - Add
parse_known_arguments()onCommand— unrecognised options are collected viaresult.get_unknown_args()instead of raising an error (PR #13). - Add
.allow_hyphen_values()onArgument— accept tokens starting with-as values without requiring--first; covers the stdin-convention (PR #13). - CJK-aware help alignment using terminal display width; CJK characters treated as 2-column-wide. No API changes — automatic (PR #14).
- Full-width → half-width auto-correction with a coloured warning when CJK users type fullwidth ASCII (e.g.,
--verbose→--verbose). Disabled viadisable_fullwidth_correction()(PR #15). - CJK punctuation auto-correction — em-dash
——verbosecorrected to--verbose. Disabled viadisable_punctuation_correction()(PR #16). - Add
.group["name"]()onArgument— group arguments under dedicated headings in--helpoutput (PR #17). - Value-name wrapping control:
.value_name["NAME"]()renders as<NAME>by default;.value_name["NAME", False]()renders bare (PR #17). - Add runtime
generate_completion("shell")overload alongside the existing compile-timegenerate_completion["shell"]()(PR #19). - Registration-time validation for
mutually_exclusive(),required_together(),one_required(), andrequired_if()— catches typos, empty lists, and self-referential rules immediately atadd_argument()time (PR #22). - Add
.prompt()/.prompt["text"]()onArgument— interactively prompt for missing values before validation. Shows choices and defaults; handles non-interactive stdin gracefully (PR #23). - Add
add_parent(parent)onCommand— share argument definitions and group constraints across commands, equivalent to argparseparents(PR #25). - Add
confirmation_option()onCommand— register--yes/-yand prompt for confirmation after parsing;--yesskips the prompt (PR #26). - Add
usage(text)onCommand— replace the auto-generated usage line with a custom string (PR #27). - Add
.password()onArgument— hide typed input via POSIXtcsetattr(3)during prompts, equivalent to Clickhide_input=True(PR #28).
🔄 Mojo v0.26.2 migration (PR #29)
- Bump Mojo dependency to
==0.26.2inpixi.toml. - Migrate stdlib imports to
std.*namespace (from sys→from std.sys, etc.). - Replace
constrained[]withcomptime assertand@parameter ifwithcomptime if. - Unify
__copyinit__/__moveinit__to new__init__(out self, *, copy/take: Self)forms. - Remove
Stringablefrom trait lists (removed from stdlib). - Add
byte=keyword to all string slicing operations. - Replace
fnwithdefacross all 30 files (754 declarations). - Remove
__str__methods; consolidate string output intowrite_to(Mojo v0.26.2 convention).
🦋 Changed in v0.4.0
- Rename
.metavar()to.value_name()across the entire API (PR #13). - Parameterise
.long[]()and.short[]()as compile-timeStringLiteralparameters (PR #20). - Parameterise
.alias_name[](),.delimiter[](),.default[](),.deprecated[](),.default_if_no_value[](),.group[](),.prompt[](), and colour setters (header_color[],arg_color[],warn_color[],error_color[]) as compile-time parameters (PR #18, #21). - Replace
.choices(list)with chained.choice["a"]().choice["b"]()compile-time parameters (PR #18). - Value-name display uses angle brackets by default;
.value_name["FOO", False]()for bare display (PR #17). - Move
print_summary()fromCommandtoParseResult(PR #24).
🔧 Fixes in v0.4.0
- Clarify that
default_if_no_valuedoes not "reject"--key value— it simply does not consume the next token (PR #12). - Fix Click description: "Python CLI framework", not "built on top of argparse" (PR #12).
- Reject
.require_equals()/.default_if_no_value()combined with.number_of_values[N]()atadd_argument()time (PR #12).
📚 Documentation and testing in v0.4.0
- Consolidate test suite from 20 files into 8 focused modules (PR #30).
- Add Developer Validation section to user manual with two-layer validation model (compile-time + registration-time) (PR #22).
- Add
pixi run debugtask for-D ASSERT=allregression testing (PR #22).
What's Changed
- [core] Add
default_if_no_valueandrequire_equals+ add cross-library reference in user manual by @forfudan in #12 - [core] Implement response file (but temporarily not exposed to users due to issues when setting
ASSERT=all) by @forfudan in #13 - [core] Rename
metavartovalue_name+ consume remainder args + hyphen values + partial parsing by @forfudan in #14 - [core][cjk] Implement CJK-aware help formatting by @forfudan in #15
- [core][cjk] Implement fullwidth-to-halfwidth conversion by @forfudan in #16
- [core] Add argument goups in help message by @forfudan in #17
- [core] Convert color customization to a compile-time parameter of
StringLiteraltype by @forfudan in #18 - [core] Provide
generate_completionoverload that takes shell name as parameter by @forfudan in #19 - [core] Make long name and short name compile-time parameters by @forfudan in #20
- [core] Convert all other arguments of the methods of the
Argumenttype to compile-time parameters by @forfudan in #21 - [core] Implement aggressive runtime validation for
Commandby @forfudan in #22 - [core] Enable interactive prompting for arguments by @forfudan in #23
- [core] Move
print_summaryfromCommandtoParseResultby @forfudan in #24 - [core] Add argument parents by @forfudan in #25
- [core] Add confirmation option (--yes / -y) by @forfudan in #26
- [core] Enable custom usage line by @forfudan in #27
- [core] Add password / masked input capability by @forfudan in #28
- [test] Consolidate the test suite by @forfudan in #30
- [doc] Update documents for release by @forfudan in #31
Full Changelog: v0.3.0...v0.4.0
ArgMojo v0.3.0
20260305 (v0.3.0)
ArgMojo v0.3.0 adds shell completion, typo suggestions, mutual implication, hidden subcommands, NO_COLOR support, and several builder-method improvements. Internally the code is decomposed into smaller helpers and a new utils.mojo module; several API names are refined for consistency. Two breaking changes affect call sites that use nargs, max, or range (now compile-time parameters) and the renamed methods listed below.
ArgMojo v0.3.0 is compatible with Mojo v0.26.1.
⭐️ New in v0.3.0
- Implement shell completion script generation for Bash, Zsh, and Fish, with a built-in
--completions <shell>flag that emits a ready-to-source script (PR #4). - Allow disabling the built-in flag (
disable_default_completions()), customising the trigger name (completions_name()), or exposing completions as a subcommand (completions_as_subcommand()) (PR #4). - Add Levenshtein-distance based "did you mean ...?" suggestions for misspelled long options and subcommand names (PR #3).
- Implement
command_aliases()onCommandto register alternative names for subcommands. Aliases are shown in help, accepted during dispatch, and included in shell completions and typo suggestions (PR #5). - Add
.clamp()modifier for.range[min, max]()-- out-of-range values are adjusted to the nearest boundary with a warning instead of a hard error (PR #6). - Move count-ceiling enforcement (
.max[N]()) and range validation into the_validate()phase so all post-parse checks run in a single pass (PR #6). - Parameterise
.max[ceiling](),.range[min, max](), and.number_of_values[N]()as compile-time parameters, enabling build-time validation of invalid values (PR #8). - Add
Command.hidden()builder method. Hidden subcommands are excluded from help output, shell completions, "Available commands" error messages, and typo suggestions, while remaining dispatchable by exact name or alias (PR #9). - Honour the
NO_COLORenvironment variable (any value, including empty). When set, all ANSI colour output from help, warning, and error messages is suppressed, following the no-color.org standard (PR #9). - Add
Command.implies(trigger, implied)to automatically set one argument when another is present. Supports chained implications (A -> B -> C) with cycle detection at registration time. Works with flags and count arguments, and integrates with existing constraints (required_if,mutually_exclusive) (PR #10).
🦋 Changed in v0.3.0
parse_args()renamed toparse_arguments()(PR #5).help_on_no_args()renamed tohelp_on_no_arguments()(PR #5)..nargs()renamed to.number_of_values()andnargs_countfield renamed to_number_of_values(PR #5).- Several
ArgumentandParseResultattributes are now underscore-prefixed (private). Public builder methods are unchanged (PR #7). - Decompose
parse_args()into four sub-methods:_parse_long_option(),_parse_short_single(),_parse_short_merged(),_dispatch_subcommand()(PR #2). - Decompose
_generate_help()into five sub-methods:_help_usage_line(),_help_positionals_section(),_help_options_section(),_help_commands_section(),_help_tips_section()(PR #2). - Extract ANSI colour constants and utility functions into a new internal module
utils.mojo(PR #2). - Rename example files to avoid confusion:
git.mojo->mgit.mojo,grep.mojo->mgrep.mojo. - Add
examples/demo.mojo-- a comprehensive showcase of all ArgMojo features in a single CLI (PR #7).
📚 Documentation and testing in v0.3.0
- Add
tests/test_typo_suggestions.mojocovering Levenshtein-based suggestions (PR #3). - Add
tests/test_completion.mojowith comprehensive tests for Bash, Zsh, and Fish script generation (PR #4). - Add
tests/test_implies.mojocovering basic, chained, and multi-target implications, cycle detection, and constraint integration (PR #10). - Add builder method compatibility section to the user manual with an ASCII tree, Mermaid diagram, and compatibility table (PR #11).
- Set up GitHub Actions workflow for automatic wiki synchronisation from
docs/user_manual.md. - Update user manual to cover all new features.
What's Changed
- [core] Refactor: decompose large methods & extract utils.mojo by @forfudan in #2
- [core] Add typo-suggestion support via Levenshtein distance by @forfudan in #3
- [core] Add shell completion support + built-in completion by @forfudan in #4
- [core] Implement command aliases by @forfudan in #5
- [core] Add range clamping + conduct ceiling and clamping at validation phase + warning message by @forfudan in #6
- [core][demo] Provide a new demo + make some attributes private + rename some attributes and methods by @forfudan in #7
- [core] Parameterize some arguments to enable compile-time check by @forfudan in #8
- [core] Add hidden subcommands + NO-COLOR option by @forfudan in #9
- [core] Add mutual implication functionality by @forfudan in #10
- [doc] Add compatibility charts in user manual showing relationship between builder methods by @forfudan in #11
Full Changelog: v0.2.0...v0.3.0
ArgMojo v0.2.0
20260228 (v0.2.0)
ArgMojo v0.2.0 is a major release that transforms the library from a single-command parser into a full subcommand-capable CLI framework. It introduces hierarchical subcommands with automatic dispatch, persistent (global) flags with bidirectional sync, negative number passthrough, colored error messages, custom tips, and significant help/UX improvements. The public API is also refined: Arg → Argument (old name kept as alias). Two complete example CLIs (grep and git) replace the previous demo.
ArgMojo v0.2.0 is compatible with Mojo v0.26.1.
⭐️ New in v0.2.0
Subcommands:
- Implement full subcommand support with
add_subcommand()API, hierarchical dispatch, and nested subcommands (e.g.,git remote add). - Auto-register a
helpsubcommand so thatapp help <command>works out of the box; opt out withdisable_help_subcommand(). - Add
allow_positional_with_subcommands()guard — prevents accidental mixing of positional args and subcommands on the sameCommand, following the cobra/clap convention. Requires explicit opt-in. - Add
subcommandandsubcommand_resultfields onParseResultwithhas_subcommand_result()/get_subcommand_result()accessors.
Persistent flags:
- Add
.persistent()builder method onArgumentto mark a flag as global. - Persistent args are automatically injected into child commands and support bidirectional sync: flags set before the subcommand push down to the child, and flags set after the subcommand bubble up to the root.
- Detect conflicting long/short names between parent persistent args and child local args at registration time (
add_subcommand()raises an error).
Negative number passthrough:
- Recognize negative numeric tokens like
-3.14or-42as positional values instead of unknown short options. - Add
allow_negative_numbers()opt-in onCommandfor explicit control.
Custom tips:
- Add
add_tip()API onCommandto attach user-defined tips that render as a dedicated section at the bottom of help output.
Error handling:
- Colored error and warning messages — ANSI-styled stderr output for all parse errors.
- Unknown subcommand error now lists all available commands.
- Errors inside child parse are prefixed with the full command path (e.g.,
git remote add: ...).
🦋 Changed in v0.2.0
API rename:
- Rename
Argstruct toArgument. The old name is kept as alias for convenience. - Rename source files:
arg.mojo→argument.mojo,result.mojo→parse_result.mojo.
Help & UX improvements:
- Add a "Commands" section to help output listing available subcommands with aligned descriptions.
- Show
<COMMAND>placeholder in the usage line for commands that have subcommands. - Display persistent flags under a "Global Options" heading in child help.
- Show the full command path in child help and error messages (e.g.,
Usage: git remote add [OPTIONS] NAME URL).
Internal refactoring:
- Extract
_apply_defaults()and_validate()into private helper methods onCommand, enabling clean reuse for both root and child parsing.
📚 Documentation and testing in v0.2.0
- Add two complete example CLIs:
examples/grep.mojo(single-command, demonstrating all argument features) andexamples/git.mojo(subcommand-based, with nested subcommands and persistent flags). - Add
tests/test_subcommands.mojocovering data model, dispatch, help subcommand, persistent flags, allow-positional guard, and error handling. - Add
tests/test_negative_numbers.mojo. - Add
tests/test_persistent.mojo. - Update user manual (
docs/user_manual.md) to cover all new features.
ArgMojo v0.1.0
20260224 (v0.1.0)
ArgMojo v0.1.0 is the initial release of a command-line argument parser library for Mojo. It provides a builder-pattern API for defining and parsing CLI arguments, inspired by Python's argparse, Rust's clap, and Go's cobra.
ArgMojo v0.1.0 is compatible with Mojo v0.26.1.
⭐️ Features
- Long and short options:
--output file.txt,--output=file.txt,-o file.txt,-ofile.txt - Boolean flags: options that take no value (
--verbose,-v) - Positional arguments: matched by position, with count validation
- Default values: fallback when an argument is not provided
- Required arguments: validation that mandatory arguments are present
- Auto-generated help:
--help/-hwith formatted usage and descriptions - Version display:
--version/-V --stop marker: everything after--is treated as positional- Short flag merging:
-abcexpands to-a -b -c - Attached short values:
-ofile.txtis equivalent to-o file.txt - Choices validation: restrict values to an allowed set (e.g.,
json,csv,table) - Metavar: custom display name for values in help text (e.g.,
--format <FMT>) - Hidden arguments: exclude internal arguments from
--helpoutput - Count flags:
-vvv→get_count("verbose") == 3 - Negatable flags:
--color/--no-colorpaired flags with.negatable() - Mutually exclusive groups: prevent conflicting flags (e.g.,
--jsonvs--yaml) - Required-together groups: enforce related flags are provided together (e.g.,
--username+--password) - Long option prefix matching: allow abbreviated options (e.g.,
--verb→--verbose), with ambiguity detection print_summary()method: human-readable summary of all parsed arguments
📚 Documentation and testing
- Comprehensive user manual covering every feature
- Unit tests covering all features and edge cases
- Demo CLI application (demo.mojo) showcasing all features
- CI workflow for Ubuntu and macOS