# Forge

> Orchestrate a full development pipeline using MCP-driven subagents. Accepts plain text or issue tracker URLs (GitHub, Jira, Linear, etc.) as input.

- Skill: `hiromaily/forge` (Agent Skill)
- Install (CLI): `npx skillmds@latest add hiromaily/forge`
- Raw SKILL.md: https://api.skillmd.com/api/skills/hiromaily/forge/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: hiromaily (https://skillmd.com/u/hiromaily)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/hiromaily/forge

---


# claude-forge Orchestrator

## Step 1: Initialize or Resume

**To resume a suspended pipeline**, supply the spec directory name from `.specs/`:

Example: `/forge 20260401-effort-only-flow`

1. Call `mcp__forge-state__pipeline_init(arguments=$ARGUMENTS)`.
2. If `result.errors` is non-empty: surface the errors to the user and stop.
3. If `result.resume_mode` is `"auto"`: the input matched an existing spec directory.
   **Do not ask for confirmation** — proceed directly to Step 2.
4. For new pipelines (`resume_mode` is absent):
   a. If `result.fetch_needed` is non-null:
      1. Fetch the external data using the method specified in `fetch_needed`:
         - If `fetch_needed.mcp_tool` is set: call the MCP tool with `fetch_needed.mcp_params`.
         - Else if `fetch_needed.command` is set: execute the command via Bash and parse the JSON output.
         - Else: follow `fetch_needed.instruction` as a fallback guide.
      2. Map the response fields to `external_context` using `fetch_needed.response_mapping`:
         for each entry `(response_key → context_key)`, set `external_context[context_key] = response[response_key]`.
      3. Call `mcp__forge-state__pipeline_init_with_context(workspace=result.workspace, source_id=result.source_id, source_url=result.source_url, flags=result.flags, external_context=<mapped data>)`.
         (`task_text` is not applicable for external issue sources — do not pass it.)
   b. If `result.fetch_needed` is null (plain text input): call
      `mcp__forge-state__pipeline_init_with_context(workspace=result.workspace, flags=result.flags, task_text=result.core_text)`.
      (`result.core_text` is a top-level field in the `pipeline_init` response — the task text with bare flags stripped.)

   **After the first `pipeline_init_with_context` call**, check which field is present in the response:

   **If `result.needs_discussion` is non-null** (triggered only when `--discuss` flag is present,
   source is plain text, and `--auto` is absent):
   1. Present each question in `result.needs_discussion.questions` to the user via AskUserQuestion
      (single prompt listing all questions).
   2. Collect the user's answers as a single freeform string
      (e.g. `"Q1: <answer>\nQ2: <answer>\nQ3: <answer>"`).
   3. Call `mcp__forge-state__pipeline_init_with_context` again (the discussion call) with:
      `workspace=<same>, flags=<same>, task_text=<same>, discussion_answers=<collected answers string>`.
      The response will contain `needs_user_confirmation` where
      `needs_user_confirmation.enriched_request_body` is non-empty.
   4. Proceed with the effort/branch confirmation step below using this `needs_user_confirmation`.
      When calling `pipeline_init_with_context` with `user_confirmation`, pass back:
      `user_confirmation={effort: <confirmed>, workspace_slug: <slug>, use_current_branch: <bool>, enriched_request_body: needs_user_confirmation.enriched_request_body}`.
      This carries the enriched `request.md` body forward to workspace initialisation.

   **If `result.needs_user_confirmation` is present directly** (`--discuss` absent, `--auto` set,
   or GitHub/Jira source): proceed with the effort/branch confirmation step below
   (no `enriched_request_body` to pass back).

   **Effort/branch confirmation step** (applies after either path above):
   Present **all** of the following to the user in a **single prompt** (use AskUserQuestion
   with multiple questions):
   1. **Effort level**: present all three effort options from `effort_options`
      (S, M, L — each with `skipped_phases` using the `label` field).
      Each option has a `recommended` boolean — mark the one where
      `recommended` is `true` as "(Recommended)".
      If `needs_user_confirmation.estimate_display` is non-empty, output it **verbatim**
      alongside the options so the user can gauge how heavy the run will be (effort L can
      be multi-hour and multi-dollar). The server pre-formats the P50/P90 figures — do not
      reconstruct them from the raw `estimate` struct.
   2. **Branch decision**: based on `current_branch` and `is_main_branch` from the response:
      - If `is_main_branch` is true: inform the user a new branch will be created (no question needed).
      - If `is_main_branch` is false: ask whether to use the current branch or create a new one.
   While waiting, generate a concise English slug (3–6 words, lowercase, hyphen-separated,
   ASCII only) that summarises the task — e.g. `"add-user-auth-endpoint"` or
   `"fix-report-export-timeout"`. If the input is in a non-English language, translate
   the core intent into English for the slug.
   **Do not include the issue number** (GitHub `#N` or Jira `PROJ-123`) in the slug —
   the server prepends `source_id` automatically when present.
   Then call `mcp__forge-state__pipeline_init_with_context` again with the same parameters plus
   `user_confirmation={effort: <confirmed>, workspace_slug: <slug>, use_current_branch: <bool>}`.
   If `needs_user_confirmation.enriched_request_body` is non-empty (from the discussion path),
   also include `enriched_request_body: <value>` inside `user_confirmation`.
   The response will contain `branch` (the branch name) and `create_branch` (boolean).
   If `create_branch` is true: run `git checkout -b <branch>` via Bash immediately.
   Use the `workspace` from the confirmed response for all subsequent calls.
   **Important**: Always pass the workspace path from `pipeline_init` unchanged to the first
   `pipeline_init_with_context` call. Never construct workspace paths manually.

