Multi-Repository Application Commands
The frg app commands run workflow commands for an application that spans several repositories. You run them from a parent directory that contains .forge/config/application.toml, and Forge either makes one AI invocation with the context of all repositories or runs a workflow in each repository.
Prerequisites
- Forge installed with a valid license.
- A parent directory with
.forge/config/application.toml,agents.toml, andtoolchain.toml. - Child repositories below the parent directory, each with a
.forge/config/project.toml. - An issue or ticket context:
frg config set-issue <id>orfrg config set-ticket <id>.
Set up an application
1. Create the application configuration
In the parent directory, run:
frg config init --app
To create toolchain.toml and agents.toml at the same time, combine --app with --issues, --tickets, --scanner, --code, --agent, and --models:
frg config init --app --agent claude_code --issues github_issues --code github
--force can't be combined with --app.
Forge prints the application ID and the line to add to each repository:
Application ID: 32d892fe-e2a8-41b8-aa7d-d018c4942d99
To include repositories in this application, add the following
to each repository's project.toml:
[project]
allow_app = "32d892fe-e2a8-41b8-aa7d-d018c4942d99"
Then run frg app discover to find all opted-in repositories.
2. Opt in each repository
Add allow_app with the application ID to the [project] table of each child repository's .forge/config/project.toml:
[project]
# ...
allow_app = "32d892fe-e2a8-41b8-aa7d-d018c4942d99"
To generate project.toml files automatically, use frg app discover --auto.
3. Discover repositories
frg app discover
Forge searches for project.toml files with a matching allow_app and saves the results to the [[repositories]] section of application.toml:
✓ Discovered 2 repositories and saved to application.toml:
frontend (frontend)
backend (backend)
If repositories are already configured, Forge lists them and makes no changes. To replace the list, run frg app discover --force.
Generate project configuration automatically
frg app discover --auto creates project.toml for every discovered child repository that doesn't have one. Forge analyzes each repository with AI to fill in metadata such as framework, test framework, and package manager, and sets allow_app to the application ID. Repositories that already have project.toml are skipped.
Before it starts, Forge shows a warning and asks for confirmation (abbreviated):
AI AUTOMATION WARNING
AI analysis will be used to populate project.toml files.
AI hallucinations in project.toml may cause:
Incorrect framework detection
Wrong testing framework configuration
Invalid package manager settings
Quality issues in dependent workflows
Generated configurations should be reviewed after completion.
Do you want to proceed? [y/N]:
In CI or other non-interactive environments, accept the warning with an environment variable:
FORGE_ACCEPT_AUTO_RISK=true frg app discover --auto
Review the generated files. To correct one repository, run frg config init --interactive in it.
application.toml reference
A file created by frg config init --app and frg app discover:
schema_version = "1.0.0"
[application]
id = "32d892fe-e2a8-41b8-aa7d-d018c4942d99"
name = "shop"
description = "Multi-repository application: shop"
[discovery]
enabled = true
max_depth = 5
exclude = ["node_modules", "target", ".git", "vendor", "dist", "build"]
[[repositories]]
name = "frontend"
path = "frontend"
[[repositories]]
name = "backend"
path = "backend"
| Key | Description | Default |
|---|---|---|
schema_version | Configuration schema version. | "1.0.0" |
[application] id | Application UUID. Child repositories reference it in allow_app. | Generated |
[application] name | Application name. | Directory name |
[application] description | Application description. | Generated |
[discovery] enabled | Enables repository discovery. | true |
[discovery] max_depth | How many directory levels discovery searches. Must be at least 1. | 5 |
[discovery] exclude | Directory names to skip. Patterns can't be empty. | node_modules, target, .git, vendor, dist, build |
[[repositories]] name, path | Repositories in the application. Written by frg app discover. | — |
[external_sources] enabled | Enables external configuration sources. Individual toggles (agents, toolchain, commands, workflows, telemetry) have no effect when enabled = false. | — |
Note: Child workflows inherit the application's
external_sourcessetting. SetFORGE_DISABLE_EXTERNAL_CONFIGSto turn off external configuration sources.
Run application commands
frg app <target> <operation> [OPTIONS]
| 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 |
A typical feature flow:
cd /path/to/app-parent
frg config set-issue 374
frg app issue draft # One issue for the whole feature
frg app issue plan # Plan in each repository
frg app code create # Implement in each repository
frg app code review # Review the feature across repositories
frg app pr draft # Create and describe a PR in each repository
frg app pr review # Review, approve, and merge in each repository
Forge propagates the issue or ticket context to each repository's .forge/context.toml. Each repository gets its own pr_id when its pull request is created.
Execution modes
The app_workflow setting of an operation in commands.toml selects the mode:
- Direct execution (default, no
app_workflow): one AI invocation with the context of all repositories. - Workflow execution (
app_workflowset): Forge runs the named workflow withfrg workflow runin each child repository.
Built-in operations with workflow execution:
| Command | Workflow |
|---|---|
frg app issue plan | issue-to-plan |
frg app code create | code-to-review |
frg app pr draft, frg app pr publish | pr-create-publish |
frg app pr review, frg app pr merge | pr-review-merge |
All other frg app commands use direct execution.
Note: Child workflows run quietly.
Options in direct execution
Direct-execution commands accept the options of the corresponding workflow command, such as --clear, --retry, --auto-retry, --agent, --role, --custom-agent, --model, --print-prompt, --context-file, --context-dir, --edit, and --bypass-safety-checks. Check frg app <target> <operation> --help.
--lite isn't supported for frg app commands:
The --lite flag is not supported for `frg app` commands. Multi-repository operations require detailed analysis for consistency. Use standard mode instead.
Options in workflow execution
| Option | Description |
|---|---|
-q, --quiet | Suppress all output except errors. |
--start-from <STEP_NAME> | Start the workflow from a specific state instead of the initial state. |
--bypass-approvals | Skip approval gates (for CI/automation). |
--dry-run | Validate the workflow without executing it. |
Workflow execution rejects --model, --agent, --role, --context-file, --clear, --edit, --fix, and --lite, even where a command's help lists a modifier. The workflow configuration defines the agent, model, role, and fix behavior. For example:
--model is not supported for workflow execution. Workflow execution uses `frg workflow run` which does not accept model overrides. Model selection is defined in the workflow configuration.
Parallel execution
Workflow execution mode runs in parallel across repositories (default: 3 concurrent workers).
Configure in application.toml:
[application]
max_workers = 5 # Run in up to 5 repositories concurrently
--max-workers controls how many repositories run concurrently; it no longer affects how quickly
completion is detected once a workflow finishes (see below).
Monitoring and Completion Detection
While a workflow executes in each child repository, the parent frg app process polls each child's
.forge/work/job_<issue-id>/logs/transaction.jsonl every 3 seconds. This transaction log is the single
source of truth — it is exactly what frg workflow status reads inside the child repository, so the
parent's view and the child's own view agree whenever the parent reaches a terminal state within its
detection bound. If the parent instead reports Unknown (see below), no terminal transition was found
in that bound; frg workflow status run inside the child repository remains the authoritative live
view of what the workflow is actually doing.
Detection bound: once a child's terminal state is durably written to its transaction log, the parent reflects that state within:
- ~4 seconds in the normal case (one poll interval plus a 1-second terminal check), or
- ~19 seconds worst case, if the child's own process has already exited before the parent's next poll (a bounded reconciliation window of 5 additional polls).
The parent never waits indefinitely. Every repository ends the run in exactly one of four states:
| State | Meaning |
|---|---|
Done | The workflow completed successfully. |
Failed | The workflow ended with an error. |
Needs Approval | The workflow reached an approval gate. When frg app is attached to an interactive terminal, it can be approved or rejected from the parent's own approvals console (see Resolving approval gates below); otherwise, run frg workflow approve inside that child repository, then re-run the frg app command. |
Unknown | No terminal transition was found in the child's transaction log within the detection bound. Run frg workflow status inside the named child repository to inspect its own view directly. |
Finding Command Output
| Execution mode | Where artifacts are written |
|---|---|
| Direct | Parent application directory: .forge/work/job_<id>/ |
| Workflow | Each child repository: .forge/work/job_<id>/ inside that repository |
After every frg app run, the parent job directory also contains:
app_index.toml— machine-readable artifact pointersapp_index.md— human-readable summary with repository outcomes and paths
Example repository table:
| Repository | Outcome | Job directory | Artifacts | Logs |
| api-service | Done | `../api-service/.forge/work/job_1225` | 4 | `../api-service/.forge/work/job_1225/logs/system.log` |
Read-only: The index contains paths only. Artifacts are owned by the repository that produced them — run
frg issue plan --edit(and every other edit command) from inside that repository. Editing anything in the parent folder has no effect on the workflow.
Use frg app status to re-render artifact locations later:
frg app status
frg app status --job 1225
frg app status --paths
frg app status also works for job directories created before the index existed; when no recorded
index is present it falls back to scanning the repositories listed in application.toml.
To investigate a failed child repository, open the system.log and transaction.jsonl paths shown
in the index, or run frg workflow status inside that repository.
Known limitation: two simultaneous frg app runs for the same issue overwrite each other's index
(last writer wins). Each index records run_id and generated_at, so the winning run is still
identifiable.
Resolving approval gates
Each repository's approval gate is independent: its own code, message and timeout_seconds. When
frg app runs in an interactive terminal, a repository blocked at a gate shows its code inline in
the status table (⏸ Needs Approval (code ABCD1234)), and the parent process arms a small
line-oriented console the moment at least one repository is pending — no second terminal required.
| Command | Effect |
|---|---|
l | List pending repositories with codes and waiting time |
d <sel> | Show the gate message and code for the selection |
a <sel> | Approve the selection |
r <sel> [reason] | Reject the selection with an optional shared reason |
w | Close the console; keep polling (out-of-band only) |
h | Help |
<sel> accepts: omitted (only when exactly one repository is pending), a single index (2), a
list (1,3), a range (2-4), a repository name, or all.
Selecting more than one repository previews the batch and asks for y/N confirmation before
anything is submitted. A batch collects one approval code (for single-target actions) or skips
straight to email (batches use each repository's own discovered code) and one email address for
the whole selection, then reports the outcome per repository — approved, rejected,
skipped (already resolved), skipped (not pending) or failed — so a partial failure in one
repository is never hidden by another repository's success.
Every resolution, whether typed into this console or run out-of-band, goes through the same
frg workflow approve / frg workflow reject validation and audit trail, so the two paths can be
mixed freely: if another session (or a second terminal) resolves a gate first, the console reports
it as resolved elsewhere on its next poll and simply continues.
Per-gate timeout_seconds keeps running while the console is open; a repository that times out
leaves Needs Approval on its own, exactly as it would with no console attached.
The console never arms — and behaves exactly as before this feature — when frg app is not
attached to an interactive terminal (piped output, --quiet, CI, or
FORGE_DISABLE_INLINE_APPROVAL=1). In those cases, resolve gates with frg workflow approve /
frg workflow reject from inside the child repository as usual.
See tests/manual_app_inline_approval.md in the repository for manual test procedures covering
concurrent gates, batches, out-of-band races, and interrupt/timeout behavior.
PR commands and --start-from
frg app pr draft and frg app pr publish run the same pr-create-publish workflow. It checks whether the repository already has a pr_id in .forge/context.toml, creates a pull request only if needed, and then drafts and publishes the description. Running both commands doesn't create duplicate pull requests.
frg app pr review and frg app pr merge run the same pr-review-merge workflow. It monitors the pull request, reviews it, waits for approval, and merges it. You can re-run either command safely.
Use --start-from with a state name to skip ahead:
# pr-create-publish
frg app pr draft --start-from pr_draft
frg app pr publish --start-from pr_publish
# pr-review-merge
frg app pr merge --start-from await_pr_approval
frg app pr merge --start-from pr_merge
To list a workflow's state names, run frg workflow run <workflow> --list-states. See Workflows.
How agents organize multi-repository output
frg app prompts include an <application> block that lists the repositories. The agent adapts to the architecture it detects:
| Context | Architecture | How work is split |
|---|---|---|
<application> block | Multi-repository | By repository |
<service> block | Monorepo | By service |
| Neither | Single project | By feature or component |
For example, frg app issue split names output files by a slug of each repository path: Forge takes the final directory name, converts it to lowercase, and replaces . and _ with -. A repository at /opt/my.app.web produces issue_my-app-web.md. In a single project, files are named issue_a.md, issue_b.md, and so on.
During workflow execution, Forge propagates the matching issue file to each repository's job directory.
Troubleshooting
No application.toml found. Run 'frg config init --app' to create one.
Cause: The current directory and its parents have no .forge/config/application.toml.
Solution: Run the command from the application's parent directory, or create the file with frg config init --app.
No repositories defined in application.toml. Run 'frg app discover' to scan for repositories or manually add them.
Cause: The [[repositories]] list is empty.
Solution:
- Add
allow_appto each child repository'sproject.toml. - Run
frg app discover.
No valid repositories available. Check application.toml and ensure repositories exist with correct allow_app UUID.
Cause: The listed repository paths don't exist, or their allow_app doesn't match [application] id.
Solution:
- Compare each repository's
allow_appwith the application ID. - Check the
pathvalues inapplication.toml. - Run
frg app discover --forceto rebuild the list.
No issue or ticket context set.
Cause: frg app commands need an issue or ticket context.
Solution: Run frg config set-issue <id> or frg config set-ticket <id> in the parent directory.
agents.toml not found. Application execution requires agent configuration.
Cause: The parent directory has no agent configuration. The same applies to toolchain.toml not found. Application execution requires toolchain configuration.
Solution: Run frg config init --agent <agent> or frg config init --issues <tool> --code <tool> in the parent directory.
Cannot run in non-interactive mode. Set FORGE_ACCEPT_AUTO_RISK=true to bypass the warning prompt.
Cause: frg app discover --auto can't show its confirmation prompt, for example in CI.
Solution: Set FORGE_ACCEPT_AUTO_RISK=true for the command, and review the generated files afterward.
Warning: Repository discovery took <n>s. Consider reducing max_depth or adding exclusions.
Cause: Discovery searches a large directory tree.
Solution: Lower [discovery] max_depth, or add large directories to [discovery] exclude.