{"id":21316503,"url":"https://github.com/desvelao/lummander","last_synced_at":"2025-07-12T02:32:19.919Z","repository":{"id":63778961,"uuid":"169235138","full_name":"Desvelao/lummander","owner":"Desvelao","description":"Create a simple CLI with Lua.","archived":false,"fork":false,"pushed_at":"2020-02-01T11:40:10.000Z","size":72,"stargazers_count":39,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-07-12T09:31:08.061Z","etag":null,"topics":["cli","command-line","lua"],"latest_commit_sha":null,"homepage":"https://desvelao.github.io/lummander","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Desvelao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-05T12:09:59.000Z","updated_at":"2023-04-12T03:45:45.000Z","dependencies_parsed_at":"2022-11-25T21:01:20.425Z","dependency_job_id":null,"html_url":"https://github.com/Desvelao/lummander","commit_stats":null,"previous_names":[],"tags_count":2,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Desvelao%2Flummander","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Desvelao%2Flummander/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Desvelao%2Flummander/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Desvelao%2Flummander/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Desvelao","download_url":"https://codeload.github.com/Desvelao/lummander/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225786131,"owners_count":17523944,"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","command-line","lua"],"created_at":"2024-11-21T18:39:00.566Z","updated_at":"2024-11-21T18:39:01.488Z","avatar_url":"https://github.com/Desvelao.png","language":"Lua","readme":"# Lummander\nCreate a simple command-line interface (CLI) application with Lua. It's inspired by JavaScript's [commander](https://github.com/tj/commander.js/).\n\n# Index\n- \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e\n- \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\n- \u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\n- \u003ca href=\"#create-instance\"\u003eCreate an instance\u003c/a\u003e\n- \u003ca href=\"#add-commands\"\u003eAdd commands\u003c/a\u003e\n    - \u003ca href=\"#add-commands-using-lummander\"\u003ea) Add command using lummander instance methods\u003c/a\u003e\n        - \u003ca href=\"#command-schema\"\u003eCommand schema\u003c/a\u003e\n        - \u003ca href=\"#command-option\"\u003eCommand option\u003c/a\u003e\n        - \u003ca href=\"#command-action\"\u003eCommand action\u003c/a\u003e\n    - \u003ca href=\"#add-commands-from-directory\"\u003eb) Add commands from a directory\u003c/a\u003e\n        - \u003ca href=\"#command-file\"\u003eCommand file\u003c/a\u003e\n- \u003ca href=\"#command-examples\"\u003eCommand examples\u003c/a\u003e\n- \u003ca href=\"#parse-input\"\u003eParse input\u003c/a\u003e\n- \u003ca href=\"#lummander-instance-methods\"\u003eLummander instance methods\u003c/a\u003e\n    - \u003ca href=\"#lummander-instance-log\"\u003eLogs methods\u003c/a\u003e\n    - \u003ca href=\"#lummander-instance-advanced\"\u003eAdvanced methods\u003c/a\u003e\n    - \u003ca href=\"#lummander-instance-pcall\"\u003ePcall\u003c/a\u003e\n- \u003ca href=\"#lummander-apply-theme\"\u003eTheme\u003c/a\u003e\n- \u003ca href=\"#cli-to-path\"\u003eAdd the CLI to the PATH\u003c/a\u003e\n\n# \u003cdiv id=\"features\"\u003eFeatures\u003c/div\u003e\n- Define required positional arguments, optional or flagged options for a command.\n- Add commands directly to lummander instance or from a folder.\n\n*Require [LuaFileSystem](https://keplerproject.github.io/luafilesystem/)*.\n\n# \u003cdiv id=\"installation\"\u003eInstallation\u003c/div\u003e\n\n- Using Luarocks:\n\n```bash\nluarocks install lummander\n```\n\n# \u003cdiv id=\"usage\"\u003eUsage\u003c/div\u003e\n```lua\n-- Require \"lummander\"\nlocal Lummander = require \"lummander\"\n\n-- Create a lummander instance\nlocal cli = Lummander.new{\n        title = \"My Custom App\", -- \u003cstring\u003e title for CLI. Default: \"\"\n        tag = \"myapp\", -- \u003cstring\u003e CLI Command to execute your program. Default: \"\".\n        description = \"My App description\", -- \u003cstring\u003e CLI description. Default: \"\"\n        version = \"0.1.1\", -- \u003cstring\u003e CLI version. Default: \"0.1.0\"\n        author = \"Myself\", -- \u003cstring\u003e author. Default: \"\"\n        root_path = \"/path/to/folder/contains/this/file\", -- \u003cstring\u003e root_path. Default \"\". Concat this path to load commands of a subfolder\n        theme = \"acid\", -- Default = \"default\". \"default\" and \"acid\" are built-in themes\n        flag_prevent_help = false -- \u003cboolean\u003e Prevent help message if not command found. Default: false\n    }\n\n-- Add commands\ncli:command(\"mycmd\", \"My command description\")\n    :action(function(parsed, command, app)\n        print(\"You activated `mycmd` command\")\n    end)\n\ncli:command(\"sum \u003cvalue1\u003e \u003cvalue2\u003e\", \"Sum 2 values\")\n    :option(\n        \"option1\",\"o\",\"Option1 description\",nil,\"normal\",\"option_default_value\")\n    :option(\n        \"option2\",\"p\",\"Option2 description\",nil,\"normal\",\"option2_default_value\")\n    :action(function(parsed, command, app)\n        print(\"\".. parsed.value1.. \"+\"..parsed.value2..\" = \" ..\n              tostring(tonumber(parsed.value1) + tonumber(parsed.value2)))\n    end)\n\n-- Parse and execute the command wrote\ncli:parse(arg) -- parse arg and execute if a command was written\n```\n\n# \u003cdiv id=\"create-instance\"\u003eCreate an instance\u003c/div\u003e\n\n```lua\nlocal Lummander = require\"lummander\"\n\nlocal cli = Lummander.new{\n    title = \"CLI title\",\n    tag = \"myapp\", -- define command to launch this script at terminal\n    description = \"My App description\", -- \u003cstring\u003e CLI description. Default: \"\"\n    version = \"0.1.1\", -- define cli version\n    author = \"Myself\", -- \u003cstring\u003e author. Default: \"\"\n    root_path = \"/path/to/folder/contains/this/file\", -- \u003cstring\u003e root_path. Default \"\". Concat this path to load commands of a subfolder\n    theme = \"acid\", -- Default = \"default\". \"default\" and \"acid\" are built-in themes\n    flag_prevent_help = false -- prevent show help when :parse() doesn't find a valid command to execute\n}\n```\n\n# \u003cdiv id=\"add-commands\"\u003eAdd commands\u003c/div\u003e\n\nWays to add commands:\na. Using lummander instance methods\nb. Loading from a folder files what contain a command table defined in lua files.\n\n## \u003cdiv id=\"add-commands-using-lummander\"\u003ea) Add command using lummander instance methods\u003c/div\u003e\n\nCreate a command with:\n\n```lua\ncli:command(schema, description, config)\n-- or\ncli:command(schema, config)\n-- or\ncli:command(config)\n```\n\n- \u003ca href=\"#command-schema\"\u003eschema\u003c/a\u003e: string - command schema\n- description: string or table - command description\n- \u003ca href=\"#command-config\"\u003econfig\u003c/a\u003e: table or nil - command config. Table with \u003ca href=\"#command-file\"\u003efields\u003c/a\u003e\n\n*Note: if description is a table, then is treated like config.*\n\n### \u003cdiv id=\"command-schema\"\u003eCommand schema\u003c/div\u003e\nIt's a string what should have a main command word. It can have required positional arguments (\u003c\u003e) or optionals ([]).\n\nExample: `mycmd \u003crequired_positional_argument1\u003e \u003crequired_positional_argument2\u003e [optional_positional_argument1]`\n\nArguments:\n- \u003cargument_name\u003e - Required argument\n- [argument_name] - Optional argument\n- [argument_name...] - Optional arguments (array)\n- [...argument_name] - Optional arguments (array)\n\n## Command methods\n\n- \u003ca href=\"#command-option\"\u003e`cmd:option(...)`\u003c/a\u003e: add a flagged option.\n- \u003ca href=\"#command-action\"\u003e`cmd:action(fn)`\u003c/a\u003e: set a function to execute for this command.\n\nSee docs to see more methods.\n\n## \u003cdiv id=\"command-option\"\u003eCommand option\u003c/div\u003e\n\nAdd options to a command with:\n```lua\n-- only long_name/long and short_name/short are required\ncmd:option(long_name, short_name, description, transform, type, default)\n-- or \ncmd:option({long, short, description, transform, type, default})\n```\n\n- short_name: string - option short name\n- long_name: string - option long name\n- description: string - option description\n- transform: function - transform value received for this option before execute action\n- type: `normal` or `flag`. Default `normal`. `flag` is true or false. `normal` can accept a value\n- default: option default value (setted by default to `parsed` table)\n\nExample:\n```lua\ncli:command(\"mycmd\",\"My cmd description\")\n    :option(\"output\", \"o\", \"My flagged option description\", function(value) return \"./\"..value..\".txt\" end, \"normal\", \"my_default_value\")\n\n-- You can add multiple options\ncli:command(\"mycmd\",\"My cmd description\")\n    :option(\"output\", \"o\", \"My flagged option description\", function(value) return \"./\"..value..\".txt\" end, \"normal\", \"my_default_value\")\n    :option(\"mode\", \"m\", \"Option mode description\")\n    :option({long = \"confirm\", short = \"c\", description = \"No require confirm\", type = \"flag\"}) -- addig with table as first argument\n```\n\nYou can add so many flagged options like you want.\n\nYou can define too this config in a table when you create the command. The example from above would be:\n\n```lua\ncli:command(\"mycmd\",\"My cmd description\", {\n    options = {\n        {long = \"output\", short = \"o\", description = \"My flagged option description\", transform = function(value) return \"./\"..value..\".txt\" end, type = \"normal\", default = \"my_defautl_value\"}\n    }\n})\n\ncli:command(\"mycmd\",\"My cmd description\", {\n    options = {\n        {long = \"output\", short = \"o\", description = \"My flagged option description\", transform = function(value) return \"./\"..value..\".txt\" end, type = \"normal\", default = \"my_defautl_value\"},\n        {long = \"mode\", short = \"m\", description = \"Option mode description\"}\n    }\n})\n\n-- You can pass command config table in description argument and add to table the description field\n\ncli:command(\"mycmd\", {\n    description = \"My cmd description\", -- includes in command config argument\n    options = {\n        {long = \"output\", short = \"o\", description = \"My flagged option description\", transform = function(value) return \"./\"..value..\".txt\" end, type = \"normal\", default = \"my_defautl_value\"}\n    }\n})\n```\n\n## \u003cdiv name=\"#command-action\"\u003eCommand action\u003c/div\u003e\n\nSet an action to execute when the command is activated.\n```lua\ncmd:action(fn)\n```\n\n- `fn` function - trigger a function when command is activated.\n\n```lua\ncmd:action(function(parsed, command, app)\n    -- parsed: table with command parsed. Include required positional arguments, optional positional arguments, and flagged options (with long name)\n    -- command: command itself\n    -- app: lummander instance\nend)\n```\n\nYou can print parsed table with `parsed:print()` to see values that contains.\n\n## \u003cdiv id=\"add-commands-from-directory\"\u003eb) Add commands from a directory\u003c/div\u003e\n\nYou can add commands from a folder with .lua files.\n\n```lua\ncli:commands_dir(\"folderpath\")\n```\n\n### \u003cdiv id=\"command-file\"\u003eCommand file\u003c/div\u003e\n\nThe command file interface is:\n\n```lua\n-- folderpath/mycmd.lua\nreturn {\n    schema = \"mycmd \u003creq_arg\u003e [opt_arg]\", -- Schema to parse. Required\n    description = \"Command description\", -- Command description\n    positional_args = { -- Set description or {description, default} for positional arguments\n        req_arg = \"A description for a required argument\",\n        opt_arg = {description = \"A description for a optional argument\", default = \"default_positional_option_value\"},\n    },\n    options = { -- Add flags arguments\n        {long = \"yes\", short = \"y\", description = \"Accept\", transform : function(param) end, type = \"normal\", default = \"default_value\"} -- same command:option(\"y\",\"yes\", \"Accept\", function(param) end, \"normal\", \"default_value\")\n    },\n    hide = false, -- hide from help command\n    main = true, -- do this command default action to CLI if true. Default = nil = false\n    action = function(parsed, command, app) -- same command:action(function)\n        parsed:print()\n        local sufix = \"\"\n        if(parsed.yes)then sufix = \" -y\" end\n        os.execute(\"npm init\"..sufix)\n    end\n}\n```\n\n*Note: only `schema` is required*\n\n## \u003cdiv id=\"command-examples\"\u003eCommand examples\u003c/div\u003e\n\n```lua\n-- Command with only a command word.\n-- Argumments:\n    -- - cmd: time\n\ncli:command(\"time\", \"Show time\")\n    :action( -- Join a action to execute\n        function(parsed, command, app) -- app is lummander instance\n            print(os.date(\"Time is: %I:%M:%S\"))\n        end\n)\n\n-- Command with only a command word and a required positional argument.\n-- Argumments:\n    -- - cmd: hi\n    -- - req_arg1: name (closed in \u003c\u003e means is required)\ncli:command(\"hi \u003cname\u003e\", \"Say hi to someone\")\n    :action(\n        function(parsed, command, app)\n            -- parsed is a table that includes a field called \"name\" due to \u003cname\u003e at command schema\n            -- \u003cname\u003e is a required positional argument and is needed to trigger this function\n            -- parsed = { name }\n            print(\"Hi \" .. parsed.name)\n        end\n)\n\n-- Command with only a command word, a required positional argument and an option.\n-- Argumments:\n    -- - cmd: hi\n    -- - rea_arg1: name (closed in \u003c\u003e means is required)\n    -- - option: hello/h \n\ncli:command(\"hi \u003cname\u003e\", \"Say hello/hi to someone\")\n    :option(\"hello\", \"h\",\"Say hello instead\") -- include a option. Example: myapp hi MyName -h. Then parsed.hello = true \n    :action(\n        function(parsed, command, app)\n            -- parsed = {name, hello}\n            local saludation = \"Hi\"\n            if(parsed.hello) then saludation = \"Hello\" end\n            print(saludation .. \" \" .. parsed.name)\n            -- `hi Lummander` =\u003e Hi Lummander\n            -- `hi Lummander -h` =\u003e Hello Lummander\n            -- `hi Lummander --hello` =\u003e Hello Lummander\n        end\n)\n\n-- Command with a command word, 1 required positional argument, 1 optional positional and 1 option \n-- Argumments:\n    -- - cmd: hi\n    -- - req_arg1: text (closed in \u003c\u003e means is required)\n    -- - opt_arg2: othertext (closed in [] means is optional)\n    -- - option: -o, --output\n\ncli:command(\"save \u003ctext\u003e [othertext]\", \"Save a file\")\n    :option(\"output\", \"o\", \"Output path\") \n    :action(\n        function(parsed)\n            -- parsed = {text, othertext, output}\n            local default_path = \"mypath/file.txt\"\n            local filename = parsed.output or default_path\n            my_write_file_function(filename, parsed.text)\n            -- `save \"My text\"` =\u003e Save a file at default_path\n            -- `save \"My text\" -o otherpath/myotherfile.txt` =\u003e Save a file at \"otherpath/myotherfile.txt\"\n            -- `save \"My text\" --output otherpath/myotherfile.txt` =\u003e Save a file at \"otherpath/myotherfile.txt\"\n        end\n)\n\ncli:command(\"install [packs...]\")\n    :option(\"dev\", \"d\", \"Set install mode\", nil, \"flag\")\n    :action(function(parsed, command, app)\n        parsed.packs:for_each(function(pack) -- options like array is a table with special methods. See https://desvelao.github.io/f/classes/ftable.html\n            -- do something with each pack\n        end)\n    end)\n```\n\n## \u003cdiv id=\"parse-input\"\u003e3. Parse input\u003c/div\u003e\n```lua\ncli:parse(arg) -- Parse a table like-array (space/comilla separated arguments). `arg` variable in Lua is an array that contains arguments passed when it executed. This is REQUIRED. Execute a command if is found.\n\n-- cli.parsed property is created after this\n```\n\n# \u003cdiv id=\"lummander-instance-methods\"\u003eLummander instance methods\u003c/div\u003e\n\nNote: `cli` is lummander instance.\n\n## \u003cdiv id=\"lummander-instance-log\"\u003eLogs methods\u003c/div\u003e\n\n- `cli.log:info(...)`: info log\n- `cli.log:warn(...)`: warning log\n- `cli.log:error(...)`: error log\n\n## \u003cdiv id=\"lummander-instance-advanced\"\u003eAdvanced methods\u003c/div\u003e\n\n- `cli:execute(cmd, [fn])`: like os.execute but returns text or callback function with value returned by cmd executed.\n\n```lua\ncli:execute(\"cd\", function(value)\n    -- do something with value after terminal executution is finished\nend)\n\n-- same to:\nlocal value = cli:execute(\"cd\")\n-- do something with value or after terminal executution is finished\n```\n\n- `cli:find_cmd(cmd_name)`: find a cli command by name\n\n- `cli:action(cmd_name)`: set default action. String or Command. Default (help command)\n\n- \u003ca href=\"#lummander-apply-theme\"\u003e`cli:apply_theme(theme)`\u003c/a\u003e: apply a theme\n\n- \u003ca href=\"#lummander-pcall\"\u003e`cli.pcall(fn)`\u003c/a\u003e: execute a function as pcall()\n\n- \u003ca href=\"http://keplerproject.github.io/luafilesystem/\"\u003e`cli.lfs`\u003c/a\u003e: access to LuaFileSystem\n\n### \u003cdiv id=\"lummander-instance-pcall\"\u003eLummander.pcall\u003c/div\u003e\n\n- `pcall(fn)`: create a pcall instance with a function to check.\n- `pcall:pass(fn)`: function to execute if pcall success.\n- `pcall:fail(fn)`: function to execute if pcall failed. Call pcall:done().\n- `pcall:done()`: execute the pcall\n\n```lua\nlum.pcall(function() -- check function to use in pcall\n    print(\"hi\")\n    error(\"Error found on this function\")\nend)\n    :pass(function() -- execute if check function doesn't raise errors\n    -- code..\nend):fail(function() -- execute if chech function raise some error\n    -- code...\nend)\n```\n\n# \u003cdiv id=\"lummander-apply-theme\"\u003eTheme\u003c/div\u003e\n\nYou can can create a custom theme for your terminal. Styles that you can apply are:\n\n- text color: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`\n- background color: `bgblack`, `bgred`, `bggreen`, `bgyellow`, `bgblue`, `bgmagenta`, `bgcyan`, `bgwhite`\n- other: `bold`, `underlined` and `reversed`\n\nUse `cli:apply_theme(theme)` to load it.\n\nExample:\n```lua\n-- mytheme.lua\nreturn {\n    cli = {\n        title =  \"yellow\",\n        text = \"white\",\n        category = \"yellow\"\n    },\n    command = {\n        definition = \"green\",\n        description = \"white\",\n        argument = \"yellow\",\n        option = \"yellow\",\n        category = \"red\"\n    },\n    primary = \"green\",\n    secondary = \"red\",\n    success = \"green\",\n    warning = \"yellow\",\n    error = \"red\"\n}\n```\n\n*Note: if you dont define some style, this will be `white` by default.*\n\n`cli.theme` print with theme color defined the text\n```lua\ncli.theme.cli.title(text)\ncli.theme.cli.text(text)\ncli.theme.cli.category(text)\ncli.theme.command.definition(text)\ncli.theme.command.description(text)\ncli.theme.command.argument(text)\ncli.theme.command.option(text)\ncli.theme.command.category(text)\ncli.theme.primary(text)\ncli.theme.secondary(text)\ncli.theme.sucess(text)\ncli.theme.warning(text)\ncli.theme.error(text)\n```\n\n# \u003cdiv id=\"cli-to-path\"\u003eAdd the CLI to the PATH\u003c/div\u003e\n## Windows\nCreate a `.bat` or `.cmd` file what contains:\n\n```\nlua \"absolute_path_to_your_cli_script.lua\" %*\n```\n\nThe name of this file will be CLI command to init your CLI script.\n\n*Note: `lua.exe` should be in a folder what is in os PATH variable, if not, add the folder path to os PATH variable.*\n\nExample:\n```\nlua \"C:/path_to_cli/mycli.lua\" %*\n```\n\n# License\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdesvelao%2Flummander","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdesvelao%2Flummander","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdesvelao%2Flummander/lists"}