Claude Cursor opencode Skill

github-triage

Read-only GitHub triage for issues AND PRs. 1 item = 1 background task (category: quick). Analyzes all open items and writes evidence-backed reports to /tmp/{datetime}/. Every claim requires a GitHub permalink as proof. NEVER takes any action on GitHub - no comments, no merges, n

LLM Mart · 0 points · 12 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download code-yeongyu-oh-my-openagent-.agents_skills_github-triage-70b9594.zip · 9 KB
Part of code-yeongyu/oh-my-openagent — 51 skills

Install

skills CLI npx skills add https://github.com/code-yeongyu/oh-my-openagent/tree/dev/.agents/skills/github-triage
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install code-yeongyu-oh-my-openagent@llmmart
Git git clone https://github.com/code-yeongyu/oh-my-openagent.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole code-yeongyu/oh-my-openagent collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

GitHub Triage - Read-Only Analyzer

Architecture

1 ISSUE/PR = 1 task_create = 1 quick SUBAGENT (background). NO EXCEPTIONS.

Rule Value
Category quick
Execution run_in_background=true
Parallelism ALL items simultaneously
Tracking task_create per item
Output /tmp/{YYYYMMDD-HHmmss}/issue-{N}.md or pr-{N}.md

Zero-Action Policy (ABSOLUTE)

<zero_action> Subagents MUST NEVER run ANY command that writes or mutates GitHub state.

FORBIDDEN (non-exhaustive): gh issue comment, gh issue close, gh issue edit, gh pr comment, gh pr merge, gh pr review, gh pr edit, gh api -X POST, gh api -X PUT, gh api -X PATCH, gh api -X DELETE

ALLOWED:

  • gh issue view, gh pr view, gh api (GET only) - read GitHub data
  • Grep, Read, Glob - read codebase
  • Write - write report files to /tmp/ ONLY
  • git log, git show, git blame - read git history (for finding fix commits)

ANY GitHub mutation = CRITICAL violation. </zero_action>


Evidence Rule (MANDATORY)

