# How Custom Commands Work in Forge: A Complete Guide to Prompt Shortcuts

> Learn how custom commands in Forge work as reusable prompt templates. Define CLI shortcuts for repetitive prompts and streamline your workflow with this complete guide.

- Repository: [Forge Code/forgecode](https://github.com/antinomyhq/forgecode)
- Tags: how-to-guide
- Published: 2026-04-08

---

**Forge treats custom commands as reusable prompt templates stored in Markdown files that you can invoke via CLI shortcuts instead of typing repetitive prompts.**

Custom commands in Forge allow you to define **named shortcuts** for frequently used AI prompts. According to the antinomyhq/forgecode source code, these commands are auto-discovered at runtime from specific directories, parsed using front-matter metadata, and executed through a dedicated CLI interface.

## How the Command Loading System Works

The `CommandLoaderService` in [`crates/forge_services/src/command.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_services/src/command.rs) handles the discovery and initialization of custom commands. This service runs automatically when Forge starts, scanning for `*.md` files in two specific locations.

### Directory Structure and Discovery

Forge searches for command definitions in the following order of priority:

1. **Global directory**: `$HOME/.forge/commands` (user-wide commands)
2. **Local directory**: `.forge/commands` inside the current working directory (project-specific commands)

The loader's `init()` method first loads built-in commands via `init_default()`, then reads the global directory using `infra.get_environment().command_path()`, followed by the local directory via `command_path_local()`. Each file is processed through `parse_command_file` using **gray-matter** for front-matter parsing and deserialization.

### Parsing and Conflict Resolution

When multiple commands share the same name, Forge resolves conflicts using a **last-write-wins** strategy. Local commands override global commands, which in turn override built-ins. This hierarchy ensures that project-specific definitions take precedence while maintaining fallback options.

The parsed data constructs a `UserCommand` struct defined in [`crates/forge_domain/src/event.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/event.rs). This struct contains:
- A unique command name
- A `Template<Value>` for the prompt body
- Optional parameter placeholders for dynamic injection

## Creating Your First Custom Command

To define a custom command, create a Markdown file with YAML front-matter in either the global or local commands directory.

```markdown
---
name: summarize-pr
description: Summarize a Pull Request with context
---
You are a helpful code reviewer.

Summarize the following PR description in a concise bullet list:

{{ .prompt }}

```

The front-matter fields `name` and `description` become the command's metadata in the CLI. The body acts as a **template engine**, where `{{ .prompt }}` serves as a placeholder that gets replaced by arguments passed during invocation. You can define multiple parameter placeholders depending on your template requirements.

## Invoking Custom Commands from the CLI

The `Cmd` sub-command group in [`crates/forge_main/src/cli.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_main/src/cli.rs) exposes two primary actions for managing custom commands: listing available commands and executing them.

### Listing Available Commands

View all loaded custom commands using:

```bash
forge cmd list --custom

```

Internally, this triggers `on_show_custom_commands` in [`crates/forge_main/src/ui.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_main/src/ui.rs), which fetches commands via `self.command_manager.list_custom()` and renders a formatted table displaying each command's name and description.

### Executing Custom Commands

Invoke a custom command by passing its name and arguments:

```bash
forge cmd execute summarize-pr "Add feature X: fixes Y, improves Z."

```

The CLI handler joins supplied arguments, ensures the string starts with a slash, and parses it into a `SlashCommand::Custom` variant. The `event.name` identifies the command while `event.parameters` inject values into the stored template. The final rendered prompt is then sent to your configured LLM provider.

## Keyboard Shortcuts and ZSH Integration

Forge includes ZSH integration for displaying keyboard shortcuts via `run_zsh_keyboard()` in [`crates/forge_main/src/zsh/plugin.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_main/src/zsh/plugin.rs). When you run `forge keyboard` or use bound key sequences, the system streams the [`keyboard.zsh`](https://github.com/antinomyhq/forgecode/blob/main/keyboard.zsh) script and prints available shortcuts.

The shortcut table is generated by `Info::from(&ForgeCommandManager)` in [`crates/forge_main/src/info.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_main/src/info.rs), which adds a "KEYBOARD SHORTCUTS" section with platform-specific bindings such as `<ALT+ENTER>` on Linux/Windows and `<OPT+ENTER>` on macOS for multiline input.

## Summary

- **Custom commands** are Markdown files with YAML front-matter stored in `$HOME/.forge/commands` (global) or `.forge/commands` (local).
- The `CommandLoaderService` auto-discovers and parses commands at startup using gray-matter, with local commands overriding global ones.
- The `UserCommand` struct represents commands in memory, holding templates that support parameter injection via `{{ .parameter }}` syntax.
- Use `forge cmd list --custom` to view available commands and `forge cmd execute <name> <args>` to run them.
- ZSH integration provides keyboard shortcut discovery through `forge keyboard` and platform-specific key bindings.

## Frequently Asked Questions

### Where should I store custom commands in Forge?

Store global commands in `$HOME/.forge/commands` and project-specific commands in `.forge/commands` within your working directory. The local directory takes precedence if command names conflict.

### How do I pass arguments to a custom command?

Arguments passed to `forge cmd execute` are injected into template placeholders like `{{ .prompt }}` or custom named parameters you define in the Markdown body. The `event.parameters` field handles the runtime substitution before sending to the LLM.

### Can local commands override global commands?

Yes. The loading mechanism in [`crates/forge_services/src/command.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_services/src/command.rs) resolves conflicts by keeping the last command found, meaning local (CWD) commands override global (`$HOME`) commands, which override built-in defaults.

### How do I view available keyboard shortcuts?

Run `forge keyboard` to display the shortcut table, or check the "KEYBOARD SHORTCUTS" section in the interactive UI generated by `Info::from(&ForgeCommandManager)`. Shortcuts include multiline input triggers like `<ALT+ENTER>` on Linux/Windows and `<OPT+ENTER>` on macOS.