{"id":19927658,"url":"https://github.com/mgielda/ultimate-cli","last_synced_at":"2026-03-03T18:32:25.698Z","repository":{"id":150635840,"uuid":"453732582","full_name":"mgielda/ultimate-cli","owner":"mgielda","description":null,"archived":false,"fork":false,"pushed_at":"2022-03-02T16:11:37.000Z","size":28,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-24T01:19:58.845Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mgielda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-01-30T16:22:57.000Z","updated_at":"2022-02-01T22:47:53.000Z","dependencies_parsed_at":"2023-05-26T20:30:14.609Z","dependency_job_id":null,"html_url":"https://github.com/mgielda/ultimate-cli","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mgielda/ultimate-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgielda%2Fultimate-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgielda%2Fultimate-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgielda%2Fultimate-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgielda%2Fultimate-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mgielda","download_url":"https://codeload.github.com/mgielda/ultimate-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgielda%2Fultimate-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30054601,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T18:21:05.932Z","status":"ssl_error","status_checked_at":"2026-03-03T18:20:59.341Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T22:34:21.064Z","updated_at":"2026-03-03T18:32:25.668Z","avatar_url":"https://github.com/mgielda.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ultimate CLI\n\nThis repository will serve the purpose of documenting my research of the \"Ultimate CLI\" problem, i.e. how to create superb CLI experiences with very little thinking and code, for various programs, utilities and frameworks that I invariably end up building.\n\nRequirements:\n\n1. Minimum code. Every line counts! While creating your new framework you don't want to be thinking about HOW to give it a nice CLI and googling for solutions, but just dropping a few lines and that is it.\n2. Maximum flexibility to do whatever you need in terms of the arguments etc. in the CLI, so that the framework does not fall apart if you go beyond the examples they provide.\n3. Simple things should be simple. Bash is simple. Everyone remembers how to do the basic stuff in bash. If basic stuff is hard to do in framework X, people fall back to bash.\n4. Good integration with bash, if we can, since bash is great for grep etc.\n5. (optional) Enable code reuse with tuttest.\n\n## Typer + click-repl (current solution)\n\n[Typer](https://github.com/tiangolo/typer) is a somewhat involved CLI generator based on click, but it just works.\nI suspect something simpler could be built based on click but that would require a non-negligible amount of work.\n\nIt does not support interactive shells, but I found a way to workaround that using [click-repl](https://github.com/click-contrib/click-repl).\nSee also [here](https://github.com/tiangolo/typer/issues/185).\n\n### Reference code\n\n#### Command\n\n```python\nimport typer\napp = typer.Typer(help=\"A test app\", add_completion=False)\n\n@app.command()\ndef test(test_arg: str):\n    \"Test command\"\n    print(f\"Test, the argument is {test_arg}.\")\n```\n\nThen:\n\n```python\nif __name__ == \"__main__\":\n    app()\n```\n\n#### Initialization + global arguments \n\n```python\n@app.callback(hidden=True)\ndef some_init_function(global_arg: str=\"global_arg\"):\n    print(\"Initializing stuff, global_arg = {global_arg}\")\n```\n\n#### Interactive shell (+ command with alias)\n\n```python\nimport click\nfrom click_repl import repl\n\n@app.command(\"i\")\ndef interactive():\n    \"Run in interactive mode\"\n    click.echo(\"Running in interactive mode. This supports tab completion.\")\n    click.echo(\"Use ':help' for help info, or ':quit' to quit.\")\n    repl(click.get_current_context())\n```\n\n### Disadvantages\n\n* when you don't provide the subcommand, the default help does not list the subcommands (is this fixable? it must be!)\n* the decorated function names are not available as commands, i.e. aliases supersede the main subcommand function name (perhaps this is fixable too; a workaround is to register the function twice)\n* you sometimes will need to use Typer.argument instead of normal Python objects for completion to work in interactive shell\n* interactive shell shows the full help (with program name etc.) every time you make a mistake (fixable?)\n* help/quitting is weird in the interactive shell (fixable? but would need to dig into click-repl)\n\nOnce some more fixes are found, we will probably need a new package to hide the complexity.\n\n### Example usage (tablist)\n\nFor some example usage, see a rudimentary utility I created, [tablist](https://github.com/mgielda/tablist/blob/master/tablist.py).\nIt actually does something in under 100 lines of code.\n\n## plac\n\n[Plac](https://plac.readthedocs.io/en/latest/) is as awesome as it is unpopular and arcane.\n\nSee:\n\n* [batch scripts](https://plac.readthedocs.io/en/latest/#plac-batch-scripts)\n* [runner](https://plac.readthedocs.io/en/latest/#the-plac-runner)\n* [automated tests](https://plac.readthedocs.io/en/latest/#plac-easy-tests)\n* [server](https://plac.readthedocs.io/en/latest/#the-plac-server)\n\nTried to use it several times, but always fall into the same pitfall:\n\n* convoluted documentation (it could be improved by separating it into multiple chapters etc. but it's a non-trivial amount of work)\n* so-so support for [subcommands](https://plac.readthedocs.io/en/latest/#implementing-subcommands) -- you need to either self-import a module or create a separate class; this is too complex, and subcommands are my main requirement\n* some arbitrary limitations just because (which may hit you for advanced use cases, but you can work around them with `argparse` which is used underneath; this however sacrifices the simplicity)\n* arcane internals (which is understandable, since it has so many cool features)\n\nIt is a great inspiration, but for now remains well, inspiration.\n\n## argh\n\n[Argh](https://argh.readthedocs.io/en/latest/tutorial.html) is nice. I used to use argh. I think where it failed me was stuff like general options not tied to any subcommand etc. Perhaps of course it was me not knowing how to use argh well enough.\nAlso no interactive shells and fancy stuff to speak of ;-)\nAnyway it remains a very decent choice and [is easy to use](https://argh.readthedocs.io/en/latest/#examples)\n\nHowever, it is [somewhat unmaintained](https://github.com/neithere/argh/issues/124#issuecomment-820376819) (last commit in 2016) and licensed on LGPL.\n\nTrivia: I learned about [plac](#plac) from [argh's similar projects page](https://argh.readthedocs.io/en/latest/similar.html).\n\n## fire\n\nWhile [fire](https://github.com/google/python-fire) is a cool project, it lacks some capabilities for the use cases that I typically hit (at least last I checked) and does not offer any of interactive shell and other advanced features.\n\n## ipython / Jupyter\n\nWith its seamless bash integration, magics, vast ecosystem etc, Jupyter seems like an excellent project to base upon here.\nHowever, it seems nobody ever seriously thought of using Jupyter for CLI-driven task automation (or I am just looking in the wrong places).\n\nAnd `ipython` seems to crash for me whenever used with anything remotely similar to plac, Typer or argparse for that matter (although workarounds exist).\n\nSo perhaps there is promise here, but failed to find it so far, will continue looking.\n\n## xonsh and similar solutions\n\nI don't know -- I am not really looking for a new shell, exactly, more a simple task automation solution.\nMay revisit in the future.\n\n[Xonsh](https://xon.sh/) seems to run e.g. `tablist` properly, **including** the interactive shell, but to get the xonsh magic, you will need to explicitly use the `.xsh` extension. So this is promising!\n\nQuestion is, if super-seamless bash integration is really the main requirement to warrant including xonsh as a dependency.\n\n## bpython\n\n[bpython](https://github.com/bpython/bpython) seems like a cool idea at first glance.\nWith the syntax highlighting and code completion, argument suggestions etc. it looks really promising, but there seems to be no way to use bpython ON a program to get a nice CLI, with only the commands the program itself offers.\n\nIf someone knows of a utility like that, it would indeed be pretty cool.\nAlso, the considerations about `xonsh` apply.\n\n## JavaScript solutions\n\nWhile there are many JS task automation solutions (perhaps too many), generally I feel somewhat reluctant to a) use JavaScript (but I can be convinced) and b) pull in Node as a dependency everywhere.\n\nFor bash integration, there is the excellent [zx](https://github.com/google/zx) at least.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmgielda%2Fultimate-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmgielda%2Fultimate-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmgielda%2Fultimate-cli/lists"}