Files (oh-my-openagent)
  • scripts
    • gh_fetch.py 12.2 KB
      #!/usr/bin/env -S uv run --script
      # /// script
      # requires-python = ">=3.11"
      # dependencies = [
      #     "typer>=0.12.0",
      #     "rich>=13.0.0",
      # ]
      # ///
      """
      GitHub Issues/PRs Fetcher with Exhaustive Pagination.
      
      Fetches ALL issues and/or PRs from a GitHub repository using gh CLI.
      Implements proper pagination to ensure no items are missed.
      
      Usage:
          ./gh_fetch.py issues                    # Fetch all issues
          ./gh_fetch.py prs                       # Fetch all PRs
          ./gh_fetch.py all                       # Fetch both issues and PRs
          ./gh_fetch.py issues --hours 48         # Issues from last 48 hours
          ./gh_fetch.py prs --state open          # Only open PRs
          ./gh_fetch.py all --repo owner/repo     # Specify repository
      """
      
      import asyncio
      import json
      from datetime import UTC, datetime, timedelta
      from enum import Enum
      from typing import Annotated
      
      import typer
      from rich.console import Console
      from rich.panel import Panel
      from rich.progress import Progress, TaskID
      from rich.table import Table
      
      app = typer.Typer(
          name="gh_fetch",
          help="Fetch GitHub issues/PRs with exhaustive pagination.",
          no_args_is_help=True,
      )
      console = Console()
      
      BATCH_SIZE = 500  # Maximum allowed by GitHub API
      
      
      class ItemState(str, Enum):
          ALL = "all"
          OPEN = "open"
          CLOSED = "closed"
      
      
      class OutputFormat(str, Enum):
          JSON = "json"
          TABLE = "table"
          COUNT = "count"
      
      
      async def run_gh_command(args: list[str]) -> tuple[str, str, int]:
          """Run gh CLI command asynchronously."""
          proc = await asyncio.create_subprocess_exec(
              "gh",
              *args,
              stdout=asyncio.subprocess.PIPE,
              stderr=asyncio.subprocess.PIPE,
          )
          stdout, stderr = await proc.communicate()
          return stdout.decode(), stderr.decode(), proc.returncode or 0
      
      
      async def get_current_repo() -> str:
          """Get the current repository from gh CLI."""
          stdout, stderr, code = await run_gh_command(
              ["repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"]
          )
          if code != 0:
              console.print(f"[red]Error getting current repo: {stderr}[/red]")
              raise typer.Exit(1)
          return stdout.strip()
      
      
      async def fetch_items_page(
          repo: str,
          item_type: str,  # "issue" or "pr"
          state: str,
          limit: int,
          search_filter: str = "",
      ) -> list[dict]:
          """Fetch a single page of issues or PRs."""
          cmd = [
              item_type,
              "list",
              "--repo",
              repo,
              "--state",
              state,
              "--limit",
              str(limit),
              "--json",
              "number,title,state,createdAt,updatedAt,labels,author,body",
          ]
          if search_filter:
              cmd.extend(["--search", search_filter])
      
          stdout, stderr, code = await run_gh_command(cmd)
          if code != 0:
              console.print(f"[red]Error fetching {item_type}s: {stderr}[/red]")
              return []
      
          try:
              return json.loads(stdout) if stdout.strip() else []
          except json.JSONDecodeError:
              console.print(f"[red]Error parsing {item_type} response[/red]")
              return []
      
      
      async def fetch_all_items(
          repo: str,
          item_type: str,
          state: str,
          hours: int | None,
          progress: Progress,
          task_id: TaskID,
      ) -> list[dict]:
          """Fetch ALL items with exhaustive pagination."""
          all_items: list[dict] = []
          page = 1
      
          progress.update(task_id, description=f"[cyan]Fetching {item_type}s page {page}...")
          items = await fetch_items_page(repo, item_type, state, BATCH_SIZE)
          fetched_count = len(items)
          all_items.extend(items)
      
          console.print(f"[dim]Page {page}: fetched {fetched_count} {item_type}s[/dim]")
      
          while fetched_count == BATCH_SIZE:
              page += 1
              progress.update(
                  task_id, description=f"[cyan]Fetching {item_type}s page {page}..."
              )
      
              last_created = all_items[-1].get("createdAt", "")
              if not last_created:
                  break
      
              search_filter = f"created:<{last_created}"
              items = await fetch_items_page(
                  repo, item_type, state, BATCH_SIZE, search_filter
              )
              fetched_count = len(items)
      
              if fetched_count == 0:
                  break
      
              existing_numbers = {item["number"] for item in all_items}
              new_items = [item for item in items if item["number"] not in existing_numbers]
              all_items.extend(new_items)
      
              console.print(
                  f"[dim]Page {page}: fetched {fetched_count}, added {len(new_items)} new (total: {len(all_items)})[/dim]"
              )
      
              if page > 20:
                  console.print("[yellow]Safety limit reached (20 pages)[/yellow]")
                  break
      
          if hours is not None:
              cutoff = datetime.now(UTC) - timedelta(hours=hours)
              cutoff_str = cutoff.isoformat()
      
              original_count = len(all_items)
              all_items = [
                  item
                  for item in all_items
                  if item.get("createdAt", "") >= cutoff_str
                  or item.get("updatedAt", "") >= cutoff_str
              ]
              filtered_count = original_count - len(all_items)
              if filtered_count > 0:
                  console.print(
                      f"[dim]Filtered out {filtered_count} items older than {hours} hours[/dim]"
                  )
      
          return all_items
      
      
      def display_table(items: list[dict], item_type: str) -> None:
          """Display items in a Rich table."""
          table = Table(title=f"{item_type.upper()}s ({len(items)} total)")
          table.add_column("#", style="cyan", width=6)
          table.add_column("Title", style="white", max_width=50)
          table.add_column("State", style="green", width=8)
          table.add_column("Author", style="yellow", width=15)
          table.add_column("Labels", style="magenta", max_width=30)
          table.add_column("Updated", style="dim", width=12)
      
          for item in items[:50]:
              labels = ", ".join(label.get("name", "") for label in item.get("labels", []))
              updated = item.get("updatedAt", "")[:10]
              author = item.get("author", {}).get("login", "unknown")
      
              table.add_row(
                  str(item.get("number", "")),
                  (item.get("title", "")[:47] + "...")
                  if len(item.get("title", "")) > 50
                  else item.get("title", ""),
                  item.get("state", ""),
                  author,
                  (labels[:27] + "...") if len(labels) > 30 else labels,
                  updated,
              )
      
          console.print(table)
          if len(items) > 50:
              console.print(f"[dim]... and {len(items) - 50} more items[/dim]")
      
      
      @app.command()
      def issues(
          repo: Annotated[
              str | None, typer.Option("--repo", "-r", help="Repository (owner/repo)")
          ] = None,
          state: Annotated[
              ItemState, typer.Option("--state", "-s", help="Issue state filter")
          ] = ItemState.ALL,
          hours: Annotated[
              int | None,
              typer.Option(
                  "--hours", "-h", help="Only issues from last N hours (created or updated)"
              ),
          ] = None,
          output: Annotated[
              OutputFormat, typer.Option("--output", "-o", help="Output format")
          ] = OutputFormat.TABLE,
      ) -> None:
          """Fetch all issues with exhaustive pagination."""
      
          async def async_main() -> None:
              target_repo = repo or await get_current_repo()
      
              console.print(f"""
      [cyan]Repository:[/cyan] {target_repo}
      [cyan]State:[/cyan] {state.value}
      [cyan]Time filter:[/cyan] {f"Last {hours} hours" if hours else "All time"}
      """)
      
              with Progress(console=console) as progress:
                  task: TaskID = progress.add_task("[cyan]Fetching issues...", total=None)
                  items = await fetch_all_items(
                      target_repo, "issue", state.value, hours, progress, task
                  )
                  progress.update(
                      task, description="[green]Complete!", completed=100, total=100
                  )
      
              console.print(
                  Panel(f"[green]Found {len(items)} issues[/green]", border_style="green")
              )
      
              if output == OutputFormat.JSON:
                  console.print(json.dumps(items, indent=2, ensure_ascii=False))
              elif output == OutputFormat.TABLE:
                  display_table(items, "issue")
              else:
                  console.print(f"Total issues: {len(items)}")
      
          asyncio.run(async_main())
      
      
      @app.command()
      def prs(
          repo: Annotated[
              str | None, typer.Option("--repo", "-r", help="Repository (owner/repo)")
          ] = None,
          state: Annotated[
              ItemState, typer.Option("--state", "-s", help="PR state filter")
          ] = ItemState.OPEN,
          hours: Annotated[
              int | None,
              typer.Option(
                  "--hours", "-h", help="Only PRs from last N hours (created or updated)"
              ),
          ] = None,
          output: Annotated[
              OutputFormat, typer.Option("--output", "-o", help="Output format")
          ] = OutputFormat.TABLE,
      ) -> None:
          """Fetch all PRs with exhaustive pagination."""
      
          async def async_main() -> None:
              target_repo = repo or await get_current_repo()
      
              console.print(f"""
      [cyan]Repository:[/cyan] {target_repo}
      [cyan]State:[/cyan] {state.value}
      [cyan]Time filter:[/cyan] {f"Last {hours} hours" if hours else "All time"}
      """)
      
              with Progress(console=console) as progress:
                  task: TaskID = progress.add_task("[cyan]Fetching PRs...", total=None)
                  items = await fetch_all_items(
                      target_repo, "pr", state.value, hours, progress, task
                  )
                  progress.update(
                      task, description="[green]Complete!", completed=100, total=100
                  )
      
              console.print(
                  Panel(f"[green]Found {len(items)} PRs[/green]", border_style="green")
              )
      
              if output == OutputFormat.JSON:
                  console.print(json.dumps(items, indent=2, ensure_ascii=False))
              elif output == OutputFormat.TABLE:
                  display_table(items, "pr")
              else:
                  console.print(f"Total PRs: {len(items)}")
      
          asyncio.run(async_main())
      
      
      @app.command(name="all")
      def fetch_all(
          repo: Annotated[
              str | None, typer.Option("--repo", "-r", help="Repository (owner/repo)")
          ] = None,
          state: Annotated[
              ItemState, typer.Option("--state", "-s", help="State filter")
          ] = ItemState.ALL,
          hours: Annotated[
              int | None,
              typer.Option(
                  "--hours", "-h", help="Only items from last N hours (created or updated)"
              ),
          ] = None,
          output: Annotated[
              OutputFormat, typer.Option("--output", "-o", help="Output format")
          ] = OutputFormat.TABLE,
      ) -> None:
          """Fetch all issues AND PRs with exhaustive pagination."""
      
          async def async_main() -> None:
              target_repo = repo or await get_current_repo()
      
              console.print(f"""
      [cyan]Repository:[/cyan] {target_repo}
      [cyan]State:[/cyan] {state.value}
      [cyan]Time filter:[/cyan] {f"Last {hours} hours" if hours else "All time"}
      [cyan]Fetching:[/cyan] Issues AND PRs
      """)
      
              with Progress(console=console) as progress:
                  issues_task: TaskID = progress.add_task(
                      "[cyan]Fetching issues...", total=None
                  )
                  prs_task: TaskID = progress.add_task("[cyan]Fetching PRs...", total=None)
      
                  issues_items, prs_items = await asyncio.gather(
                      fetch_all_items(
                          target_repo, "issue", state.value, hours, progress, issues_task
                      ),
                      fetch_all_items(
                          target_repo, "pr", state.value, hours, progress, prs_task
                      ),
                  )
      
                  progress.update(
                      issues_task,
                      description="[green]Issues complete!",
                      completed=100,
                      total=100,
                  )
                  progress.update(
                      prs_task, description="[green]PRs complete!", completed=100, total=100
                  )
      
              console.print(
                  Panel(
                      f"[green]Found {len(issues_items)} issues and {len(prs_items)} PRs[/green]",
                      border_style="green",
                  )
              )
      
              if output == OutputFormat.JSON:
                  result = {"issues": issues_items, "prs": prs_items}
                  console.print(json.dumps(result, indent=2, ensure_ascii=False))
              elif output == OutputFormat.TABLE:
                  display_table(issues_items, "issue")
                  console.print("")
                  display_table(prs_items, "pr")
              else:
                  console.print(f"Total issues: {len(issues_items)}")
                  console.print(f"Total PRs: {len(prs_items)}")
      
          asyncio.run(async_main())
      
      
      if __name__ == "__main__":
          app()
      
  • SKILL.md 16.7 KB
    ---
    name: github-triage
    description: "Read-only GitHub triage for issues AND PRs. 1 item = 1 background task (category: quick). Analyzes all open items and writes evidence-backed reports to /tmp/{datetime}/. Every claim requires a GitHub permalink as proof. NEVER takes any action on GitHub - no comments, no merges, no closes, no labels. Reports only. Triggers: 'triage', 'triage issues', 'triage PRs', 'github triage'."
    ---
    
    # GitHub Triage - Read-Only Analyzer
    
    <role>
    Read-only GitHub triage orchestrator. Fetch open issues/PRs, classify, spawn 1 background `quick` subagent per item. Each subagent analyzes and writes a report file. ZERO GitHub mutations.
    </role>
    
    ## Architecture
    
    **1 ISSUE/PR = 1 `task_create` = 1 `quick` SUBAGENT (background). NO EXCEPTIONS.**
    
    | Rule | Value |
    |------|-------|
    | Category | `quick` |
    | Execution | `run_in_background=true` |
    | Parallelism | ALL items simultaneously |
    | Tracking | `task_create` per item |
    | Output | `/tmp/{YYYYMMDD-HHmmss}/issue-{N}.md` or `pr-{N}.md` |
    
    ---
    
    ## Zero-Action Policy (ABSOLUTE)
    
    <zero_action>
    Subagents MUST NEVER run ANY command that writes or mutates GitHub state.
    
    **FORBIDDEN** (non-exhaustive):
    `gh issue comment`, `gh issue close`, `gh issue edit`, `gh pr comment`, `gh pr merge`, `gh pr review`, `gh pr edit`, `gh api -X POST`, `gh api -X PUT`, `gh api -X PATCH`, `gh api -X DELETE`
    
    **ALLOWED**:
    - `gh issue view`, `gh pr view`, `gh api` (GET only) - read GitHub data
    - `Grep`, `Read`, `Glob` - read codebase
    - `Write` - write report files to `/tmp/` ONLY
    - `git log`, `git show`, `git blame` - read git history (for finding fix commits)
    
    **ANY GitHub mutation = CRITICAL violation.**
    </zero_action>
    
    ---
    
    ## Evidence Rule (MANDATORY)
    
    <evidence>
    **Every factual claim in a report MUST include a GitHub permalink as proof.**
    
    A permalink is a URL pointing to a specific line/range in a specific commit, e.g.:
    `https://github.com/{owner}/{repo}/blob/{commit_sha}/{path}#L{start}-L{end}`
    
    ### How to generate permalinks
    
    1. Find the relevant file and line(s) via Grep/Read.
    2. Get the current commit SHA: `git rev-parse HEAD`
    3. Construct: `https://github.com/{REPO}/blob/{SHA}/{filepath}#L{line}` (or `#L{start}-L{end}` for ranges)
    
    ### Rules
    
    - **No permalink = no claim.** If you cannot back a statement with a permalink, state "No evidence found" instead.
    - Claims without permalinks are explicitly marked `[UNVERIFIED]` and carry zero weight.
    - Permalinks to `main`/`master`/`dev` branches are NOT acceptable - use commit SHAs only.
    - For bug analysis: permalink to the problematic code. For fix verification: permalink to the fixing commit diff.
    </evidence>
    
    ---
    
    ## Phase 0: Setup
    
    ```bash
    REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
    REPORT_DIR="/tmp/$(date +%Y%m%d-%H%M%S)"
    mkdir -p "$REPORT_DIR"
    COMMIT_SHA=$(git rev-parse HEAD)
    ```
    
    Pass `REPO`, `REPORT_DIR`, and `COMMIT_SHA` to every subagent.
    
    ---
    
    ---
    
    ## Phase 1: Fetch All Open Items (CORRECTED)
    
    **IMPORTANT:** `body` and `comments` fields may contain control characters that break jq parsing. Fetch basic metadata first, then fetch full details per-item in subagents.
    
    ```bash
    # Step 1: Fetch basic metadata (without body/comments to avoid JSON parsing issues)
    ISSUES_LIST=$(gh issue list --repo $REPO --state open --limit 500 \
      --json number,title,labels,author,createdAt)
    ISSUE_COUNT=$(echo "$ISSUES_LIST" | jq length)
    
    # Paginate if needed
    if [ "$ISSUE_COUNT" -eq 500 ]; then
      LAST_DATE=$(echo "$ISSUES_LIST" | jq -r '.[-1].createdAt')
      while true; do
        PAGE=$(gh issue list --repo $REPO --state open --limit 500 \
          --search "created:<$LAST_DATE" \
          --json number,title,labels,author,createdAt)
        PAGE_COUNT=$(echo "$PAGE" | jq length)
        [ "$PAGE_COUNT" -eq 0 ] && break
        ISSUES_LIST=$(echo "$ISSUES_LIST" "$PAGE" | jq -s '.[0] + .[1] | unique_by(.number)')
        ISSUE_COUNT=$(echo "$ISSUES_LIST" | jq length)
        [ "$PAGE_COUNT" -lt 500 ] && break
        LAST_DATE=$(echo "$PAGE" | jq -r '.[-1].createdAt')
      done
    fi
    
    # Same for PRs
    PRS_LIST=$(gh pr list --repo $REPO --state open --limit 500 \
      --json number,title,labels,author,headRefName,baseRefName,isDraft,createdAt)
    PR_COUNT=$(echo "$PRS_LIST" | jq length)
    
    if [ "$PR_COUNT" -eq 500 ]; then
      LAST_DATE=$(echo "$PRS_LIST" | jq -r '.[-1].createdAt')
      while true; do
        PAGE=$(gh pr list --repo $REPO --state open --limit 500 \
          --search "created:<$LAST_DATE" \
          --json number,title,labels,author,headRefName,baseRefName,isDraft,createdAt)
        PAGE_COUNT=$(echo "$PAGE" | jq length)
        [ "$PAGE_COUNT" -eq 0 ] && break
        PRS_LIST=$(echo "$PRS_LIST" "$PAGE" | jq -s '.[0] + .[1] | unique_by(.number)')
        PR_COUNT=$(echo "$PRS_LIST" | jq length)
        [ "$PAGE_COUNT" -lt 500 ] && break
        LAST_DATE=$(echo "$PAGE" | jq -r '.[-1].createdAt')
      done
    fi
    
    echo "Total issues: $ISSUE_COUNT, Total PRs: $PR_COUNT"
    ```
    
    **LARGE REPOSITORY HANDLING:**
    If total items exceeds 50, you MUST process ALL items. Use the pagination code above to fetch every single open issue and PR.
    **DO NOT** sample or limit to 50 items - process the entire backlog.
    
    Example: If there are 500 open issues, spawn 500 subagents. If there are 1000 open PRs, spawn 1000 subagents.
    
    **Note:** Background task system will queue excess tasks automatically.
    
    
    ---
    
    ## Phase 2: Classify
    
    | Type | Detection |
    |------|-----------|
    | `ISSUE_QUESTION` | `[Question]`, `[Discussion]`, `?`, "how to" / "why does" / "is it possible" |
    | `ISSUE_BUG` | `[Bug]`, `Bug:`, error messages, stack traces, unexpected behavior |
    | `ISSUE_FEATURE` | `[Feature]`, `[RFE]`, `[Enhancement]`, `Feature Request`, `Proposal` |
    | `ISSUE_OTHER` | Anything else |
    | `PR_BUGFIX` | Title starts with `fix`, branch contains `fix/`/`bugfix/`, label `bug` |
    | `PR_OTHER` | Everything else |
    
    ---
    
    ## Phase 3: Spawn Subagents (Individual Tool Calls)
    
    **CRITICAL: Create tasks ONE BY ONE using individual `task_create` tool calls. NEVER batch or script.**
    
    For each item, execute these steps sequentially:
    
    ### Step 3.1: Create Task Record
    ```typescript
    task_create(
      subject="Triage: #{number} {title}",
      description="GitHub {issue|PR} triage analysis - {type}",
      metadata={"type": "{ISSUE_QUESTION|ISSUE_BUG|ISSUE_FEATURE|ISSUE_OTHER|PR_BUGFIX|PR_OTHER}", "number": {number}}
    )
    ```
    
    ### Step 3.2: Spawn Analysis Subagent (Background)
    ```typescript
    task(
      category="quick",
      run_in_background=true,
      load_skills=[],
      prompt=SUBAGENT_PROMPT
    )
    ```
    
    **ABSOLUTE RULES for Subagents:**
    - **ONLY ANALYZE** - Never take action on GitHub (no comments, merges, closes)
    - **READ-ONLY** - Use tools only for reading code/GitHub data
    - **WRITE REPORT ONLY** - Output goes to `{REPORT_DIR}/{issue|pr}-{number}.md` via Write tool
    - **EVIDENCE REQUIRED** - Every claim must have GitHub permalink as proof
    
    ```
    For each item:
      1. task_create(subject="Triage: #{number} {title}")
      2. task(category="quick", run_in_background=true, load_skills=[], prompt=SUBAGENT_PROMPT)
      3. Store mapping: item_number -> { task_id, background_task_id }
    ```
    
    ---
    
    ## Subagent Prompts
    
    ### Common Preamble (include in ALL subagent prompts)
    
    ```
    CONTEXT:
    - Repository: {REPO}
    - Report directory: {REPORT_DIR}
    - Current commit SHA: {COMMIT_SHA}
    
    PERMALINK FORMAT:
    Every factual claim MUST include a permalink: https://github.com/{REPO}/blob/{COMMIT_SHA}/{filepath}#L{start}-L{end}
    No permalink = no claim. Mark unverifiable claims as [UNVERIFIED].
    To get current SHA if needed: git rev-parse HEAD
    
    ABSOLUTE RULES (violating ANY = critical failure):
    - NEVER run gh issue comment, gh issue close, gh issue edit
    - NEVER run gh pr comment, gh pr merge, gh pr review, gh pr edit
    - NEVER run any gh command with -X POST, -X PUT, -X PATCH, -X DELETE
    - NEVER run git checkout, git fetch, git pull, git switch, git worktree
    - Your ONLY writable output: {REPORT_DIR}/{issue|pr}-{number}.md via the Write tool
    ```
    
    
    ---
    
    ### ISSUE_QUESTION
    
    ```
    You are analyzing issue #{number} for {REPO}.
    
    ITEM:
    - Issue #{number}: {title}
    - Author: {author}
    - Body: {body}
    - Comments: {comments_summary}
    
    TASK:
    1. Understand the question.
    2. Search the codebase (Grep, Read) for the answer.
    3. For every finding, construct a permalink: https://github.com/{REPO}/blob/{COMMIT_SHA}/{path}#L{N}
    4. Write report to {REPORT_DIR}/issue-{number}.md
    
    REPORT FORMAT (write this as the file content):
    
    # Issue #{number}: {title}
    **Type:** Question | **Author:** {author} | **Created:** {createdAt}
    
    ## Question
    [1-2 sentence summary]
    
    ## Findings
    [Each finding with permalink proof. Example:]
    - The config is parsed in [`src/config/loader.ts#L42-L58`](https://github.com/{REPO}/blob/{SHA}/src/config/loader.ts#L42-L58)
    
    ## Suggested Answer
    [Draft answer with code references and permalinks]
    
    ## Confidence: [HIGH | MEDIUM | LOW]
    [Reason. If LOW: what's missing]
    
    ## Recommended Action
    [What maintainer should do]
    
    ---
    REMEMBER: No permalink = no claim. Every code reference needs a permalink.
    ```
    
    ---
    
    ### ISSUE_BUG
    
    ```
    You are analyzing bug report #{number} for {REPO}.
    
    ITEM:
    - Issue #{number}: {title}
    - Author: {author}
    - Body: {body}
    - Comments: {comments_summary}
    
    TASK:
    1. Understand: expected behavior, actual behavior, reproduction steps.
    2. Search the codebase for relevant code. Trace the logic.
    3. Determine verdict: CONFIRMED_BUG, NOT_A_BUG, ALREADY_FIXED, or UNCLEAR.
    4. For ALREADY_FIXED: find the fixing commit using git log/git blame. Include the commit SHA and what changed.
    5. For every finding, construct a permalink.
    6. Write report to {REPORT_DIR}/issue-{number}.md
    
    FINDING "ALREADY_FIXED" COMMITS:
    - Use `git log --all --oneline -- {file}` to find recent changes to relevant files
    - Use `git log --all --grep="fix" --grep="{keyword}" --all-match --oneline` to search commit messages
    - Use `git blame {file}` to find who last changed the relevant lines
    - Use `git show {commit_sha}` to verify the fix
    - Construct commit permalink: https://github.com/{REPO}/commit/{fix_commit_sha}
    
    REPORT FORMAT (write this as the file content):
    
    # Issue #{number}: {title}
    **Type:** Bug Report | **Author:** {author} | **Created:** {createdAt}
    
    ## Bug Summary
    **Expected:** [what user expects]
    **Actual:** [what actually happens]
    **Reproduction:** [steps if provided]
    
    ## Verdict: [CONFIRMED_BUG | NOT_A_BUG | ALREADY_FIXED | UNCLEAR]
    
    ## Analysis
    
    ### Evidence
    [Each piece of evidence with permalink. No permalink = mark [UNVERIFIED]]
    
    ### Root Cause (if CONFIRMED_BUG)
    [Which file, which function, what goes wrong]
    - Problematic code: [`{path}#L{N}`](permalink)
    
    ### Why Not A Bug (if NOT_A_BUG)
    [Rigorous proof with permalinks that current behavior is correct]
    
    ### Fix Details (if ALREADY_FIXED)
    - **Fixed in commit:** [`{short_sha}`](https://github.com/{REPO}/commit/{full_sha})
    - **Fixed date:** {date}
    - **What changed:** [description with diff permalink]
    - **Fixed by:** {author}
    
    ### Blockers (if UNCLEAR)
    [What prevents determination, what to investigate next]
    
    ## Severity: [LOW | MEDIUM | HIGH | CRITICAL]
    
    ## Affected Files
    [List with permalinks]
    
    ## Suggested Fix (if CONFIRMED_BUG)
    [Specific approach: "In {file}#L{N}, change X to Y because Z"]
    
    ## Recommended Action
    [What maintainer should do]
    
    ---
    CRITICAL: Claims without permalinks are worthless. If you cannot find evidence, say so explicitly rather than making unverified claims.
    ```
    
    ---
    
    ### ISSUE_FEATURE
    
    ```
    You are analyzing feature request #{number} for {REPO}.
    
    ITEM:
    - Issue #{number}: {title}
    - Author: {author}
    - Body: {body}
    - Comments: {comments_summary}
    
    TASK:
    1. Understand the request.
    2. Search codebase for existing (partial/full) implementations.
    3. Assess feasibility.
    4. Write report to {REPORT_DIR}/issue-{number}.md
    
    REPORT FORMAT (write this as the file content):
    
    # Issue #{number}: {title}
    **Type:** Feature Request | **Author:** {author} | **Created:** {createdAt}
    
    ## Request Summary
    [What the user wants]
    
    ## Existing Implementation: [YES_FULLY | YES_PARTIALLY | NO]
    [If exists: where, with permalinks to the implementation]
    
    ## Feasibility: [EASY | MODERATE | HARD | ARCHITECTURAL_CHANGE]
    
    ## Relevant Files
    [With permalinks]
    
    ## Implementation Notes
    [Approach, pitfalls, dependencies]
    
    ## Recommended Action
    [What maintainer should do]
    ```
    
    ---
    
    ### ISSUE_OTHER
    
    ```
    You are analyzing issue #{number} for {REPO}.
    
    ITEM:
    - Issue #{number}: {title}
    - Author: {author}
    - Body: {body}
    - Comments: {comments_summary}
    
    TASK: Assess and write report to {REPORT_DIR}/issue-{number}.md
    
    REPORT FORMAT (write this as the file content):
    
    # Issue #{number}: {title}
    **Type:** [QUESTION | BUG | FEATURE | DISCUSSION | META | STALE]
    **Author:** {author} | **Created:** {createdAt}
    
    ## Summary
    [1-2 sentences]
    
    ## Needs Attention: [YES | NO]
    ## Suggested Label: [if any]
    ## Recommended Action: [what maintainer should do]
    ```
    
    ---
    
    ### PR_BUGFIX
    
    ```
    You are reviewing PR #{number} for {REPO}.
    
    ITEM:
    - PR #{number}: {title}
    - Author: {author}
    - Base: {baseRefName} <- Head: {headRefName}
    - Draft: {isDraft} | Mergeable: {mergeable}
    - Review: {reviewDecision} | CI: {statusCheckRollup_summary}
    - Body: {body}
    
    TASK:
    1. Fetch PR details (READ-ONLY): gh pr view {number} --repo {REPO} --json files,reviews,comments,statusCheckRollup,reviewDecision
    2. Read diff: gh api repos/{REPO}/pulls/{number}/files
    3. Search codebase to verify fix correctness.
    4. Write report to {REPORT_DIR}/pr-{number}.md
    
    REPORT FORMAT (write this as the file content):
    
    # PR #{number}: {title}
    **Type:** Bugfix | **Author:** {author}
    **Base:** {baseRefName} <- {headRefName} | **Draft:** {isDraft}
    
    ## Fix Summary
    [What bug, how fixed - with permalinks to changed code]
    
    ## Code Review
    
    ### Correctness
    [Is fix correct? Root cause addressed? Evidence with permalinks]
    
    ### Side Effects
    [Risky changes, breaking changes - with permalinks if any]
    
    ### Code Quality
    [Style, patterns, test coverage]
    
    ## Merge Readiness
    
    | Check | Status |
    |-------|--------|
    | CI | [PASS / FAIL / PENDING] |
    | Review | [APPROVED / CHANGES_REQUESTED / PENDING / NONE] |
    | Mergeable | [YES / NO / CONFLICTED] |
    | Draft | [YES / NO] |
    | Correctness | [VERIFIED / CONCERNS / UNCLEAR] |
    | Risk | [NONE / LOW / MEDIUM / HIGH] |
    
    ## Files Changed
    [List with brief descriptions]
    
    ## Recommended Action: [MERGE | REQUEST_CHANGES | NEEDS_REVIEW | WAIT]
    [Reasoning with evidence]
    
    ---
    NEVER merge. NEVER comment. NEVER review. Write to file ONLY.
    ```
    
    ---
    
    ### PR_OTHER
    
    ```
    You are reviewing PR #{number} for {REPO}.
    
    ITEM:
    - PR #{number}: {title}
    - Author: {author}
    - Base: {baseRefName} <- Head: {headRefName}
    - Draft: {isDraft} | Mergeable: {mergeable}
    - Review: {reviewDecision} | CI: {statusCheckRollup_summary}
    - Body: {body}
    
    TASK:
    1. Fetch PR details (READ-ONLY): gh pr view {number} --repo {REPO} --json files,reviews,comments,statusCheckRollup,reviewDecision
    2. Read diff: gh api repos/{REPO}/pulls/{number}/files
    3. Write report to {REPORT_DIR}/pr-{number}.md
    
    REPORT FORMAT (write this as the file content):
    
    # PR #{number}: {title}
    **Type:** [FEATURE | REFACTOR | DOCS | CHORE | TEST | OTHER]
    **Author:** {author}
    **Base:** {baseRefName} <- {headRefName} | **Draft:** {isDraft}
    
    ## Summary
    [2-3 sentences with permalinks to key changes]
    
    ## Status
    
    | Check | Status |
    |-------|--------|
    | CI | [PASS / FAIL / PENDING] |
    | Review | [APPROVED / CHANGES_REQUESTED / PENDING / NONE] |
    | Mergeable | [YES / NO / CONFLICTED] |
    | Risk | [LOW / MEDIUM / HIGH] |
    | Alignment | [YES / NO / UNCLEAR] |
    
    ## Files Changed
    [Count and key files]
    
    ## Blockers
    [If any]
    
    ## Recommended Action: [MERGE | REQUEST_CHANGES | NEEDS_REVIEW | CLOSE | WAIT]
    [Reasoning]
    
    ---
    NEVER merge. NEVER comment. NEVER review. Write to file ONLY.
    ```
    
    ---
    
    ## Phase 4: Collect & Update
    
    Poll `background_output()` per task. As each completes:
    1. Parse report.
    2. `task_update(id=task_id, status="completed", description=REPORT_SUMMARY)`
    3. Stream to user immediately.
    
    ---
    
    ## Phase 5: Final Summary
    
    Write to `{REPORT_DIR}/SUMMARY.md` AND display to user:
    
    ```markdown
    # GitHub Triage Report - {REPO}
    
    **Date:** {date} | **Commit:** {COMMIT_SHA}
    **Items Processed:** {total}
    **Report Directory:** {REPORT_DIR}
    
    ## Issues ({issue_count})
    | Category | Count |
    |----------|-------|
    | Bug Confirmed | {n} |
    | Bug Already Fixed | {n} |
    | Not A Bug | {n} |
    | Needs Investigation | {n} |
    | Question Analyzed | {n} |
    | Feature Assessed | {n} |
    | Other | {n} |
    
    ## PRs ({pr_count})
    | Category | Count |
    |----------|-------|
    | Bugfix Reviewed | {n} |
    | Other PR Reviewed | {n} |
    
    ## Items Requiring Attention
    [Each item: number, title, verdict, 1-line summary, link to report file]
    
    ## Report Files
    [All generated files with paths]
    ```
    
    ---
    
    ## Anti-Patterns
    
    | Violation | Severity |
    |-----------|----------|
    | ANY GitHub mutation (comment/close/merge/review/label/edit) | **CRITICAL** |
    | Claim without permalink | **CRITICAL** |
    | Using category other than `quick` | CRITICAL |
    | Batching multiple items into one task | CRITICAL |
    | `run_in_background=false` | CRITICAL |
    | `git checkout` on PR branch | CRITICAL |
    | Guessing without codebase evidence | HIGH |
    | Not writing report to `{REPORT_DIR}` | HIGH |
    | Using branch name instead of commit SHA in permalink | HIGH |
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related