Command Customization
You customize workflow commands in .forge/config/commands.toml. You can replace the role or task prompt of a built-in operation, add operations or entire command targets, and attach context or hooks. This page covers the common customizations; for every field, see the Configuration Reference.
Prerequisites
- Forge is initialized in the repository (
.forge/config/exists). - For custom prompts: prompt files you write, or prompts exported with
frg config export --prompts.
Quick start
- Export a commented template:
frg config export --commands
-
Uncomment and edit sections in
.forge/config/commands.toml. -
Validate your changes:
frg config validate
- Optional: lock the configuration.
frg config lock
frg config lock validates all configuration files and writes .forge/.config_lock with the SHA-256 hash of each file. If a configuration file changes after locking, workflow commands are blocked until you run frg config lock again.
Override prompts for a built-in operation
Use [[prompt_overrides]] to change the role or task prompt of an existing operation:
[[prompt_overrides]]
target = "issue"
operation = "draft"
role = "scrum_master"
[[prompt_overrides]]
target = "code"
operation = "review"
role = "senior_reviewer"
task = "thorough_code_review"
Example: Disable role prompting for one operation
[[prompt_overrides]]
target = "issue"
operation = "draft"
role = "none" # Reserved value - no role prompt is composed
none is a reserved value, not a prompt file name. frg never looks for
none.role.xml / none.role.md; the role section is omitted from the composed
prompt entirely, including its separator, which reduces input and cached token cost.
Requirements:
targetmust be a built-in target (issue, code, pr, etc.)operationmust exist under that target- At least one of
roleortaskmust be specified - Referenced prompts must exist in
.forge/prompts/or be built-in rolemay be a custom role prompt name or the reserved value"none"to disable role prompting
Disabling role prompting
Role prompts give the AI a persona. Disabling them reduces input and cached token cost and can be preferable with newer models where role framing adds little value.
There are three ways to disable role prompting:
| Mechanism | Scope | Example |
|---|---|---|
--role none | One invocation | frg issue draft --role none |
role = "none" in [[prompt_overrides]] | One target/operation | see above |
roles = "disabled" in commands.toml | Every operation | see below |
# .forge/config/commands.toml
roles = "disabled"
Precedence
Highest to lowest:
- The
--role <name>command-line flag (--role nonedisables). - A per-operation role from
[[prompt_overrides]],[[custom_commands]]or[[commands]]. - The global
roles = "disabled"setting. - The built-in role for the operation.
An explicit per-operation role therefore overrides a global roles = "disabled":
roles = "disabled" # no role prompt anywhere ...
[[prompt_overrides]]
target = "code"
operation = "review"
role = "security_analyst" # ... except for `frg code review`
Notes and limits
noneis the only reserved value.off,disableandnilremain invalid role names and are rejected with the usualInvalid roleerror, so a typo can never silently disable role prompting.--roleand--custom-agentremain mutually exclusive.--role noneis not a way around that rule. Custom agents already skip the role prompt, so combining a custom agent with a disabled role produces exactly the same prompt.frg workflow rundoes not accept--roleat all, but configuration-level settings (roles = "disabled"androle = "none") do apply to it.- Resumed (delta) sessions already omit the role prompt; disabling roles changes nothing for them.
- The global setting is read from
commands.tomlat the system, user and repository levels using the normal configuration hierarchy. There is no per-service (monorepo) role switch.
Built-in targets and operations:
| Target | Operations |
|---|---|
issue | draft, publish, review, plan, triage, split, ask |
backlog | create, review, rank, ask |
code | create, review, refactor, document, build, test, ask |
pr | draft, publish, review, merge, ask |
docs | create, build, ask |
ticket | triage, respond, ask, correlate |
sec | audit, patch, ask |
ask | default |
run | default |
Note:
frg config validatedoesn't report a misspelledtargetoroperationin[[prompt_overrides]]. Compare the names with the table above.
Every referenced prompt must exist. See Create custom prompts.
Add custom commands
Use [[custom_commands]] to add an operation to a built-in target or to define a new target. Forge adds the command to its CLI, with the standard workflow flags (--clear, --retry, --auto-retry, --agent, --role, --custom-agent, --model, --print-prompt, --context-file, --context-dir, --bypass-safety-checks).
| Field | Required | Description |
|---|---|---|
target | Yes | Existing or new target name. |
operation | Yes | Operation name. |
role | Yes | Role prompt name. |
task | Yes | Task prompt name. |
description | No | Text shown in --help. |
Add an operation to a built-in target
[[custom_commands]]
target = "issue"
operation = "estimate"
role = "product_manager"
task = "story_point_estimate"
description = "Estimate issue using story points"
This adds frg issue estimate.
Add a new target
The following user-defined example adds a deployment target. deployment isn't a built-in Forge command; it exists only when your commands.toml defines it.
[[custom_commands]]
target = "deployment"
operation = "plan"
role = "devops_engineer"
task = "deployment_plan"
description = "Plan a deployment"
After you add the prompt files, frg deployment --help lists it as Custom target: deployment, and you run it with frg deployment plan.
Add a modifier flag
A modifier adds a flag that switches the command to a different task prompt:
[[custom_commands]]
target = "deployment"
operation = "execute"
role = "devops_engineer"
task = "deployment_execute"
description = "Execute a deployment"
[[custom_commands.modifiers]]
name = "dry_run"
task_override = "deployment_execute_dryrun"
description = "Simulate deployment without changes"
Forge uses the modifier name as the flag name: frg deployment execute --dry_run.
Reserved targets
You can't define commands for these targets: config, guide, shell, status, workflow, app, learn, and diagnose. frg config validate reports Cannot define workflow for reserved target '<name>'.
Extend operations with the full schema
Use [[commands]] when you need context, hooks, flags, options, or multi-repository settings. Forge also accepts [[workflows]] as an alias.
A partial entry extends a built-in operation. Fields you omit keep their built-in values, and context you add is appended to the built-in context:
[[commands]]
target = "issue"
[[commands.operations]]
name = "draft"
[[commands.operations.context]]
type = "file"
path = "docs/issue-template.md"
description = "Standard issue template"
A complete entry defines a new target with its own prompts, flags, and modifiers:
schema_version = "2.1.0"
[metadata]
name = "Release commands"
version = "1.0.0"
author = "platform-team"
[[commands]]
target = "release"
description = "Release operations"
[[commands.operations]]
name = "plan"
description = "Plan a release"
flags = ["role", "custom_agent"]
[commands.operations.prompts]
role = "software_engineer"
task = "release_plan"
[[commands.operations.modifiers]]
name = "dry_run"
description = "Simulate the release"
task_override = "release_plan_dryrun"
Other operation keys include hooks (see Command Hooks), options, and app_workflow (see Multi-Repository Application Commands). For context entries, see Using Context Effectively.
Create custom prompts
- Export built-in prompts as a starting point:
frg config export --prompts --target issue
Forge writes the prompts in both formats, organized by target:
.forge/prompts/
├── xml/
│ ├── role/product_manager.role.xml
│ └── issue/
│ ├── issue_draft.task.xml
│ └── issue_draft_delta.task.xml
└── markdown/
├── role/product_manager.role.md
└── issue/
├── issue_draft.task.md
└── issue_draft_delta.task.md
Files with a _delta suffix are used when you run the same command again in a session. See Session Context and Token Optimization.
-
Save your prompt as
.forge/prompts/<name>.role.xmlor.forge/prompts/<name>.task.xml. For a modifier, create the prompt named intask_override. -
Reference the prompt by name, without the suffix:
[[prompt_overrides]]
target = "issue"
operation = "draft"
role = "custom_role" # .forge/prompts/custom_role.role.xml
task = "custom_issue_draft" # .forge/prompts/custom_issue_draft.task.xml
- Run
frg config validate.
Forge uses a prompt file only when your configuration references it, so stray files in .forge/prompts/ don't change built-in behavior. For prompt format selection, see Prompt Formats.
Troubleshooting
Role prompt <name> not found or Task prompt <name> not found
Cause: The configuration references a prompt that Forge can't find. The error lists the paths Forge searched and suggests a file to create.
Solution:
- Create the file at the suggested path, for example
.forge/prompts/<name>.task.xml. - Or remove the reference from
commands.toml. - Run
frg config validate.
Modifier task prompt <name> not found
Cause: A modifier's task_override references a missing prompt.
Solution: Create .forge/prompts/<name>.task.xml, then run frg config validate.
Prompt override for <target>.<operation> must specify at least 'role' or 'task'
Cause: A [[prompt_overrides]] entry has neither role nor task.
Solution: Add role, task, or both, or remove the entry.
Cannot define workflow for reserved target <name>
Cause: A custom command uses a reserved target name.
Solution: Rename the target. See Reserved targets.
Configuration has changed since last lock.
Cause: You edited a configuration file after running frg config lock.
Solution:
- Run
frg config validate. - Run
frg config lockto update the lockfile.
Changes don't take effect
Cause: The file is in the wrong location, or a target or operation name is misspelled.
Solution:
- Check that the file is
.forge/config/commands.toml. - Run
frg config validate. - Run
frg <target> --helpand confirm that the operation is listed.