{"id":13667392,"url":"https://github.com/daniele77/cli","last_synced_at":"2025-05-14T15:06:58.721Z","repository":{"id":39861866,"uuid":"64490184","full_name":"daniele77/cli","owner":"daniele77","description":"A library for interactive command line interfaces in modern C++","archived":false,"fork":false,"pushed_at":"2025-04-12T16:12:55.000Z","size":813,"stargazers_count":1273,"open_issues_count":39,"forks_count":148,"subscribers_count":32,"default_branch":"master","last_synced_at":"2025-04-12T20:43:41.006Z","etag":null,"topics":["cli","header-only","interactive","parser","ui-library"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daniele77.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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,"zenodo":null},"funding":{"github":"daniele77","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2016-07-29T15:17:43.000Z","updated_at":"2025-04-12T16:12:58.000Z","dependencies_parsed_at":"2024-01-31T09:02:19.267Z","dependency_job_id":"26037246-41e6-4ad7-a39b-050b364426f2","html_url":"https://github.com/daniele77/cli","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daniele77%2Fcli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daniele77%2Fcli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daniele77%2Fcli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daniele77%2Fcli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daniele77","download_url":"https://codeload.github.com/daniele77/cli/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254169335,"owners_count":22026211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["cli","header-only","interactive","parser","ui-library"],"created_at":"2024-08-02T07:00:36.184Z","updated_at":"2025-05-14T15:06:58.672Z","avatar_url":"https://github.com/daniele77.png","language":"C++","readme":"[![CI of Cli](https://github.com/daniele77/cli/actions/workflows/ci.yml/badge.svg)](https://github.com/daniele77/cli/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/daniele77/cli/actions/workflows/codeql.yml/badge.svg)](https://github.com/daniele77/cli/actions/workflows/codeql.yml)\n\n[:heart: Sponsor](https://github.com/sponsors/daniele77)\n\n# cli\n\nA cross-platform header only C++14 library for interactive command line interfaces (Cisco style)\n\n![demo_local_session](https://user-images.githubusercontent.com/5451767/51046611-d1dadc00-15c6-11e9-8a0d-2c66efc83290.gif)\n\n![demo_telnet_session](https://user-images.githubusercontent.com/5451767/51046612-d1dadc00-15c6-11e9-83c2-beadb3593348.gif)\n\n## Features\n\n* Header only\n* Cross-platform (linux and windows)\n* Menus and submenus\n* Remote sessions (telnet)\n* Persistent history (navigation with arrow keys)\n* Autocompletion (with TAB key)\n* Async interface\n* Colors\n\n## How to get CLI library\n\n* From [GitHub](https://github.com/daniele77/cli/releases)\n* Using [Vcpkg](https://github.com/Microsoft/vcpkg)\n* Using [Conan](https://conan.io/center/recipes/cli)\n\n## Dependencies\n\nThe library has no dependencies if you don't need remote sessions.\n\nThe library depends on asio (either the standalone version or the boost version \u003e= 1.66)\n*only* to provide telnet server (i.e., remote sessions).\n\n## Installation\n\nThe library is header-only: it consists entirely of header files\ncontaining templates and inline functions, and requires no separately-compiled\nlibrary binaries or special treatment when linking.\n\nExtract the archive wherever you want.\n\nNow you must only remember to specify the cli (and optionally asio or boost) paths when\ncompiling your source code.\n\nIf you fancy it, a Cmake script is provided. To install you can use:\n\n    mkdir build \u0026\u0026 cd build\n    cmake ..\n    sudo make install\n\nor, if you want to specify the installation path:\n\n    mkdir build \u0026\u0026 cd build\n    cmake .. -DCMAKE_INSTALL_PREFIX:PATH=\u003ccli_install_location\u003e\n    make install\n    \nAlternatively, you can use CMake's `FetchContent` module to include CLI library in your project directly.\nAdd something like this in your `CMakeLists.txt` file:\n\n    include(FetchContent)\n    FetchContent_Declare(\n      cli\n      GIT_REPOSITORY https://github.com/daniele77/cli.git\n      GIT_TAG v2.1.0\n    )\n    FetchContent_MakeAvailable(cli)\n    \n    add_executable(main-project)\n    target_link_libraries(main-project PRIVATE cli::cli)\n\n\n## Compilation of the examples\n\nYou can find some examples in the directory \"examples\".\nEach .cpp file corresponds to an executable.\nEach example can be compiled by including cli\n(and optionally asio/boost header files) \nand linking pthread on linux (and optionally boost system).\n\nTo compile the examples using cmake, use:\n\n    mkdir build \u0026\u0026 cd build\n\n    # compile only the examples that do not require boost/asio libraries\n    cmake .. -DCLI_BuildExamples=ON\n\n    # compile the examples by using boost asio libraries\n    cmake .. -DCLI_BuildExamples=ON -DCLI_UseBoostAsio=ON\n    # or: cmake .. -DCLI_BuildExamples=ON -DCLI_UseBoostAsio=ON -DBOOST_ROOT=\u003cboost_path\u003e\n\n    # compile the examples by using standalone asio library\n    cmake .. -DCLI_BuildExamples=ON -DCLI_UseStandaloneAsio=ON\n    # or: cmake .. -DCLI_BuildExamples=ON -DCLI_UseStandaloneAsio=ON -DASIO_INCLUDEDIR=\u003casio_path\u003e\n\n    cmake --build .\n\nIn the same directory you can also find:\n\n* GNU make files (Makefile.noasio, Makefile.boostasio, Makefile.standaloneasio)\n* Windows nmake files (makefile.noasio.win, makefile.boostasio.win, makefile.standaloneasio.win)\n* a Visual Studio solution\n\nIf needed, you can specify asio library path in the following ways:\n\n### GNU Make\n\nfor boost:\n\n    make CXXFLAGS=\"-isystem \u003cboost_include\u003e\" LDFLAGS=\"-L\u003cboost_lib\u003e\"\n\nexample:\n\n    make CXXFLAGS=\"-isystem /opt/boost_1_66_0/install/x86/include\" LDFLAGS=\"-L/opt/boost_1_66_0/install/x86/lib\"\n\nfor standalone asio:\n\n    make CXXFLAGS=\"-isystem \u003casio_include\u003e\"\n\nexample:\n\n    make CXXFLAGS=\"-isystem /opt/asio-1.18.0/include\"\n\n(if you want to use clang instead of gcc, you can set the variable CXX=clang++)\n\n### Windows nmake\n\nOptionally set the environment variable ASIO or BOOST to provide the library path.\nThen, from a visual studio console, start `nmake` passing one of the `makefile.*.win` files.\n\nE.g., from a visual studio console, use one of the following commands:\n\n    # only compile examples that do not require asio\n    nmake /f makefile.noasio.win\n    # compile examples using boost asio\n    set BOOST=\u003cpath of boost libraries\u003e\n    nmake /f makefile.boostasio.win\n    # compile examples using standalone asio\n    set ASIO=\u003cpath of asio library\u003e\n    nmake /f makefile.standaloneasio.win\n\n### Visual Studio solution\n\nSet the environment variables BOOST and/or ASIO. Then, open the file\n`cli/examples/examples.sln`\n\n## Compilation of the Doxygen documentation\n\nIf you have doxygen installed on your system, you can get the html documentation\nof the library in this way:\n\n    cd doc/doxy\n    doxygen Doxyfile\n\n## CLI usage\n\nAt the start of your application, the CLI presents a prompt with the\nname you provided in the `Cli` constructor.\nThis indicates you're in the root menu.\n\n### Navigation\n\n- **Enter a submenu:** Type the submenu name to enter it.\n  The prompt will change to reflect the current submenu.\n- **Go back to parent menu:** Type the name of the parent menu or `..` to return.\n- **Navigate history:** Use up and down arrow keys to navigate through previously entered commands.\n- **Exit:** Type `exit` to terminate the CLI application.\n\n### Commands in any menu\n\n- `help`: Prints a list of available commands with descriptions.\n- `history`: Displays the list of previously entered commands.\n- `exit`: Terminates the CLI application.\n- **Command execution:**\n    - **Current menu:** Enter the name of a command available in the current menu to execute it.\n    - **Submenu (full path):** Specify the complete path (separated by spaces) to a command within a submenu to execute it.\n    - **History execution:** Use `!` followed by the identifier of a history item to execute that command again.\n\n### Autocompletion\n\nUse the Tab key to get suggestions for completing command or menu names as you type.\n\n### Screen Clearing\n\nPress `Ctrl-L` to clear the screen at any time.\n\n### Parameter parsing\n\nThe CLI interpreter can handle sentences using single quotes (`'`) and double quotes (`\"`).\nAny character (including spaces) enclosed within quotes is considered a single parameter for a command.\nYou can use quotes within parameters by escaping them with a backslash (`\\`).\n\n**Examples:**\n\n    cli\u003e echo \"this is a single parameter\"\n    this is a single parameter\n    cli\u003e echo 'this too is a single parameter'\n    this too is a single parameter\n    cli\u003e echo \"you can use 'single quotes' inside double quoted parameters\"\n    you can use 'single quotes' inside double quoted parameters\n    cli\u003e echo 'you can use \"double quotes\" inside single quoted parameters'\n    you can use \"double quotes\" inside single quoted parameters\n    cli\u003e echo \"you can escape \\\"quotes\\\" inside a parameter\"               \n    you can escape \"quotes\" inside a parameter\n    cli\u003e echo 'you can escape \\'single quotes\\' inside a parameter'\n    you can escape 'single quotes' inside a parameter\n    cli\u003e echo \"you can also show backslash \\\\ ... \"                \n    you can also show backslash \\ ... \n\n## Async programming and Schedulers\n\n`cli` is an asynchronous library, and the handlers of commands are executed\nby a scheduler, in a thread provided by the user (possibly the main thread),\nthis allows you to develop a single thread application\nwithout need to worry about synchronization.\n\nSo, your application must have a scheduler and pass it to `CliLocalTerminalSession`. \n\nThe library provides three schedulers:\n\n- `LoopScheduler`\n- `BoostAsioScheduler`\n- `StandaloneAsioScheduler`\n\n`LoopScheduler` is the simplest: it does not depend on other libraries\nand should be your first choice if you don't need remote sessions.\n\n`BoostAsioScheduler` and `StandaloneAsioScheduler` are wrappers around\nasio `io_context` objects.\nYou should use one of them if you need a `BoostAsioCliTelnetServer` or a `StandaloneAsioCliTelnetServer`\nbecause they internally use `boost::asio` and `asio`.\n\nYou should use one of them also if your application uses `asio` in some way.\n\nAfter setting up your application, you must call `Scheduler::Run()`\nto enter the scheduler loop. Each command handler of the library\nwill execute in the thread that called `Scheduler::Run()`.\n\nYou can exit the scheduler loop by calling `Scheduler::Stop()`\n(e.g., as an action associated to the \"exit\" command).\n\nYou can submit work to be executed by the scheduler invoking the method\n`Scheduler::Post(const std::function\u003cvoid()\u003e\u0026 f)`.\nSchedulers are thread safe, so that you can post function object\nfrom any thread, to be executed in the scheduler thread.\n\nThis is an example of use of `LoopScheduler`:\n\n```C++\n...\nLoopScheduler scheduler;\nCliLocalTerminalSession localSession(cli, scheduler);\n...\n// in another thread you can do:\nscheduler.Post([](){ cout \u003c\u003c \"This will be executed in the scheduler thread\\n\"; });\n...\n// start the scheduler main loop\n// it will exit from this method only when scheduler.Stop() is called\n// each cli callback will be executed in this thread\nscheduler.Run();\n...\n```\n\nThis is an example of use of `BoostAsioScheduler`\n\n```C++\n...\nBoostAsioScheduler scheduler;\nCliLocalTerminalSession localSession(cli, scheduler);\nBoostAsioCliTelnetServer server(cli, scheduler, 5000);\n...\nscheduler.Run();\n...\n```\n\nFinally, this is an example of use of `BoostAsioScheduler`\nwhen your application already uses `boost::asio` and has\na `boost::asio::io_context` object (the case of standalone asio is similar). \n\n```C++\n...\n// somewhere else in your application\nboost::asio::io_context ioc;\n...\n// cli setup\nBoostAsioScheduler scheduler(ioc);\nCliLocalTerminalSession localSession(cli, scheduler);\nBoostAsioCliTelnetServer server(cli, scheduler, 5000);\n...\n// somewhere else in your application\nioc.run();\n...\n```\n\n## Adding menus and commands\n\nYou must provide at least a root menu for your cli:\n\n```C++\n// create a menu (this is the root menu of our cli)\nauto rootMenu = make_unique\u003cMenu\u003e(\"myprompt\");\n\n... // fills rootMenu with commands\n\n// create the cli with the root menu\nCli cli(std::move(rootMenu));\n\n```\n\nYou can add menus to existing menus, to get a hierarchy:\n\n```C++\nauto rootMenu = make_unique\u003cMenu\u003e(\"myprompt\");\nauto menuA = make_unique\u003cMenu\u003e(\"a_prompt\");\nauto menuAA = make_unique\u003cMenu\u003e(\"aa_prompt\");\nauto menuAB = make_unique\u003cMenu\u003e(\"ab_prompt\");\nauto menuAC = make_unique\u003cMenu\u003e(\"ac_prompt\");\nauto menuACA = make_unique\u003cMenu\u003e(\"aca_prompt\");\nauto menuB = make_unique\u003cMenu\u003e(\"b_prompt\");\nauto menuBA = make_unique\u003cMenu\u003e(\"ba_prompt\");\nauto menuBB = make_unique\u003cMenu\u003e(\"bb_prompt\");\n\nmenuAC-\u003eInsert( std::move(menuACA) );\nmenuB-\u003eInsert( std::move(menuBA) );\nmenuB-\u003eInsert( std::move(menuBB) );\nmenuA-\u003eInsert( std::move(menuAA) );\nmenuA-\u003eInsert( std::move(menuAB) );\nmenuA-\u003eInsert( std::move(menuAC) );\nrootMenu-\u003eInsert( std::move(menuA) );\nrootMenu-\u003eInsert( std::move(menuB) );\n```\n\nThis results in this tree:\n\n```\nmyprompt\n    |\n    +--- a_prompt\n    |        |\n    |        +--- aa_prompt\n    |        |\n    |        +--- ab_prompt\n    |        |\n    |        +--- ac_prompt\n    |                |\n    |                +--- aca_prompt\n    |\n    +--- b_prompt\n             |\n             +--- ba_prompt\n             |\n             +--- bb_prompt\n\n```\n\nFinally, you can add commands to menus, using the `Menu::Insert` method.\nThe library supports adding commands handler via:\n- free functions\n- `std::function`\n- lambdas\n\n```C++\n\nstatic void foo(std::ostream\u0026 out, int x) { out \u003c\u003c x \u003c\u003c std::endl; }\n\nstd::function\u003cvoid(std::ostream\u0026 out, int x)\u003e fun(foo);\n\n...\n\nmyMenu-\u003eInsert(\"free_function\", foo);\n\nmyMenu-\u003eInsert(\"std_function\", fun);\n\nmyMenu-\u003eInsert(\"lambda\", [](std::ostream\u0026 out, int x){ out \u003c\u003c x \u003c\u003c std::endl; } );\n\n```\n\nThere is no limit to the number of parameters that a command handler can take.\nThey can be basic types and `std::string`s\n\n```C++\nmyMenu-\u003eInsert(\n    \"mycmd\", \n    [](std::ostream\u0026 out, int a, double b, const std::string\u0026 c, bool d, long e)\n    { \n        ...\n    } );\n\nmyMenu-\u003eInsert(\n    \"complex\", \n    [](std::ostream\u0026 out, std::complex c)\n    { \n        ...\n    } );\n```\n\nOr they can be custom types by overloading the `std::istream::operator\u003e\u003e`:\n\n```C++\nstruct Foo\n{\n    friend istream \u0026 operator \u003e\u003e (istream \u0026in, Foo\u0026 p);\n    int value;\n};\n\nistream \u0026 operator \u003e\u003e (istream\u0026 in, Foo\u0026 p)\n{\n    in \u003e\u003e p.value;\n    return in;\n}\n\nmyMenu-\u003eInsert(\n    \"foo\", \n    [](std::ostream\u0026 out, Foo f)\n    { \n        ...\n    } );\n\n```\n\nIf you need it, you can have a command handlers taking an arbitrary\nnumber of `std::string` parameters:\n\n```C++\nmyMenu-\u003eInsert(\n    \"mycmd\", \n    [](std::ostream\u0026 out, const std::vector\u003cstd::string\u003e\u0026 pars)\n    { \n        ...\n    } );\n\n```\n\nPlease note that in this case your command handler must take *only one*\nparameter of type `std::vector\u003cstd::string\u003e`.\n\n## Enter and exit actions\n\nYou can add an enter action and/or an exit action (for example to print a welcome/goodbye message\nevery time a user opens/closes a session, even a remote one):\n\n```C++\nCli cli(std::move(rootMenu));\ncli.EnterAction(\n    [\u0026enterActionDone](std::ostream\u0026 out) { out \u003c\u003c \"Welcome\\n\"; });\ncli.ExitAction(\n    [\u0026enterActionDone](std::ostream\u0026 out) { out \u003c\u003c \"Goodbye\\n\"; });\n```\n\n## Custom Handler for Unknown Commands\n\nYou can modify the default behavior of the library for cases where\nthe user enters an unknown command or its parameters do not match:\n\n```C++\nCli cli(std::move(rootMenu));\ncli.WrongCommandHandler(\n    [](std::ostream\u0026 out, const std::string\u0026 cmd)\n    {\n        out \u003c\u003c \"Unknown command or incorrect parameters: \"\n            \u003c\u003c cmd\n            \u003c\u003c \".\\n\";\n    }\n);\n```\n\n## Standard Exception Custom Handler\n\nYou can handle cases where an exception is thrown inside a command handler\nby registering a specific handler:\n\n```C++\nCli cli(std::move(rootMenu));\ncli.StdExceptionHandler(\n    [](std::ostream\u0026 out, const std::string\u0026 cmd, const std::exception\u0026 e)\n    {\n        out \u003c\u003c \"Exception caught in CLI handler: \"\n            \u003c\u003c e.what()\n            \u003c\u003c \" while handling command: \"\n            \u003c\u003c cmd\n            \u003c\u003c \".\\n\";\n    }\n);\n```\n\n## Unicode\n\n`cli` uses the input and output stream objects provided by the standard library (such as `std::cin` and `std::cout`) by default, so currently `cli` does not have effective support for unicode input and output.\n\nIf you want to handle unicode input and output, you need to provide custom i/o unicode aware stream objects derived from `std::istream` or `std::ostream`.\n\nFor example, you can use [boost::nowide](https://github.com/boostorg/nowide) as an alternative to implement UTF-8 aware programming in a out-of-box and cross-platform way:\n\n```c++\n#include \u003cboost/nowide/iostream.hpp\u003e // for boost::nowide::cin and boost::nowide::cout\n// other headers...\n\ncli::Cli app{/*init code*/};\n\n// FileSession session{app}; // default\n\n// now, all parameters is in a UTF-8 encoded std::string\n// pass boost::nowide::cin and boost::nowide::cout as parameters(FileSession requires std::istream and std::ostream)\nFileSession session{app, boost::nowide::cin, boost::nowide::cout};\n\n/*....*/\n\n// you can call this command funtion and get UTF-8 encoded input data (p), just use it.\n// boost::noide helps us avoid the trouble\n// caused by inconsistent default code page and encoding of the console under different platforms.\nvoid a_command_function(std::ostream\u0026 os, std::string const\u0026 p) {\n /* implements */\n}\n\n```\n\nOf course, you can also pass stream objects with other capabilities to achieve more customized input and output functions.\n\n## License\n\nDistributed under the Boost Software License, Version 1.0.\n(See accompanying file [LICENSE.txt](LICENSE.txt) or copy at\n\u003chttp://www.boost.org/LICENSE_1_0.txt\u003e)\n\n## Contact\n\nPlease report issues here:\n\u003chttp://github.com/daniele77/cli/issues\u003e\n\nand questions, feature requests, ideas, anything else here:\n\u003chttp://github.com/daniele77/cli/discussions\u003e\n\nYou can always contact me via twitter at @DPallastrelli\n\n---\n\n## Contributing (We Need Your Help!)\n\nAny feedback from users and stakeholders, even simple questions about\nhow things work or why they were done a certain way, carries value\nand can be used to improve the library.\n\nEven if you just have questions, asking them in [GitHub Discussions](http://github.com/daniele77/cli/discussions)\nprovides valuable information that can be used to improve the library - do not hesitate,\nno question is insignificant or unimportant!\n\nIf you or your company uses cli library, please consider becoming a sponsor to keep the project strong and dependable.\n","funding_links":["https://github.com/sponsors/daniele77"],"categories":["CLI","C++","Uncategorized","Libraries \u0026 Frameworks:"],"sub_categories":["Uncategorized","Other"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaniele77%2Fcli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaniele77%2Fcli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaniele77%2Fcli/lists"}