Summary
Add first-class support for CLIs that change behavior based on how they are invoked (their argv[0] / symlink name). This is the "multicall" pattern, used by BusyBox, bzip2/bunzip2, gunzip, vim/vi/view, exiftool, and others. Click-extra already has sophisticated prog_name handling; extending it to let commands inspect and dispatch on the invocation name is a natural next step.
Motivation
Programs that read their own invocation name to select behavior are a long-standing Unix tradition:
- BusyBox: a single binary, hundreds of applets selected by
argv[0] via symlinks.
- bzip2 / bunzip2 / bzcat: same binary, mode chosen by name. Same pattern for gzip/gunzip/zcat, xz/unxz/lzcat.
- vim / vi / view / vimdiff: different default options depending on the invoked name.
- exiftool: supports parenthesized options embedded in the filename, e.g.
exiftool(-k) is equivalent to exiftool -k.
- pigz / unpigz, pkg-config variants: hardlinks to the same inode, different names.
A recent proposal pushes the idea further, suggesting that entire configurations could be encoded in the filename (e.g. train---resnet50---lr0.001---batch32.exe). The Hacker News discussion surfaced practical observations:
- Filenames persist on the filesystem, unlike shell history or ephemeral flags. A renamed binary is a self-documenting, shareable, zero-setup invocation.
- The pattern works best for a small, fixed set of named personalities (bunzip2, vi vs. vim) rather than arbitrary parameter encoding, which runs into filename length limits (255 chars), special-character restrictions, discoverability problems, and version-control friction.
- Symlinks eliminate the "duplicate binary" concern.
- The traditional multicall pattern (dispatch to a subcommand) is distinct from the "filename-as-config" pattern (parse arbitrary key-value pairs from the name). Both are worth supporting, but the former is far more common and less controversial.
Proposed scope
1. Multicall group (primary feature)
A new decorator or Group subclass that inspects sys.argv[0] (resolved through symlinks) and dispatches to a subcommand matching the invocation name:
from click_extra import multicall_group, command
@multicall_group()
def toolkit():
"""A BusyBox-style multicall binary."""
@toolkit.command()
def compress():
"""Compress files."""
...
@toolkit.command()
def decompress():
"""Decompress files."""
...
When invoked as toolkit, it works as a normal group with subcommands. When invoked via a symlink named compress, it skips the group and runs the compress command directly.
Key behaviors:
- Symlink resolution: detect whether
argv[0] basename matches a registered subcommand. If so, dispatch directly.
- Fallback: if the name doesn't match any subcommand, fall through to normal group behavior (show help, require a subcommand argument).
--help still works: compress --help shows help for the compress subcommand, not the group.
- Listing personalities:
toolkit --list-personalities (or similar) enumerates all available symlink names.
- Installer helper: optionally generate symlinks or shell aliases for all subcommands in a given directory.
2. Invocation-name hook (secondary feature)
Expose the raw invocation name to any command via the Click context, so authors can implement custom dispatch logic:
from click_extra import command, pass_context
@command()
@pass_context
def my_tool(ctx):
invoked_as = ctx.meta["invocation_name"] # or a dedicated context attribute
if invoked_as == "bunzip2":
# decompress mode
...
This is lower-level and lets users build their own patterns without the full multicall group machinery.
3. Name-embedded options (optional, exploratory)
Support for exiftool-style parenthesized flags in the binary name, e.g. mytool(-v)(--format=json). This is a niche feature and could be deferred, but it fits naturally into the argv[0]-inspection infrastructure.
Design considerations
prog_name interaction: click-extra already overrides prog_name in ExtraCommand.main(). The multicall logic should integrate with this, not fight it. When dispatching via symlink, prog_name should reflect the symlink name.
- Windows: symlinks require elevated privileges on older Windows. Hardlinks or
.cmd wrappers are the practical alternative. The feature should degrade gracefully.
- Entry points: Python packaging entry points (via
[project.scripts]) already create named wrappers. The multicall pattern is complementary: one entry point for the group, symlinks for the personalities.
- Testing:
ExtraCliRunner should support simulating invocation under a different argv[0] without actual symlinks on disk.
Prior art in other frameworks
- Click itself has no multicall support.
CommandCollection merges multiple groups but doesn't dispatch on argv[0].
- argparse has no built-in support.
- Rust's clap: no direct multicall, but the pattern is commonly hand-rolled.
- Go's cobra: no built-in multicall.
This would be a differentiating feature for click-extra.
Non-goals
- Arbitrary key-value parsing from filenames (the
train---resnet50---lr0.001 pattern). The HN discussion consensus is that this is fragile, hard to discover, breaks tooling expectations, and is better served by config files or wrapper scripts.
- Self-modifying executables or filename-embedded encrypted configs.
- Replacing standard
--flags for normal CLI usage. This feature is about dispatch, not a wholesale alternative to argument parsing.
References
Summary
Add first-class support for CLIs that change behavior based on how they are invoked (their
argv[0]/ symlink name). This is the "multicall" pattern, used by BusyBox, bzip2/bunzip2, gunzip, vim/vi/view, exiftool, and others. Click-extra already has sophisticatedprog_namehandling; extending it to let commands inspect and dispatch on the invocation name is a natural next step.Motivation
Programs that read their own invocation name to select behavior are a long-standing Unix tradition:
argv[0]via symlinks.exiftool(-k)is equivalent toexiftool -k.A recent proposal pushes the idea further, suggesting that entire configurations could be encoded in the filename (e.g.
train---resnet50---lr0.001---batch32.exe). The Hacker News discussion surfaced practical observations:Proposed scope
1. Multicall group (primary feature)
A new decorator or
Groupsubclass that inspectssys.argv[0](resolved through symlinks) and dispatches to a subcommand matching the invocation name:When invoked as
toolkit, it works as a normal group with subcommands. When invoked via a symlink namedcompress, it skips the group and runs thecompresscommand directly.Key behaviors:
argv[0]basename matches a registered subcommand. If so, dispatch directly.--helpstill works:compress --helpshows help for the compress subcommand, not the group.toolkit --list-personalities(or similar) enumerates all available symlink names.2. Invocation-name hook (secondary feature)
Expose the raw invocation name to any command via the Click context, so authors can implement custom dispatch logic:
This is lower-level and lets users build their own patterns without the full multicall group machinery.
3. Name-embedded options (optional, exploratory)
Support for exiftool-style parenthesized flags in the binary name, e.g.
mytool(-v)(--format=json). This is a niche feature and could be deferred, but it fits naturally into theargv[0]-inspection infrastructure.Design considerations
prog_nameinteraction: click-extra already overridesprog_nameinExtraCommand.main(). The multicall logic should integrate with this, not fight it. When dispatching via symlink,prog_nameshould reflect the symlink name..cmdwrappers are the practical alternative. The feature should degrade gracefully.[project.scripts]) already create named wrappers. The multicall pattern is complementary: one entry point for the group, symlinks for the personalities.ExtraCliRunnershould support simulating invocation under a differentargv[0]without actual symlinks on disk.Prior art in other frameworks
CommandCollectionmerges multiple groups but doesn't dispatch onargv[0].This would be a differentiating feature for click-extra.
Non-goals
train---resnet50---lr0.001pattern). The HN discussion consensus is that this is fragile, hard to discover, breaks tooling expectations, and is better served by config files or wrapper scripts.--flagsfor normal CLI usage. This feature is about dispatch, not a wholesale alternative to argument parsing.References