## Step 2: Main Loop

Repeat until done:

1. Call `mcp__forge-state__pipeline_next_action(workspace=<workspace>,
   previous_action_complete=true,
   previous_tokens=<tokens from last action>,
   previous_duration_ms=<ms from last action>)`.
   On the very first call, omit the `previous_*` parameters.
   `previous_model` and `previous_setup_only` are **optional** and auto-carried: the
   engine records the model and setup-only flag of the action it dispatched and reuses
   them, so you only need to report `previous_action_complete` plus the observed
   `previous_tokens` / `previous_duration_ms`. You may still pass `previous_model`
   explicitly to override (it wins when non-empty).

2. If `result.report_result` is non-null:
   - If `result.report_result.next_action_hint == "revision_required"`:
     present `result.report_result.findings` to the user, then continue the loop.
     On the next `pipeline_next_action` call, omit `previous_action_complete` (or pass false),
     and pass `previous_tokens=0, previous_duration_ms=0` with no `previous_model` or
     `previous_setup_only` — no new agent ran during this step, so P5 must not fire again.
   (`setup_continue` is handled server-side — the handler re-enters NextAction automatically.)

3. Execute the action based on `action.type`:
   - `spawn_agent`: If `action.display_message` is non-empty, output it verbatim.
     Then call Agent tool with `action.prompt`. Use `action.agent` as description.
     Record the tokens, duration, and model for the next `pipeline_next_action` call.
     - If `action.parallel_tasks` is non-empty: spawn one Agent call per entry in
       parallel; wait for all to complete before calling `pipeline_next_action` again.
       Each entry `t` is an explicit, server-computed contract — do **not** derive any
       filename yourself. Instruct that agent to work on task `t.id` (still using
       `action.prompt` as the base prompt), with:
       - inputs = `action.input_files` (shared) + `t.input_files` (task-specific)
       - output artifact = `t.output_file`
       After each agent returns, apply the **artifact write fallback per entry**: if
       `{workspace}/{t.output_file}` does not exist on disk, Write that agent's response
       text there before calling `pipeline_next_action`. `pipeline_report_result`
       reconciles all of the per-task verdicts in a single pass.
       (`action.parallel_task_ids` still lists the same IDs in order for reference.)
     - **Artifact write fallback**: After the agent returns, if `action.output_file` is
       non-empty, check whether `{workspace}/{action.output_file}` exists on disk. If
       the file does **NOT** exist, the agent returned its output as text instead of
       writing the file (subagents may lack write permission in some permission modes).
       In that case, use the Write tool to write the agent's final response text to
       `{workspace}/{action.output_file}` before calling `pipeline_next_action`.
       This ensures `pipeline_report_result` artifact validation always succeeds.
   - `checkpoint`: Present `action.present_to_user` to the user.
     If `display_message` is non-empty, output it **verbatim** — the server already appends
     the running-cost line (tokens / estimated USD / phases / retries so far) so the user can
     weigh "continue vs stop" against the spend. Do not assemble that line yourself from the
     raw `analytics` field.
     Mention that the Dashboard can be used to approve without terminal input.
     Then **immediately** call `mcp__forge-state__pipeline_next_action(workspace)`
     (no `user_response`, no `previous_*`). The server long-polls up to 50 s
     waiting for a Dashboard approval event.
     - If the response has `still_waiting: true`: call `pipeline_next_action(workspace)`
       again immediately (no `user_response`). Repeat until a non-checkpoint action is
       returned or the user provides a terminal response.
     - If the user types a response in the terminal (proceed / revise / abandon) while
       still_waiting is looping: on the next `pipeline_next_action` call, pass
       `user_response=<response>` instead of looping.
     - If a non-checkpoint action is returned: Dashboard approved; proceed normally.
     - **Special: `post-to-source` checkpoint** — when `action.name`
       is `"post-to-source"`:
       1. Ask the user whether to post the work report (use AskUserQuestion
          with options "post" / "skip").
       2. If the user chooses **"post"** and `action.post_method` is present:
          a. Read the body content from `action.post_method.body_source`.
          b. Post the comment using the method specified in `post_method`:
             - If `post_method.mcp_tool` is set: call it with `post_method.mcp_params`
               and the body content (pass body as the `body` parameter).
             - Else if `post_method.command` is set: execute the command via Bash.
             - Else if `post_method.instruction` is set: follow the instruction as a fallback guide.
          c. Report success or failure to the user.
       3. If the user chooses **"skip"**: do nothing.
       Pass the user's response to `pipeline_next_action(workspace, user_response=<response>)`.
     Do NOT call `checkpoint()` — `pipeline_next_action` handles the checkpoint
     state transition internally.
     On every `pipeline_next_action` call for a checkpoint (still_waiting loops and
     terminal-response call alike), omit `previous_action_complete` (or pass false),
     and pass `previous_tokens=0, previous_duration_ms=0` with no `previous_model`
     or `previous_setup_only`
     (checkpoints have no agent cost; omitting `previous_action_complete` causes the P5 block to be skipped).
   - `exec`: Run `action.commands` via Bash. Record the duration and `action.setup_only`
     for the next `pipeline_next_action` call. Pass `previous_setup_only=true` if
     `action.setup_only` is true. There is no model to record for exec actions; omit
     `previous_model` or pass it as an empty string.
     Also pass `previous_action_complete=true` (see Rules below).
   - `write_file`: Write `action.content` to `action.path`. Record the duration for the
     next `pipeline_next_action` call. Omit `previous_model` or pass it as an empty string.
     Also pass `previous_action_complete=true` (see Rules below).
   - `human_gate`: A task requires human action (e.g. merge an external PR, update dependencies).
     Present `action.present_to_user` to the user **verbatim** using AskUserQuestion with
     `action.options`. The server already embeds any cross-repository flow (external PR → CI
     watch → preview-pin → verify, plus a dependency-update skill pointer) into
     `present_to_user` for external-repo tasks — render that guidance as-is and help the user
     drive it; do not re-derive the steps here. Choosing **"done"** clears the gate (the
     deterministic close); for a cross-repo task, only choose it once the external change is
     pinned and building here.
     - If the user chooses **"done"** or **"skip"**: call `pipeline_next_action` again
       with no `previous_*` parameters (no agent ran).
       The handler automatically marks the task as completed.
     - If the user chooses **"abandon"**: call `mcp__forge-state__abandon(workspace)`.
     Do NOT call `checkpoint` or `phase_complete` for human_gate actions.
   - `done`: Pipeline complete. Stop.

