README.md

April 18, 2026 · View on GitHub

argsh logo
arg.sh

Quickstart · CLI Parser · Libraries · Styleguide

Stargazers Releases Bash Coverage Rust Coverage

 

Bash is a powerful tool (and widly available), but it's also a language that is easy to write in a way that is hard to read and maintain. As such Bash is used often but used as little as possible, resulting in poor quality scripts that are hard to maintain and understand.

Not only is this happaning as Bash is seen as a "glue" language, but also because there is no hardend styleguide, easy testing and good documentation around it.

The Google Shell Style Guide says it itself:

If you are writing a script that is more than 100 lines long, or that uses non-straightforward control flow logic, you should rewrite it in a more structured language now.

You can write bad code in every other language too, but there is lots of effort to make it better. So let's make it better for bash too. Let's make Bash a more structured language.

This is what argsh is trying to do. Check out the Quickstart to see how you can use it.

 

📦 Install

curl -sL https://min.arg.sh > .bin/argsh
chmod +x .bin/argsh

Or use the interactive installer:

bash -c "$(curl -sL https://get.arg.sh)"

See the Getting Started guide for more options.

 

🧠 Design Philosophy

  • First class citizen: Treat your scripts as first class citizens. They are important and should be treated as such.
  • Be Consistent: Consistency is key. It makes your scripts easier to read and maintain.
  • Perfect is the enemy of good: Don't try to make your scripts perfect. Make them good and maintainable.
  • Write for the next person: Write your scripts for the next person that has to read and maintain them. This person might be you.

 

🔧 CLI Parser

argsh turns a plain Bash array into a full CLI — flags, types, defaults, validation and help — with zero boilerplate.

#!/usr/bin/env bash
source argsh

main() {
  local name age verbose
  local -a args=(
    'name|n:!'    "Name of the person"
    'age|a:int'   "Age in years"
    'verbose|v:+' "Enable verbose output"
  )
  :args "Greet someone" "${@}"

  echo "Hello ${name}, you are ${age} years old."
}

main "${@}"
$ ./greet --name World --age 42
Hello World, you are 42 years old.

$ ./greet --help
Greet someone

Options:
   --name, -n     string  Name of the person (required)
   --age, -a      int     Age in years
   --verbose, -v          Enable verbose output
   --help, -h             Show this help message

The :args builtin handles --flag value, -f value, --flag=value, --no-flag (booleans), automatic --help/-h, unknown flag errors, and type validation (int, float, boolean, file, or custom). See the CLI Parser docs for the full syntax.

 

🗂️ Subcommand Routing

:usage gives you git-style subcommands with auto-generated help, fuzzy suggestions on typos, and convention-based function dispatch.

#!/usr/bin/env bash
source argsh

main::deploy() {
  local env
  local -a args=(
    'env|e:!'  "Target environment"
    '-' "Globals options:"
    "${args[@]}"
  )
  :args "Deploy the application" "${@}"

  echo "${cluster} -> Deploying ${env} environment..."
}

main::status() {
  :args "Show deployment status" "${@}"

  echo "${cluster} -> All systems operational."
}

main() {
  local cluster="${CLUSTER:-local}"
  local -a args=(
    'cluster|c'    "Specific cluster"
  )
  local -a usage=(
    'deploy|d' "Deploy the application"
    'status|s' "Show deployment status"
  )
  :usage "Application manager" "${@}"

  "${usage[@]}"
}

main "${@}"
$ ./app deploy --env production
local -> Deploying production environment...

$ ./app stat
Invalid command: stat. Did you mean 'status'?

Each subcommand maps to a function by convention (main::deploy, main::status). Nested subcommands compose naturally — just add another :usage inside a subcommand function.

 

🧰 Batteries Included

argsh is not just a library — the launcher itself is a small multi-tool that ships with the utilities you need to develop, test, and release a Bash project. Install once, then argsh --help shows everything available:

$ argsh --help
Tools
  minify      Minify Bash files
  lint        Lint Bash files
  test        Run tests
  coverage    Generate coverage report for your Bash scripts
  docs        Generate documentation
Runtime
  builtin     Manage native builtins (.so)
  status      Show argsh runtime status
CommandWhat it doesBacked by
argsh testRun .bats tests; auto-discovers tests via PATH_TESTSbats-core
argsh lintLint all shell files (shellcheck + argsh-lint)shellcheck + argsh-lint
argsh coverageGenerate a coverage report with a minimum thresholdkcov
argsh docsGenerate Markdown docs from @description commentsshdoc
argsh minifyBundle + minify + obfuscate a multi-file project into a single scriptnative Rust minifier
argsh statusReport runtime state (builtin, discovered tests, coverage)built-in
argsh builtinInstall/update the native .so builtinbuilt-in

Each utility runs locally when its backing tool is installed, and transparently forwards to the official docker image otherwise — so argsh test works on any machine with just Docker. Every command has its own -h/--help, and unknown commands get typo suggestions (argsh testsDid you mean 'test'?).

Zero config: drop .bats files anywhere under ${PATH_TESTS:-.} and argsh test finds them. See argsh status to verify discovery.

 

🤖 AI Integration

Every argsh script is AI-ready out of the box — no glue code required.

MCP Server — expose subcommands as tools for AI agents over Model Context Protocol:

./myscript mcp            # starts JSON-RPC 2.0 stdio server
./myscript mcp --help     # prints .mcp.json config snippet

LLM Tool Schemas — generate ready-to-use tool definitions for AI APIs:

