From forge
Run the FORGE apply pipeline. Use when: user approved Gate #2 and wants to apply the implementation to source files.
How this skill is triggered — by the user, by Claude, or both
Slash command
/forge:apply [feature name][feature name]claude-sonnet-4-6This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Normal flow:** After gate2 approval the existing worker resumes automatically and handles apply (documenter, lifecycle, commit gate). The conductor does NOT need to invoke /forge:apply — just wait for the commit gate and then /forge:approve.
Normal flow: After gate2 approval the existing worker resumes automatically and handles apply (documenter, lifecycle, commit gate). The conductor does NOT need to invoke /forge:apply — just wait for the commit gate and then /forge:approve.
Manual recovery only: Use /forge:apply when the original worker died, failed, or was killed before reaching the commit gate. This spawns a fresh apply worker to pick up where the dead worker left off.
feedback_conductor_handles_commits)feedback_run_completion_timing)feedback_no_duplicate_apply_worker)The normal flow does NOT use /forge:apply. After gate2 approval, the existing worker resumes automatically and handles apply (documenter, lifecycle, commit gate). The conductor just waits for the commit gate and then runs /forge:approve.
Only proceed if the original worker is confirmed dead. Check:
forge_list_runs with status: "gate-pending" to find the source run (implement/debug/refactor). Call forge_get_run on it.status: "failed" or status: "discarded" or failureReason is non-null: the worker is dead. Proceed to Step 1b.gate-pending with gateState.gate === "gate2" and gateState.status === "approved": the worker should be resuming. Print "Worker should be resuming after gate2 approval — wait for the commit gate. Only use /forge:apply if the worker is confirmed dead (status: failed/discarded)." and STOP.running: the worker is alive. Print "Worker is still running — wait for it to finish." and STOP.Call forge_check_gate to read the current gate state. If the MCP tool is unavailable, fall back to reading .pipeline/gate-pending.json directly.
Proceed only if ALL of these are true:
gate is "gate2"status is "approved"If any check fails, stop immediately. Print: "Gate #2 has not been approved. Run /forge:implement (or /forge:debug, /forge:refactor) and then /forge:approve before applying."
Do NOT proceed. Do NOT spawn a worker.
Call forge_create_run with:
sessionId: your session ID (or "unknown" if unavailable)pipelineType: "apply"feature: the feature name from $ARGUMENTS, or read from docs/PLAN.md first headingspawnWorker: trueuseWorktree: falseThe worker runs the apply pipeline autonomously — documenter, lifecycle cleanup — then pauses at a commit gate for user approval before committing and merging.
Report to the user:
<runId><logFile> (tail with tail -f <logFile> to follow progress)Do NOT invoke documenter directly. Do NOT check for existing runs first. Every /forge:apply invocation creates exactly one new run.
Exit — do not proceed to further steps.
Check if a worktree-backed run exists for this feature. Call forge_list_runs with no pipelineType filter. For each candidate, call forge_get_run and check:
worktreePath is non-null, ANDworktreePath exists on disk, ANDstages.implement.status === "completed", ORstages.debug.status === "completed", ORstages.refactor.status === "completed"The stage-progression check (condition 3) is the authoritative selector. If stages is absent on the run (pre-dates the stages field), fall back to accepting runs whose pipelineType is "implement", "debug", or "refactor" — this preserves backward compatibility with older runs.
Among all matching candidates, select the most recent one by createdAt. If no match is found, or the matching run's worktree directory does not exist on disk: agents work in the main project directory as before (see fallback below).
If the run has a worktreePath AND the directory exists on disk: save it as <worktreePath>. All agent work in STEP 3 happens inside this path. Persist it in two places:
forge_update_run with the current apply runId and worktreePath: "<worktreePath>". This lets the worker process (forge-worker.mjs) resolve the correct gate file path..pipeline/run-active.json, add/update the worktreePath field with the resolved path, write the file back (preserve all other fields). The workflow-guard hook uses this field to block source writes outside the worktree.If no worktree-backed run is found, or the directory does not exist: agents work in the main project directory as before. Do NOT write worktreePath to run-active.json or the run. Skip the worktree targeting block below.
If a worktree was resolved: read <worktreePath>/docs/context/handoff.md for the approved implementation.
If no worktree: read docs/context/handoff.md from the main project root.
See Model routing in CLAUDE.md.
Read gitIntegration from .pipeline/project.json (prefer forge_read_project MCP tool, fall back to Read). If gitIntegration is not an object, or gitIntegration.enabled is not true, skip git branch creation (step 3.1) and auto-PR (step 7). Missing fields use defaults: branchPrefix: "forge/", autoCommit: false, autoPR: false.
The documenter runs OFF the worktree. Under the orchestrated apply path the worktree may already be merged and removed, so the documenter does not run inside it and does not need live worktree access. <mainProjectRoot> is the main project root — the directory that contains .pipeline/. Its sole input is the change-summary the implement orchestrator captured before gate2 (at .pipeline/runs/<runId>/change-summary.md).
Git branch creation (opt-in — only if gitIntegration.enabled):
$ARGUMENTS if provided, else read the first ## Feature: heading from docs/PLAN.md[a-z0-9-], truncate to 50 charsgit checkout -b <branchPrefix><slug>git checkout <branchPrefix><slug> instead (reuse existing branch)Test execution (opt-in — only if testCommand is set in project.json):
timeout: 60000[suggest] debug — tests failed after apply. Do NOT auto-fix or retry.Documenter (dispatched off-worktree against <mainProjectRoot>): updates CHANGELOG, ARCHITECTURE, DECISIONS, captures solution — reading what changed from the change-summary, not the worktree.
Record documenterStartedAt = Date.now() (epoch-ms) immediately before the documenter dispatch.
Dispatch the documenter against <mainProjectRoot> (NOT the worktree). Prepend to its prompt:
Your working directory is the main project root:
<mainProjectRoot>changeSummaryPath:<mainProjectRoot>/.pipeline/runs/<runId>/change-summary.md— read this for the set of changes to document; the worktree is not available. changelogFragmentPath:<mainProjectRoot>/.pipeline/runs/<runId>/CHANGELOG-fragment.mdrunId:<runId>
After the documenter completes, verify its output via mtime check — do NOT use git diff (gitignored files never appear in git diff output):
For each expected doc file the documenter should have written (typically CHANGELOG.md, docs/ARCHITECTURE.md, docs/DECISIONS.md, and any solution file under docs/solutions/), run:
node scripts/verify-output.mjs --file=<absoluteDocFilePath> --since=<documenterStartedAt>
ok: true): file was written or updated — continue.mtime < since): documenter did NOT write this file. Re-invoke the documenter once with a note identifying the missing or stale file. If the second run also fails the mtime check, log [apply] documenter output unverified: <file> and continue — do NOT loop further.After mtime verification passes (or the single retry is exhausted), proceed to Step 3.4a.
3.4a. Auto-dispatch learnings-extractor (non-blocking):
After mtime verification, dispatch the learnings-extractor to record outcome-keyed learnings.
outcome: read run.json status and failureReason; map to "approved", "blocked", or "debug_resolved" based on pipelineType.Agent(subagent_type="forge:learnings-extractor") targeting <mainProjectRoot> as the working directory. Do NOT pass <worktreePath>.[learnings] failed — continuing and continue. Apply never blocks on learnings extraction.3.4b. Auto-dispatch compound-refresh (non-blocking):
After learnings extraction, auto-dispatch compound-refresh to keep the knowledge store current.
<mainProjectRoot> (two directory levels up from <worktreePath>; if no worktree, use the main project root directly). This matches the <mainProjectRoot> derivation already described at the top of Step 3.Agent(subagent_type="forge:compound-refresh") targeting <mainProjectRoot> as the working directory. Do NOT pass <worktreePath>.[refresh] done; on any failure log [refresh] failed — continuing and fall through to Step 3b. Apply never blocks on refresh./forge:refresh manually while apply is active — a 2nd dispatch triggers the loop-guard warning; a 3rd is hard-blocked.3b. Post-apply lifecycle cleanup (always runs, not gated by gitIntegration):
node scripts/post-apply-lifecycle.mjs "<safe-feature>" (use the sanitized feature from Step 1). Set timeout: 30000.[lifecycle] cleanup done.[lifecycle] cleanup failed: <stderr output> and continue. Do NOT retry. This is non-blocking.3c. Commit apply changes (always runs — closes TODO 38bca814):
Worker owns ALL commits inside the worktree — the conductor at the commit gate only merges. This step commits the documenter's CHANGELOG/ARCHITECTURE/PLAN.md updates AND any uncommitted source changes left over from the implement/debug/refactor stage (single-pass implement runs don't have per-phase commits).
When <worktreePath> is non-null:
git -C <worktreePath> diff --name-only HEAD to find changed files.git -C <worktreePath> add <file> (do NOT use git add -A).git -C <worktreePath> commit -m "feat(forge): <safe-feature> [<runId>]".[worktree] No changes to commit — skipping. and proceed to Step 4.[worktree] Commit failed: <error> and proceed to Step 4 (the conductor will surface uncommitted files via the verify step in approve Step 4).When <worktreePath> is null (apply runs without a worktree):
git diff --name-only HEAD, git add <file> per file, git commit -m "feat(forge): <safe-feature> [<runId>]".Forbidden ops: --force, --force-with-lease, --amend, --no-verify, git reset, git clean, git stash. Worker does not bypass any safeguard.
After documenter + lifecycle cleanup AND the apply commit (Step 3c) are done, write the commit gate and exit. The conductor handles only the merge.
forge_set_gate or write directly:<worktreePath>/.pipeline/gate-pending.json.pipeline/gate-pending.json{"runId":"<runId>","gate":"commit","feature":"<feature name>","status":"pending","createdAt":"<now ISO>"}forge_update_run with the runId, status: "gate-pending", and gateState: {"gate":"commit","status":"pending","feature":"<feature name>","createdAt":"<now ISO>"}git diff --name-only or git -C <worktreePath> status --porcelain), note test results if any.Conductor reminder: When the user says "approve" for a commit gate, invoke /forge:approve via the Skill tool — never manually call forge_set_gate. The approve skill's Step 4 handles the merge.
$ARGUMENTS
npx claudepluginhub chulf58/forge --plugin forgeProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.