# How the Omarchy CLI Router Resolves Commands and Handles Dispatch

> Discover how the Omarchy CLI router resolves commands and dispatches with its dynamic scanning, metadata parsing, and efficient two-phase lookup strategy for faster execution.

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

---

**The Omarchy CLI router dynamically discovers commands by scanning for `omarchy-*` executables, parsing embedded metadata comments to build a routing table, and employing a two-phase resolution strategy that prioritizes fast binary-prefix matching before falling back to full route lookup, ultimately dispatching via `exec` or rendering rich help output.**

The Omarchy CLI router serves as the central dispatch mechanism for the basecamp/omarchy repository, transforming scattered shell scripts into a cohesive command-line interface. Unlike static command registration, this router employs a metadata-driven architecture that automatically discovers new binaries and handles complex routing scenarios including aliases, command groups, and contextual help generation.

## Command Discovery and Metadata Parsing

The router begins by dynamically discovering available commands rather than maintaining a hardcoded list.

### Dynamic Discovery with `load_commands`

In `bin/omarchy`, the `load_commands` function iterates over every executable matching the pattern `omarchy-*` within the same directory. This discovery phase happens at lines 15-18, where each found binary is passed to `register_command` for metadata extraction. This design allows new commands to become available immediately upon being added to the `bin/` directory without modifying the router itself.

### Metadata Extraction in `register_command`

Each command script embeds configuration data using structured comment headers formatted as `# omarchy:<key>=<value>`. The `register_command` function, located at lines 48-99, reads the first approximately 80 comment lines from each binary to extract metadata keys including `group`, `name`, `summary`, `args`, and `aliases`.

As the parser processes each command, it populates two critical lookup tables:

- **`COMMAND_ROUTE`**: Maps command keys to their full route definitions
- **`ROUTE_TO_KEY`**: Maps human-readable route strings back to their internal keys

These mappings enable both fast lookups and reverse lookups throughout the dispatch lifecycle.

## Route Registration and Resolution

Once metadata is parsed, the router constructs multiple entry points for each command.

### Primary and Fallback Route Registration

The `register_route` function (lines 95-103) creates several access paths for every command:

1. **Primary route**: The full command path (e.g., `omarchy theme set`)
2. **Filename fallback**: A route derived directly from the binary name
3. **Alias routes**: Additional shortcuts defined in the metadata (lines 101-108)

This multi-route strategy ensures users can invoke commands through intuitive names while maintaining backward compatibility with script filenames.

### Fast Binary-Prefix Matching

Before consulting the full routing table, `resolve_direct_route` (lines 95-107) attempts an optimization. It joins input arguments with dashes and checks whether a binary named `omarchy-<joined-args>` exists directly. If found, the router stores the binary path in `DIRECT_RESOLVED_PATH` and the number of consumed arguments in `DIRECT_RESOLVED_CONSUMED`, bypassing the heavier routing table lookup entirely.

### Full Routing Table Lookup

When the fast path fails, `resolve_route` (lines 161-176) performs a comprehensive search. It walks the argument list from longest to shortest, checking each prefix against the `ROUTE_TO_KEY` mapping. The first match becomes `RESOLVED_KEY`, allowing commands to be invoked with partial arguments when unambiguous.

## Dispatch Logic and Help Handling

The router prioritizes help generation over execution when appropriate, creating a self-documenting interface.

### The Fast Path: `dispatch_fast_or_help`

Located at lines 145-190, `dispatch_fast_or_help` processes the result of `resolve_direct_route`. This function implements a "help-first" philosophy:

- If remaining arguments contain `--help` or `-h`, it loads command metadata via `load_command_by_binary` and renders detailed help via `show_command_help`, or JSON output via `show_command_json`
- If no arguments remain but the command requires them, it displays help instead of executing
- Otherwise, it immediately `exec`s the resolved binary with leftover arguments

### Fallback Dispatch with `dispatch_or_help`

When full routing is required, `dispatch_or_help` (lines 210-267) handles the complexity. After `resolve_route` identifies a match, it applies the same help-detection logic. If the resolved key represents a command *group* (e.g., `omarchy theme` without a subcommand), the router lists available commands in that group rather than failing. If no route matches, it invokes `suggest_command` to offer closest matches before exiting with an error.

## Group Resolution and Command Execution

The router distinguishes between executable commands and organizational groups.

### Group Validation Utilities

Functions `group_exists`, `group_has_binary_commands`, and `group_has_child_commands` (lines 84-96) enable the router to differentiate between a parent group container and a concrete executable command. When a user invokes only a group name, the router queries these utilities to determine whether to display the group's command list or proceed with execution.

### Process Replacement via `exec`

Finally, after all resolution and validation checks complete, the router executes the target binary at line 224 using:

```bash
exec "$binary_path" "${remaining[@]}"

```

This `exec` call replaces the current shell process with the target command, preserving exit status and process integrity while passing through any unconsumed arguments.

## Summary

- **Dynamic discovery**: The router scans `bin/` for `omarchy-*` executables at startup, requiring no manual registration
- **Metadata-driven**: Commands self-document via `# omarchy:<key>=<value>` comments parsed by `register_command`

- **Two-phase resolution**: Fast binary-prefix matching (`resolve_direct_route`) precedes full table lookup (`resolve_route`)
- **Help-first design**: Both dispatch paths check for `--help`, `--json`, and missing required arguments before execution
- **Group awareness**: The router validates groups separately from commands, showing command lists when group names are invoked alone
- **Clean execution**: All valid commands ultimately dispatch via `exec` to replace the router process entirely

## Frequently Asked Questions

### How does Omarchy discover new commands without configuration files?

The `load_commands` function in `bin/omarchy` (lines 15-18) scans the `bin/` directory for any executable matching `omarchy-*` and automatically registers them by parsing their embedded metadata comments. This allows adding new commands simply by dropping scripts into the directory.

### What is the priority order for route resolution in the Omarchy CLI?

Resolution occurs in two phases: first, `resolve_direct_route` checks if the arguments form a valid binary name directly; second, `resolve_route` walks the argument list from longest to shortest against the `ROUTE_TO_KEY` table. The fast path prioritizes direct binary matches, while the fallback allows partial command matching.

### How does the router handle ambiguous or misspelled commands?

When `dispatch_or_help` fails to find a matching route, it invokes `suggest_command` to calculate and display the closest matching valid commands. If the user invokes only a group name like `omarchy theme`, the router lists all available subcommands rather than executing.

### Can custom aliases be defined for Omarchy commands?

Yes, aliases are defined within command scripts using the `# omarchy:aliases=<alias1>,<alias2>` metadata format. The `register_command` function (lines 101-108) processes these values and registers additional routes pointing to the same command key, enabling shortcuts like `omarchy up` for `omarchy update`.