Skip to content
Development
Command

/explain

Plain-English breakdown of what happened on a GitHub issue — translates FORGE pipeline annotations into a human-readable narrative for teammates and PMs

BOOST
From plugin
forgedock
11980 skills80 commands
Install
> /plugin marketplace add RapierCraftStudios/ForgeDock
> /plugin install forgedock@forgedock

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/explain

Context preview

What this command does when you run it.

Plain-English breakdown of what happened on a GitHub issue — translates FORGE pipeline annotations into a human-readable narrative for teammates and PMs

Command definition

explain.md
description: Plain-English breakdown of what happened on a GitHub issue — translates FORGE pipeline annotations into a human-readable narrative for teammates and PMs
argument-hint: <issue number or PR#N> [--repo prefix]
install: extras

/explain — FORGE Annotation Translator

**Input**: $ARGUMENTS

You translate the FORGE pipeline's machine-readable annotations on a GitHub issue or PR into a clear, plain-English narrative that anyone can understand — no pipeline knowledge required. This is a read-only command: it never writes to GitHub, never changes labels, and never modifies any state.

**Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154. **NEVER use plan mode (EnterPlanMode).**

---

Config Preamble

Read `forge.yaml` from the project root before executing any phase:

CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
if [ -f "$CONFIG_FILE" ]; then
  GH_OWNER=$(yq '.project.owner' "$CONFIG_FILE")
  GH_REPO_NAME=$(yq '.project.repo' "$CONFIG_FILE")
  GH_REPO="${GH_OWNER}/${GH_REPO_NAME}"
  GH_FLAG="-R $GH_REPO"
  REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
else
  echo "WARNING: forge.yaml not found — using placeholder values"
  GH_REPO="your-org/your-repo"
  GH_FLAG="-R $GH_REPO"
  REPO_PATH="./"
fi

---

Phase 1: Parse Input

Supported input forms:

| Input | Example | What it explains | |-------|---------|-----------------| | Issue number | `616` | GitHub issue #616 | | Issue prefix | `#616` | GitHub issue #616 | | PR prefix | `PR#42` or `PR 42` | Pull request #42 | | Multi-repo prefix | `mcp:616` | Issue #616 in the `mcp` satellite repo |

INPUT="$ARGUMENTS"

# Strip leading # if present
INPUT=$(echo "$INPUT" | sed 's/^#//')

# Check for repo prefix (e.g. "mcp:616")
REPO_PREFIX=$(echo "$INPUT" | grep -oE '^[a-z]+:' | sed 's/://')
if [ -n "$REPO_PREFIX" ]; then
  INPUT=$(echo "$INPUT" | sed "s/^${REPO_PREFIX}://")
  # Look up satellite repo from forge.yaml
  SAT_REPO=$(yq ".repos.satellites[] | select(.prefix == \"$REPO_PREFIX\") | .repo" "$CONFIG_FILE" 2>/dev/null)
  if [ -n "$SAT_REPO" ]; then
    GH_REPO="$SAT_REPO"
    GH_FLAG="-R $GH_REPO"
  else
    echo "Unknown repo prefix: $REPO_PREFIX — using default repo"
  fi
fi

# Determine if this is an issue or PR
IS_PR=false
if echo "$INPUT" | grep -qiE '^pr[[:space:]]*#?[[:space:]]*[0-9]+$'; then
  NUMBER=$(echo "$INPUT" | grep -oE '[0-9]+')
  IS_PR=true
else
  NUMBER=$(echo "$INPUT" | grep -oE '^[0-9]+')
fi

if [ -z "$NUMBER" ]; then
  echo "ERROR: Could not parse a number from input: $ARGUMENTS"
  echo "Usage: /explain 616  or  /explain PR#42  or  /explain mcp:616"
  exit 1
fi

---

Phase 2: Fetch GitHub Artifacts

Fetch the issue or PR metadata and all its comments.

