forge
Orchestrate a full development pipeline using MCP-driven subagents. Accepts plain text or issue tracker URLs (GitHub, Jira, Linear, etc.) as input.
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
-
Call
mcp__forge-state__pipeline_init(arguments=$ARGUMENTS). -
If
result.errorsis non-empty: surface the errors to the user and stop. -
If
result.resume_modeis"auto": the input matched an existing spec directory. Do not ask for confirmation — proceed directly to Step 2. -
For new pipelines (
resume_modeis absent): a. Ifresult.fetch_neededis non-null:- Fetch the external data using the method specified in
fetch_needed:- If
fetch_needed.mcp_toolis set: call the MCP tool withfetch_needed.mcp_params. - Else if
fetch_needed.commandis set: execute the command via Bash and parse the JSON output. - Else: follow
fetch_needed.instructionas a fallback guide.
- If
- Map the response fields to
external_contextusingfetch_needed.response_mapping: for each entry(response_key → context_key), setexternal_context[context_key] = response[response_key]. - 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_textis not applicable for external issue sources — do not pass it.) b. Ifresult.fetch_neededis null (plain text input): callmcp__forge-state__pipeline_init_with_context(workspace=result.workspace, flags=result.flags, task_text=result.core_text). (result.core_textis a top-level field in thepipeline_initresponse — the task text with bare flags stripped.)
After the first
pipeline_init_with_contextcall, check which field is present in the response:If
result.needs_discussionis non-null (triggered only when--discussflag is present, source is plain text, and--autois absent):- Present each question in
result.needs_discussion.questionsto the user via AskUserQuestion (single prompt listing all questions). - Collect the user's answers as a single freeform string
(e.g.
"Q1: <answer>\nQ2: <answer>\nQ3: <answer>"). - Call
mcp__forge-state__pipeline_init_with_contextagain (the discussion call) with:workspace=<same>, flags=<same>, task_text=<same>, discussion_answers=<collected answers string>. The response will containneeds_user_confirmationwhereneeds_user_confirmation.enriched_request_bodyis non-empty. - Proceed with the effort/branch confirmation step below using this
needs_user_confirmation. When callingpipeline_init_with_contextwithuser_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 enrichedrequest.mdbody forward to workspace initialisation.
If
result.needs_user_confirmationis present directly (--discussabsent,--autoset, or GitHub/Jira source): proceed with the effort/branch confirmation step below (noenriched_request_bodyto 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):
- Effort level: present all three effort options from
effort_options(S, M, L — each withskipped_phasesusing thelabelfield). Each option has arecommendedboolean — mark the one whererecommendedistrueas "(Recommended)". - Branch decision: based on
current_branchandis_main_branchfrom the response:- If
is_main_branchis true: inform the user a new branch will be created (no question needed). - If
is_main_branchis 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#Nor JiraPROJ-123) in the slug — the server prependssource_idautomatically when present. Then callmcp__forge-state__pipeline_init_with_contextagain with the same parameters plususer_confirmation={effort: <confirmed>, workspace_slug: <slug>, use_current_branch: <bool>}. Ifneeds_user_confirmation.enriched_request_bodyis non-empty (from the discussion path), also includeenriched_request_body: <value>insideuser_confirmation. The response will containbranch(the branch name) andcreate_branch(boolean). Ifcreate_branchis true: rungit checkout -b <branch>via Bash immediately. Use theworkspacefrom the confirmed response for all subsequent calls. Important: Always pass the workspace path frompipeline_initunchanged to the firstpipeline_init_with_contextcall. Never construct workspace paths manually.
- If
- Fetch the external data using the method specified in
Step 2: Main Loop
Repeat until done:
-
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>, previous_model=<model from last action>, previous_setup_only=<setup_only from last action>). On the very first call, omit theprevious_*parameters. -
If
result.report_resultis non-null:- If
result.report_result.next_action_hint == "revision_required": presentresult.report_result.findingsto the user, then continue the loop. On the nextpipeline_next_actioncall, omitprevious_action_complete(or pass false), and passprevious_tokens=0, previous_duration_ms=0with noprevious_modelorprevious_setup_only— no new agent ran during this step, so P5 must not fire again. (setup_continueis handled server-side — the handler re-enters NextAction automatically.)
- If
-
Execute the action based on
action.type:spawn_agent: Ifaction.display_messageis non-empty, output it verbatim. Then call Agent tool withaction.prompt. Useaction.agentas description. Record the tokens, duration, and model for the nextpipeline_next_actioncall.- If
action.parallel_task_idsis non-empty: spawn one Agent call per task ID in parallel; wait for all to complete before callingpipeline_next_actionagain. - Artifact write fallback: After the agent returns, if
action.output_fileis 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 callingpipeline_next_action. This ensurespipeline_report_resultartifact validation always succeeds.
- If
checkpoint: Presentaction.present_to_userto the user. Mention that the Dashboard can be used to approve without terminal input. Then immediately callmcp__forge-state__pipeline_next_action(workspace)(nouser_response, noprevious_*). The server long-polls up to 50 s waiting for a Dashboard approval event.- If the response has
still_waiting: true: callpipeline_next_action(workspace)again immediately (nouser_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_actioncall, passuser_response=<response>instead of looping. - If a non-checkpoint action is returned: Dashboard approved; proceed normally.
- Special:
post-to-sourcecheckpoint — whenaction.nameis"post-to-source":- Ask the user whether to post the work report (use AskUserQuestion with options "post" / "skip").
- If the user chooses "post" and
action.post_methodis present: a. Read the body content fromaction.post_method.body_source. b. Post the comment using the method specified inpost_method:- If
post_method.mcp_toolis set: call it withpost_method.mcp_paramsand the body content (pass body as thebodyparameter). - Else if
post_method.commandis set: execute the command via Bash. - Else if
post_method.instructionis set: follow the instruction as a fallback guide. c. Report success or failure to the user.
- If
- If the user chooses "skip": do nothing.
Pass the user's response to
pipeline_next_action(workspace, user_response=<response>). Do NOT callcheckpoint()—pipeline_next_actionhandles the checkpoint state transition internally. On everypipeline_next_actioncall for a checkpoint (still_waiting loops and terminal-response call alike), omitprevious_action_complete(or pass false), and passprevious_tokens=0, previous_duration_ms=0with noprevious_modelorprevious_setup_only(checkpoints have no agent cost; omittingprevious_action_completecauses the P5 block to be skipped).
- If the response has
exec: Runaction.commandsvia Bash. Record the duration andaction.setup_onlyfor the nextpipeline_next_actioncall. Passprevious_setup_only=trueifaction.setup_onlyis true. There is no model to record for exec actions; omitprevious_modelor pass it as an empty string. Also passprevious_action_complete=true(see Rules below).write_file: Writeaction.contenttoaction.path. Record the duration for the nextpipeline_next_actioncall. Omitprevious_modelor pass it as an empty string. Also passprevious_action_complete=true(see Rules below).human_gate: A task requires human action (e.g. merge an external PR, update dependencies). Presentaction.present_to_userto the user using AskUserQuestion withaction.options.- If the user chooses "done" or "skip": call
pipeline_next_actionagain with noprevious_*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 callcheckpointorphase_completefor human_gate actions.
- If the user chooses "done" or "skip": call
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 andneeds_user_confirmationis 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 intorequest.mdbefore invoking any agents. Has no effect for GitHub/Jira source pipelines (external context already provides structured content) or when--autois 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, andprevious_setup_onlyon everypipeline_next_actioncall after the first (after anyspawn_agent,exec, orwrite_fileaction completes). Do NOT passprevious_action_complete=trueafter a checkpoint — it must remain false (or omitted) to skip the P5 report block. - When
still_waiting: trueis returned: callpipeline_next_action(workspace)again immediately with noprevious_*oruser_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.