| name | skogai-argc |
| description | Create and manage argc-powered bash CLIs and Argcfile.sh task runners. Use when creating Argcfile.sh files, converting bash scripts to argc CLIs, adding argument parsing, shell completion, or task automation to projects. |
Argc CLI Framework
Purpose
This skill enables working with the argc framework - a bash-based system that transforms comment annotations into full-featured command-line interfaces with automatic argument parsing, validation, help generation, and shell completions.
Argc has two components:
- argc framework - The parser/engine that reads comment tags and generates CLI functionality
- argc-completions - 1000+ pre-built completion scripts for common commands, using argc as the engine
When to Use
Trigger this skill when:
- Creating or modifying
Argcfile.shtask runner files - Converting existing bash scripts to use argc argument parsing
- Adding command-line argument handling to bash scripts
- Implementing shell completions for custom commands
- Building CLI tools that need subcommands, flags, options, or positional arguments
- Setting up task automation similar to Make but using bash
How to Use This Skill
Core Workflow
CRITICAL: Always use argc --argc-eval to understand what's happening
You're good at bash, not argc. The fastest way to learn argc is to see the bash code it generates:
# Don't just run: argc +.md process file.md
# Instead, SEE what it generates:
argc --argc-eval Argcfile.sh +.md process file.md
This shows you exactly what bash code argc creates. Once you see the generated code, you can understand it because it's just bash.
Why this matters:
- Argc is code generation, not magic
- The eval output IS the implementation
- Validation errors are generated bash (heredoc + exit 1)
- Success cases are variable assignments + function calls
- Hooks (
_argc_before,_argc_after) are visible in the execution order
Integration pattern - Every argc script requires the eval line:
eval "$(argc --argc-eval "$0" "$@")"
The argc --argc-eval command reads comment tags, validates arguments, and generates bash code. The eval executes this generated code.
Success case - Valid arguments generate variable setup and function invocation:
$ argc --argc-eval Argcfile.sh +.md process ./argc-skill/SKILL.md
export ARGC_PWD=/home/skogix/dev/skogargc
export ARGC_VARS=YXJnY19maWxldHlwZT0ubWQ7YXJnY19vdXRwdXQ9Li9vdXQ7...
argc_filetype=.md # Symbol value
argc_output=./out # Option with default
argc_files=( ./argc-skill/SKILL.md ) # Validated argument
argc__args=( Argcfile +.md process ./argc-skill/SKILL.md )
argc__fn=process
argc__positionals=( ./argc-skill/SKILL.md )
_argc_before # Hook runs first
process ./argc-skill/SKILL.md # Command executes
_argc_after # Hook runs last
Validation error - Wrong filetype shows argc validated the symbol first, then failed on file:
$ argc --argc-eval Argcfile.sh +.sh process ./argc-skill/SKILL.md
export ARGC_PWD=/home/skogix/dev/skogargc
command cat >&2 <<-'EOF'
error: invalid value `./argc-skill/SKILL.md` for `<FILES>...`
[possible values: ./argc-skill/examples/hooks.sh, ./argc-skill/examples/demo.sh, ...]
EOF
exit 1
The symbol +.sh set argc_filetype=.sh, which made _choice_files only return .sh files. The .md file failed validation because it wasn't in the filtered list.
Missing required argument - Generates error and exit:
# argc --argc-eval examples/quick-start.sh
command cat >&2 <<-'EOF'
error: the following required arguments were not provided:
<FILE>
EOF
exit 1
Key points:
- Validation happens during
argc --argc-eval, before your bash code runs - Error path: heredoc to stderr + exit 1 (script never runs)
- Success path: variable setup + function execution
- Flag values are
0or1, not booleans - Multi-value arguments become bash arrays:
${argc_files[@]} - Built-in variables (
argc__args,argc__fn,argc__positionals) available for advanced use
Use comment tags - Define CLI structure through bash comments prefixed with
@:# @describe Command description # @flag -v --verbose Enable verbose output # @option -f --file Input file path # @arg name! Required positional argumentAccess parsed values - Argc generates variables with
argc_prefix:echo $argc_verbose # Flag value (0 or 1) echo $argc_file # Option value echo $argc_name # Argument valueMulti-value parameters become arrays:
# @arg files* # @option --include* for file in "${argc_files[@]}"; do echo "File: $file" done echo "Includes: ${argc_include[@]}" echo "Count: ${#argc_files[@]}"Built-in variables for advanced use:
argc__args- Array of all CLI argumentsargc__positionals- Array of positional arguments only (no flags/options)argc__fn- Name of the function being executed
Environment variables injected by argc:
ARGC_PWD- Original working directory (available in Argcfile.sh)ARGC_SHELL_PATH- Path to shell executable (configurable)ARGC_SCRIPT_NAME- Override default Argcfile.sh name (configurable)
Available Comment Tags
Reference these tags when building argc scripts:
| Tag | Purpose | Example |
|---|---|---|
@describe |
Command description | # @describe My CLI tool |
@cmd |
Define subcommand | # @cmd build Build it |
@alias |
Subcommand aliases | # @alias b |
@arg |
Positional argument | # @arg file! |
@option |
Option argument | # @option --output |
@flag |
Boolean flag | # @flag -v --verbose |
@env |
Environment variable | # @env API_KEY! |
@meta |
Metadata | # @meta version 1.0 |
Use "!" suffix for required parameters and "*" suffix for multi-value arrays.
@cmd vs @describe - When to Use Which
Use @describe for single-script invocations:
#!/bin/bash
# @describe Process multiple files with validation
# @arg inputs* Input files to process
# @option --output=./out Output directory
# @flag -v --verbose
main() {
[[ "$argc_verbose" == "1" ]] && echo "Processing ${#argc_inputs[@]} files"
for file in "${argc_inputs[@]}"; do
echo "Processing: $file -> $argc_output/$(basename "$file")"
done
}
eval "$(argc --argc-eval "$0" "$@")"
Run directly: ./script.sh file1.txt file2.txt file3.txt --verbose
Use @cmd for Argcfile.sh task runners or scripts with subcommands:
#!/bin/bash
# Argcfile.sh
# @cmd build Build the project
build() {
echo "Building..."
}
# @cmd test Run tests
test() {
echo "Testing..."
}
eval "$(argc --argc-eval "$0" "$@")"
Run with subcommand: argc build or argc test
Rule: @describe describes the main entry point. @cmd defines subcommands (tasks). Use @cmd when you have multiple functions that users can invoke separately.
Argument Modifiers
Argc uses suffix symbols to modify parameter behavior:
Basic modifiers:
- "!" - Required parameter (must be provided)
- "*" - Multi-value parameter (can be provided multiple times, becomes array)
- "+" - Required multi-value (combines "!" and "*")
Examples:
# @arg name! Required positional arg
# @arg files* Optional multi-value (0 or more)
# @arg tags+ Required multi-value (1 or more)
# @option --config! Required option
# @option --include* Multi-value option (can use multiple times)
Accessing multi-value parameters:
# @arg files* Input files to process
main() {
for file in "${argc_files[@]}"; do
echo "Processing $file"
done
}
Rest-of-arguments pattern:
Use <VALUE+> in the last notation to capture "all remaining arguments":
# @arg command! Command to run
# @arg args* <ARGS+> All remaining arguments
main() {
echo "Running: $argc_command ${argc_args[@]}"
"$argc_command" "${argc_args[@]}"
}
Run: ./script.sh docker run -it ubuntu bash - captures run -it ubuntu bash into argc_args array.
Inline Choices and Default Values
Inline choices - Define valid values directly in the tag:
# @arg env[dev|staging|prod] Environment choice
# @option --format[json|yaml|toml] Output format
Default choice - Use = prefix to set default:
# @arg env[=dev|staging|prod] Defaults to 'dev'
# @option --format[=json|yaml|toml] Defaults to 'json'
Default values - Use =value syntax:
# @arg name=world Defaults to 'world' if not provided
# @option --timeout=30 Defaults to 30
# @option --host=localhost Defaults to 'localhost'
When to use inline vs choice functions:
- Use inline choices for static lists (3-10 items) known at script-writing time
- Use choice functions for dynamic lists generated at runtime (files, git branches, API data)
Environment Variables
The @env tag validates and documents environment variables your script requires:
Basic syntax:
# @env API_KEY! Required env var
# @env DEBUG Optional env var
# @env PORT=8080 Optional with default value
# @env ENV[dev|prod] Env var with choices
# @env REGION[=us|eu|asia] Env var with choices and default
Validation example:
#!/bin/bash
# @describe API client
# @env API_KEY! API authentication key
# @env API_TIMEOUT=30 Request timeout in seconds
main() {
echo "Using API_KEY: ${API_KEY:0:8}..."
echo "Timeout: $API_TIMEOUT seconds"
}
eval "$(argc --argc-eval "$0" "$@")"
If API_KEY is not set, argc generates an error before your code runs:
error: the following required environment variables were not provided:
API_KEY
Use in Argcfile.sh tasks:
# @cmd deploy Deploy application
# @env DEPLOY_ENV![dev|staging|prod] Target environment
# @env AWS_REGION=us-east-1 AWS region
deploy() {
echo "Deploying to $DEPLOY_ENV in $AWS_REGION"
}
Environment variables are accessed directly by name (not via argc_ prefix).
Choice Functions - Dynamic Validation
When to create choice functions:
Choice functions should be created almost always when the valid input values are known at runtime. They should always be created when input can be validated by checking against a generated list.
Basic pattern:
# @arg file!`_choice_files` File to process
_choice_files() {
ls examples/
}
How it works:
- The backtick syntax
`_choice_files`tells argc to call this function - Argc captures the function's stdout (one value per line)
- Input is validated against this list
- Invalid input triggers automatic error with possible values shown
Real-world examples:
# Files in directory
_choice_files() {
ls -1 "$EXAMPLE_FOLDER_PATH"
}
# Git branches
_choice_branches() {
git branch --list --format='%(refname:short)'
}
# Docker containers
_choice_containers() {
docker ps --format '{{.Names}}'
}
# GitHub repositories via gh CLI
_choice_repos() {
gh repo list skogai --json nameWithOwner --jq ".[].nameWithOwner"
}
# API endpoints from config
_choice_endpoints() {
jq -r '.endpoints[].name' config.json
}
Key principle: If you can generate a list of valid values, create a choice function. This enables immediate validation and helpful error messages.
Symbols - Dynamic Choice Functions
Symbols let you use an earlier parameter to filter what values are valid for later parameters. This creates "pre-validation validation" where one choice constrains another.
Pattern:
# @meta symbol +filetype[`_choice_filetypes`]
# @arg files+,[`_choice_files`] Files to process
_choice_filetypes() {
echo ".md"
echo ".sh"
}
_choice_files() {
find ./src/ -type f -name "*${argc_filetype}"
}
How it works:
@meta symbol +filetypedefines+as a symbol that takes a filetype- User runs:
argc +.md process file1.md file2.md - Argc sets
argc_filetype=.mdbefore validatingfiles _choice_filesuses${argc_filetype}to filter the find results- Only
.mdfiles are valid choices forfilesargument
The validation cascade:
# With +.md - validates successfully
$ argc --argc-eval script.sh +.md process doc.md
argc_filetype=.md
argc_files=( doc.md )
process doc.md
# With +.sh - validation fails because doc.md not in .sh file list
$ argc --argc-eval script.sh +.sh process doc.md
error: invalid value `doc.md` for `<FILES+>...`
[possible values: script.sh, build.sh, test.sh]
exit 1
Symbols make choice functions dynamic based on user input, enabling complex validation logic through pure declarations.
Task Runner Pattern (Argcfile.sh)
Argc doubles as a task runner - a bash-native alternative to Make. The Argcfile.sh pattern transforms bash functions into discoverable, documented CLI commands.
Basic Setup:
- Create
Argcfile.shin the project root - Define tasks as bash functions with
@cmdtags - Run tasks via
argc <task-name>
Simple Example:
#!/bin/bash
# @cmd build Build the project
build() {
echo "Building..."
}
# @cmd test Run tests
test() {
echo "Testing..."
}
eval "$(argc --argc-eval "$0" "$@")"
Run: argc build or argc test
Task Runner Capabilities:
1. Tasks with arguments:
# @cmd deploy Deploy to environment
# @arg env! Target environment (staging/production)
deploy() {
echo "Deploying to $argc_env"
}
2. Tasks with options:
# @cmd test Run test suite
# @option --filter Test name pattern to match
# @flag --verbose Show detailed output
test() {
local args=()
[[ -n "$argc_filter" ]] && args+=(--filter "$argc_filter")
[[ "$argc_verbose" == "1" ]] && args+=(--verbose)
pytest "${args[@]}"
}
3. Multi-namespace organization for large Argcfiles:
# @cmd docker::build Build docker image
docker::build() {
docker build -t myapp .
}
# @cmd docker::run Run docker container
docker::run() {
docker run myapp
}
# @cmd k8s::deploy Deploy to kubernetes
k8s::deploy() {
kubectl apply -f k8s/
}
Run with: argc docker::build, argc k8s::deploy
4. Environment variable management:
# @cmd connect Connect to database
# @env DATABASE_URL! Database connection string
# @env DATABASE_TIMEOUT=30 Connection timeout in seconds
connect() {
echo "Connecting to $DATABASE_URL with ${DATABASE_TIMEOUT}s timeout"
}
5. Task dependencies (manual orchestration):
# @cmd ci Run full CI pipeline
ci() {
argc lint
argc test
argc build
}
# @cmd lint Run linters
lint() {
echo "Linting..."
}
Why use Argcfile.sh over Make:
- Native bash syntax - full scripting power, no Make DSL
- Automatic help generation and argument validation
- Shell autocompletion for tasks and arguments
- Environment variable integration
- No tab-vs-spaces issues
- Natural parameter passing (no Make variable gymnastics)
Behind the Scenes
Argc is not magic - it's bash code generation all the way down. To understand what argc --argc-eval actually does, look at what argc --argc-build generates: a standalone script with all parsing logic embedded.
The quick-start example (20 lines with comment tags) becomes 280+ lines when built standalone. Here's what argc generates:
Validation function - This is what validates choices from _choice_files():
_argc_validate_choices() {
local render_name="$1" raw_choices="$2" choices item choice concated_choices=""
while IFS= read -r line; do
choices+=("$line")
done <<<"$raw_choices"
for choice in "${choices[@]}"; do
if [[ -z "$concated_choices" ]]; then
concated_choices="$choice"
else
concated_choices="$concated_choices, $choice"
fi
done
for item in "${@:3}"; do
local pass=0 choice
for choice in "${choices[@]}"; do
if [[ "$item" == "$choice" ]]; then
pass=1
fi
done
if [[ $pass -ne 1 ]]; then
_argc_die "error: invalid value \`$item\` for $render_name"$'\n'" [possible values: $concated_choices]"
fi
done
}
This function:
- Takes the output from your choice function (
_choice_files) - Reads it line-by-line into a bash array
- Checks if the user's input matches any valid choice
- If not, generates the error message with all possible values
- Calls
_argc_dieto exit with error
Other generated functions:
_argc_parse()- Main argument parser with case statement for all flags/options_argc_usage()- Help text generator (what you see with--help)_argc_version()- Version display handler_argc_take_args()- Multi-value argument collector_argc_match_positionals()- Positional argument matcher_argc_maybe_flag_option()- Flag/option detection_argc_die()- Error handler and exit_argc_run()- Entry point orchestrator
Key insight: When you run argc --argc-eval, it generates similar code but outputs it as text for eval instead of embedding it in the script. Same logic, different delivery mechanism.
Trade-offs:
- Small script + argc binary: 20 lines +
eval "$(argc --argc-eval ...)"(requires argc installed) - Standalone script: 280+ lines, no dependencies, works anywhere bash works
Full generated code: @references/quick-start-built.sh
Structured schema export:
Argc can also export the parsed CLI structure as JSON via argc --argc-export:
{
"name": "quick-start",
"describe": "Example CLI tool",
"flag_options": [
{ "id": "verbose", "flag": true, "required": false, ... },
{ "id": "file", "flag": false, "num_args": [1, 1], ... }
],
"positionals": [
{ "id": "file", "required": true, "choice": { "type": "Fn", "data": ["_choice_files", true] } }
],
"envs": [
{ "id": "EXAMPLE_FOLDER_PATH", "default": { "value": "examples/" } }
],
"command_fn": "main"
}
This machine-readable format captures everything argc extracted from the comment tags - useful for programmatic access, tooling integration, and AI agent automation.
Full export: @references/quick-start-export.json
Safety Guidelines
Never make argc scripts executable:
Do not use chmod +x on argc scripts. Instead, always execute them via:
argc --argc-run ./script.sh [args]
Or for Argcfile.sh task runners:
argc <task-name>
Note: Detailed explanation for this guideline will be provided separately.
Additional Resources
- @docs/ - Original argc documentation from upstream repository (source of truth)
- @examples/ - Original argc examples from upstream repository
Quick Start Template
- @examples/quick-start.sh
./examples/quick-start.sh --help # View generated help
./examples/quick-start.sh quick-start.sh # Basic usage
./examples/quick-start.sh quick-start.sh --verbose # With verbose flag
./examples/quick-start.sh idontexist.sh # Validation error
EXAMPLE_FOLDER_PATH=/tmp/ ./examples/quick-start.sh claude -v # Override env var