# How Omarchy Handles Arguments Passed to CLI Commands: Group-Command Dispatch Pattern

> Discover how Omarchy handles CLI arguments with its group-command dispatch pattern. Learn how scripts like bin/omarchy-group-command execute with forwarded arguments.

- Repository: [37signals/omarchy](https://github.com/basecamp/omarchy)
- Tags: internals
- Published: 2026-08-29

---

**TLDR:** Omarchy uses a hierarchical dispatcher in `bin/omarchy` that extracts the first two positional arguments as group and command, shifts them from the argument list, and executes the corresponding sub-script (`bin/omarchy-${group}-${cmd}`) with remaining arguments forwarded via `$@`.

Omarchy is Basecamp’s open-source system configuration tool that implements a unique group-command architecture for its command-line interface. Understanding how Omarchy handles arguments passed to CLI commands reveals a lightweight yet extensible pattern where a central dispatcher delegates parsing to specialized sub-scripts. This design keeps the core CLI minimal while allowing individual commands to evolve their own flag sets independently.

## The Group-Command Dispatcher in `bin/omarchy`

The entry point for all Omarchy CLI operations is the dispatcher script located at `bin/omarchy` in the repository root. This script implements a strict two-level namespace where commands are organized into groups, and each combination maps to a specific executable sub-script.

### Extracting Group and Command Identifiers

When the `omarchy` binary is invoked, the script reads the first positional argument (`$1`) as the **group** (e.g., `theme`, `update`, `toggle`) and the second argument (`$2`) as the **command** within that group. These values determine which sub-script handles the request.

### Shifting and Forwarding Arguments

After extracting the group and command, the dispatcher applies `shift 2` to remove these tokens from the argument list. The remaining arguments—whether flags like `-f` or positional values—are preserved in `$@` and forwarded to the target script. The dispatcher constructs the script path using the pattern `"${OMARCHY_PATH}/bin/omarchy-${group}-${cmd}"` and executes it via `exec`, replacing the current process entirely.

```bash
#!/usr/bin/env bash
group=$1
cmd=$2
shift 2

script="${OMARCHY_PATH}/bin/omarchy-${group}-${cmd}"
if [[ -x "$script" ]]; then
  exec "$script" "$@"
else
  echo "Unknown command: $group $cmd"
  exit 1
fi

```

## Sub-Script Argument Parsing Strategy

Because the dispatcher forwards arguments verbatim without interpretation, each sub-script retains full control over its own argument parsing logic. This allows different commands to implement distinct flag sets without modification to the central dispatcher.

### Option Parsing with `getopts`

Command-specific scripts typically use Bash’s built-in `getopts` utility to process short options. For example, the update-related sub-script located at `bin/omarchy-update` implements a standard `while` loop to handle flags:

```bash
#!/usr/bin/env bash
while getopts "fd" opt; do
  case $opt in
    f) FORCE=1 ;;
    d) DRY_RUN=1 ;;
    *) echo "Invalid option"; exit 1 ;;
  esac
done
shift $((OPTIND-1))

# $@ now contains only non-option arguments

```

### Manual Argument Handling

Sub-scripts that do not require flag parsing can access forwarded arguments directly through positional parameters (`$1`, `$2`, etc.) or iterate over `$@`. This approach is common for commands like `toggle` that expect simple on/off values or device identifiers without complex option sets.

## Practical CLI Usage Examples

The hierarchical argument structure enables intuitive command composition where the group and command form a namespace, followed by command-specific options:

```bash

# General pattern: omarchy <group> <command> [options] [arguments]

# Force a system update (parsed by bin/omarchy-update)

omarchy update install -f

# Dry-run update to preview changes

omarchy update install -d

# Toggle a feature (receives arguments directly)

omarchy toggle touchscreen

```

In the `update install` example, the dispatcher extracts `update` and `install`, resolves the script path to `bin/omarchy-update-install` (following the `${group}-${cmd}` convention), and forwards `-f` to that script. The sub-script’s `getopts` loop then processes the force flag accordingly.

## Summary

- **Centralized dispatch**: The `bin/omarchy` script routes all commands by extracting group and command identifiers from the first two positional arguments.
- **Argument isolation**: Using `shift 2`, the dispatcher strips the namespace components and forwards the remaining `$@` array to the target sub-script.
- **Decentralized parsing**: Sub-scripts like `bin/omarchy-update` implement their own `getopts` loops or manual parsing, allowing independent evolution of command-line interfaces.
- **Path convention**: Sub-scripts follow the naming pattern `bin/omarchy-${group}-${cmd}` and must be executable to be invoked by the dispatcher.

## Frequently Asked Questions

### How does Omarchy parse command-line flags?

Omarchy does not parse flags at the top level. Instead, the dispatcher in `bin/omarchy` forwards all arguments after the group and command to the appropriate sub-script. Each sub-script is responsible for its own option parsing, typically using Bash’s `getopts` built-in as demonstrated in `bin/omarchy-update`.

### What happens if I provide an invalid group or command?

If the dispatcher cannot find an executable file at the constructed path `"${OMARCHY_PATH}/bin/omarchy-${group}-${cmd}"`, it outputs "Unknown command: $group $cmd" to stderr and exits with status code 1. No partial matching or fuzzy logic is applied.

### Can Omarchy sub-scripts accept positional arguments?

Yes, sub-scripts receive all forwarded arguments via `$@` after the dispatcher executes `shift 2`. They can access positional arguments directly using `$1`, `$2`, etc., or process them iteratively. The `omarchy toggle` command, for instance, receives device identifiers as positional arguments.

### Where is the main argument dispatch logic located?

The primary dispatch logic resides in `bin/omarchy` at the repository root. This script handles the initial extraction of group and command identifiers, validates the target script’s existence, and manages the `exec` call that transfers control to sub-scripts like `bin/omarchy-update` or `bin/omarchy-toggle`.