./myscript docgen llm claude   # Anthropic tool array (input_schema)
./myscript docgen llm openai   # OpenAI function calling format
./myscript docgen llm gemini   # Gemini (OpenAI-compatible)

Shell Completions & Docs — also generated from the same source:

./myscript completion bash|zsh|fish
./myscript docgen man|md|rst|yaml

See the AI Integration docs for details on MCP and LLM tool schemas.

 

⚡ Native Builtins (Rust)

argsh ships with optional Bash loadable builtins compiled from Rust. When the shared library is available, the core parsing commands (:args, :usage, type converters, etc.) run as native code inside the Bash process — zero fork overhead, zero subshell cost.

BuiltinPurpose
:argsCLI argument parser with type checking
:usageSubcommand router with intelligent suggestions
:usage::helpDeferred help display (runs after setup code)
is::array, is::uninitialized, is::set, is::ttyVariable introspection
to::int, to::float, to::boolean, to::file, to::stringType converters
args::field_nameField name extraction
:usage::completionAutocomplete backend for :usage completion (bash, zsh, fish)
:usage::docgenDocumentation backend for :usage docgen (man, md, rst, yaml, llm)
:usage::mcpMCP server backend for :usage mcp (JSON-RPC 2.0 over stdio)

Transparent fallbackargs.sh auto-detects the .so at load time. If found, builtins are enabled via enable -f and the pure-Bash function definitions are skipped. If not found, everything works as before with no change in behavior.

# Build (requires Rust toolchain)
cd builtin && cargo build --release
# Output: builtin/target/release/libargsh.so

# Copy to PATH_BIN and auto-load
cp builtin/target/release/libargsh.so .bin/argsh.so
source libraries/args.sh

# Or set explicit path
export ARGSH_BUILTIN_PATH="/path/to/argsh.so"
source libraries/args.sh

Search order: ARGSH_BUILTIN_PATH > PATH_LIB > PATH_BIN > LD_LIBRARY_PATH > BASH_LOADABLES_PATH

Benchmark

Subcommand dispatch (cmd x x ... x -h) — 50 iterations:

DepthPure BashBuiltinSpeedup
101188 ms21 ms57x
252686 ms53 ms51x
505434 ms155 ms35x

Argument parsing (cmd --flag1 v1 ... --flagN vN) — 50 iterations:

FlagsPure BashBuiltinSpeedup
105405 ms4 ms1351x
2513986 ms9 ms1554x
5029603 ms20 ms1480x

Run bash bench/usage-depth.sh to reproduce.

 

🧩 IDE Support

argsh includes a Language Server, CLI linter, debugger, and VSCode/Windsurf extension for a complete development experience:

  • Diagnostics — 12 checks (AG001–AG013) with shellcheck-style suppression (# argsh disable=AG004)
  • Completions — modifiers, types (built-in + custom), annotations, library functions (is::, to::, string::, ...)
  • Help preview — hover over functions to see generated --help output
  • Go to definition — Ctrl+Click on usage entries, :- mappings, :~custom types, imports
  • Cross-file resolution — follows import and source argsh across files
  • Auto formatter — aligns args/usage array entries on save
  • Code lens — flag/subcommand counts above functions with parent link
  • Script preview — dashboard with command tree, MCP tools, exports (Ctrl+Shift+A)
  • Command tree panel — function hierarchy with active function highlighting
  • Snippetsargsh-main, argsh-func, argsh-args, argsh-flag-*, argsh-import

Works alongside shellcheck — argsh handles framework-specific validation, shellcheck handles general bash.

CLI Linter (argsh-lint) — standalone binary with shellcheck-compatible flags for CI pipelines:

argsh-lint script.sh                        # gcc-style output
argsh-lint --format=json --severity=warning  # JSON for CI
argsh lint                                   # runs both shellcheck + argsh-lint
argsh lint --only-argsh                      # argsh-lint only

See the Lint docs for the full flag reference.

Debugger (argsh-dap) — step-through debugging via VSCode with no external dependencies:

  • Breakpoints — file:line, conditional ((( i == 3 ))), and by subcommand name
  • Stepping — step in (F11), step over (F10), step out (Shift+F11)
  • Call stack — full FUNCNAME/BASH_SOURCE trace with argsh namespace resolution
  • Variable inspection — argsh Args inspector shows :args field definitions with types
  • Watch expressions and set variable at runtime
  • Subshell support$(), pipes, { ...; } & don't deadlock

Uses bash's built-in DEBUG trap — no bashdb required. See the Debugger docs.

# Build from source
cd crates/argsh-lsp && cargo build --release  # builds argsh-lsp, argsh-lint, argsh-dap
cd vscode-argsh && npm install && npm run compile

See the LSP docs for configuration and full feature reference.

 

📜 License

Argsh is released under the MIT license, which grants the following permissions:

  • Commercial use
  • Distribution
  • Modification
  • Private use

For more convoluted language, see the LICENSE. Let's build a better Bash experience together.

 

❤️ Gratitude

Thanks to the following tools and projects developing this project is possible:

  • medusajs: From where the base of this docs, github and more is copied.
  • Google Styleguide: Google's Shell Style Guide used as base for the argsh styleguide.
  • Catppuccin: Base for the readme.md and general nice color palettes.

 

🐾 Projects to follow

  • bash-it: A Bash shell - autocompletion, themes, aliases, custom functions, and more.

 

Copyright © 2024-present Jan Guth