## Supported Flags

- `--auto`: Skips all human confirmation prompts; progresses the pipeline automatically.
  Takes precedence over `--discuss`: when both flags are present, discussion mode is suppressed
  and `needs_user_confirmation` is returned directly.
- `--discuss`: Triggers a pre-pipeline clarification dialogue for plain text input pipelines.
  The orchestrator presents targeted questions to the user, collects answers, and writes the
  enriched task description into `request.md` before invoking any agents. Has no effect for
  GitHub/Jira source pipelines (external context already provides structured content) or when
  `--auto` is also set.
- `--nopr`: Skips the PR creation phase at the end of the pipeline.
- `--debug`: Enables debug mode in the pipeline state.

Flags can also be set as persistent defaults via `/forge-setup`. Explicit flags on `/forge` always override preferences.

## Rules

- Never make orchestration decisions independently — follow action.type exactly.
- Always pass `previous_action_complete=true`, `previous_tokens`, `previous_duration_ms`, `previous_model`, and `previous_setup_only` on every `pipeline_next_action` call after the first (after any `spawn_agent`, `exec`, or `write_file` action completes). Do NOT pass `previous_action_complete=true` after a checkpoint — it must remain false (or omitted) to skip the P5 report block.
- When `still_waiting: true` is returned: call `pipeline_next_action(workspace)` again immediately with no `previous_*` or `user_response`. This is the Dashboard long-poll loop (50 s per iteration) — keep calling until a non-still_waiting response arrives or the user types a terminal response.
- Never pass `isolation: "worktree"` to any Agent call.
- On MCP error: surface the error to the user and stop.