if [ "$IS_PR" = "true" ]; then
  # Fetch PR details
  METADATA=$(gh pr view "$NUMBER" $GH_FLAG --json number,title,body,state,mergedAt,baseRefName,headRefName,author,labels 2>/dev/null)
  if [ -z "$METADATA" ]; then
    echo "ERROR: PR #$NUMBER not found in $GH_REPO"
    exit 1
  fi
  TITLE=$(echo "$METADATA" | jq -r '.title')
  STATE=$(echo "$METADATA" | jq -r '.state')
  COMMENTS=$(gh api repos/$GH_REPO/pulls/$NUMBER/comments --jq '.[].body' 2>/dev/null)
  ISSUE_COMMENTS=$(gh api repos/$GH_REPO/issues/$NUMBER/comments --jq '.[].body' 2>/dev/null)
  ALL_COMMENTS=$(printf '%s\n%s' "$COMMENTS" "$ISSUE_COMMENTS")
  # Also check if this PR closes an issue, and fetch that issue's comments
  CLOSES_ISSUE=$(gh pr view "$NUMBER" $GH_FLAG --json body --jq '.body' 2>/dev/null | grep -oiE 'Closes[[:space:]]+#[0-9]+' | grep -oE '[0-9]+' | head -1)
  if [ -n "$CLOSES_ISSUE" ]; then
    ISSUE_COMMENTS_FROM_LINKED=$(gh api repos/$GH_REPO/issues/$CLOSES_ISSUE/comments --jq '.[].body' 2>/dev/null)
    ALL_COMMENTS=$(printf '%s\n%s' "$ALL_COMMENTS" "$ISSUE_COMMENTS_FROM_LINKED")
  fi
else
  # Fetch issue details
  METADATA=$(gh issue view "$NUMBER" $GH_FLAG --json number,title,body,state,labels,milestone 2>/dev/null)
  if [ -z "$METADATA" ]; then
    echo "ERROR: Issue #$NUMBER not found in $GH_REPO"
    exit 1
  fi
  TITLE=$(echo "$METADATA" | jq -r '.title')
  STATE=$(echo "$METADATA" | jq -r '.state')
  ALL_COMMENTS=$(gh api repos/$GH_REPO/issues/$NUMBER/comments --jq '.[].body' 2>/dev/null)
fi

if [ -z "$ALL_COMMENTS" ]; then
  echo "## #$NUMBER — $TITLE"
  echo ""
  echo "**Status**: ${STATE}"
  echo ""
  echo "No FORGE pipeline annotations found on this issue. It may not have been processed by the pipeline yet, or it was handled manually."
  exit 0
fi

---

Phase 3: Parse FORGE Annotations

Extract each annotation block from the comments. Each `<!-- FORGE:TYPE -->` block is a structured comment posted by the pipeline agent.

# Helper: extract comment body containing a FORGE marker
extract_forge() {
  local marker="${1}"
  echo "$ALL_COMMENTS" | awk "
    /<!-- ${marker}(:[A-Z]*)? -->/{found=1; buffer=\"\"}
    found {buffer = buffer \$(0) ORS}
    /<!-- ${marker}[^>]*:COMPLETE -->/{if(found){print buffer; found=0; buffer=\"\"}}
    END {if(found && length(buffer)>0) print buffer}
  "
}

INVESTIGATOR=$(echo "$ALL_COMMENTS" | awk '/<!-- FORGE:INVESTIGATOR -->/{found=1} found{print} /<!-- INVESTIGATION:COMPLETE -->/{found=0}')
CONTRACT=$(echo "$ALL_COMMENTS" | grep -A 1000 "<!-- FORGE:CONTRACT -->" | head -80)
CONTEXT_COMMENT=$(echo "$ALL_COMMENTS" | grep -A 1000 "<!-- FORGE:CONTEXT -->" | head -80)
ARCHITECT=$(echo "$ALL_COMMENTS" | grep -A 1000 "<!-- FORGE:ARCHITECT -->" | head -100)
BUILDER=$(echo "$ALL_COMMENTS" | grep -A 1000 "<!-- FORGE:BUILDER -->" | head -100)
TRAJECTORY=$(echo "$ALL_COMMENTS" | grep -A 1000 "<!-- FORGE:TRAJECTORY -->" | head -60)
DECOMPOSED=$(echo "$ALL_COMMENTS" | grep -A 1000 "<!-- FORGE:DECOMPOSED -->" | head -40)

---

Phase 4: Produce Plain-English Narrative

Output a clean, human-readab

Read more
Ships withforgedock

ForgeDock — autonomous AI dev pipeline for Claude Code. GitHub as agent memory: issue in, PR out.

Get the whole plugin

Other commands on forgedock.