- name
- issue-resolver
- description
- Create an atomic PR closing a GitHub issue end-to-end via a 6-step pipeline. Use to resolve, fix, or implement issue #N. Don't use for analysis without fixing (/issue-analysis), reviewing a PR (/issue-pr-review), or bulk backlog work (/auto-pilot).
- license
- MIT
- compatibility
- Requires git and GitHub CLI (gh) with authentication and push access. Self-contained — uses shared agents from shared/agents/.
- metadata
- {"version":"0.20.1","author":"Luong NGUYEN <luongnv89@gmail.com>","effort":"max"}
# /issue-resolver N
Issue → atomic PR in 6 steps.
## Invocation
| Invocation (`N` = issue number) | What happens |
|------------|--------------|
| `/issue-resolver <N>` | Interactive; user picks the plan |
| `/issue-resolver <N> --auto` | No prompts; set by `/auto-pilot` |
| `/issue-resolver <N> --no-run-log` | Modifier, **orthogonal to `--auto`**: append nothing to `.gitissue/runs.jsonl`, return telemetry (*Run-log entry*) |
## Prerequisites
Run these checks in order before any operation. A non-zero exit or empty output stops the run: print that check's `references/error-messages.md` error and the *Closing Summary* `BLOCKED` block.
1. Git repository: `git rev-parse --git-dir`.
2. `gh` installed: `which gh`.
3. `gh` authenticated: `gh auth status`.
4. Remote present: `git remote -v` prints at least one line.
## Repo Sync Before Edits (mandatory)
In-place path only; worktree paths start from the fetched base, and an invalid `IDD_CALLER_WORKTREE=1` is a stop, never a sync bypass. Use the **stash-first pattern**: copy the snippet and recovery from `references/docs/sync-conventions.md` (*Quick Reference (Copy-Paste Snippet)*), never a bare rebase on a dirty tree. A missing `origin` or a conflict stops and asks (interactive), or aborts (auto).
## Configuration
Load config once; never re-read it. Run `python3 references/scripts/gi-config.py` — **Working directory:** the repo root; **Script path:** absolute, as the *Bundled dependency precheck* resolves its list. It prints `{"config": …, "config_file": …, "first_run": …}` merged over the defaults below (rationale: `references/steps/step-0-preflight.md`, *Configuration load*).
- **Exit 0** — use `config`.
- **Exit 3** — invalid `.gitissue.yml`: print the `references/error-messages.md` error (*Invalid config*), stop.
- **Script file absent** — a broken install and not a degrade: stop, print `✗ Missing bundled dependency`.
- **Anything else** (no `python3`, non-zero exit, unparsable stdout) — print `⚠ gi-config unavailable — reading .gitissue.yml by hand` and read it yourself *instead of* the script, over the keys and defaults below.
Either path: no `.gitissue.yml` (`first_run`) prints `○ First run — using default config. Run /init-gitissue to customize.`
**Capture the run clock here:** chain that same `python3` invocation as `python3 …; ec=$?; date +%s >&2; exit "$ec"`; the stderr epoch is `run_started_epoch`, the *Run Stats Footer*'s `elapsed` anchor. Take `date +%s` again as each `[N/5]` step starts and at the terminal outcome: those boundaries are the run log's `phases` (`references/report-templates.md`).
Defaults and behavior per key: `references/docs/config-schema.md` — `issue.auto_normalize` · `resolve.approval_gate` · `resolve.branch_prefix` · `resolve.auto_test` · `resolve.test_timeout` · `resolve.max_commits` · `resolve.qa_max_cycles` · `resolve.adaptive_effort` · `resolve.ui_review.browser_review` · `resolve.borrow_skills: false`.
---
## Subagent Architecture
Heavy work goes to subagents (`shared/agents/`), keeping the main agent's **context window** lean: it owns Step 0, Step 4's loop and Step 5; Steps 1-3 each spawn one (*Subagent Architecture Diagram*). Prompts: `references/agents/<name>.md`; conventions: `references/docs/shared-agent-conventions.md`. Without the Agent tool (e.g. Claude.ai), run each step inline via its fallback instructions.
### Spawning a subagent (canonical pattern)
Role, description and prompt file change per step. **Do NOT set `subagent_type`**:
```python
Agent(
description="{role} — {action} issue #N",
prompt=<{agent-file}.md prompt with {variables} replaced>,
# never subagent_type — the default general-purpose agent, not "code-reviewer"
)
```
**Per-role overrides:** at every spawn apply `references/docs/agent-overrides.md` — the resolved `agents.model.<role>` / `agents.effort.<role>` for the spawned role (`codebase-researcher`, `synthesizer`, `implementer`, `code-reviewer`, `ui-reviewer`, `fixer`); `null` (the default) passes nothing.
### Orchestrating the agents
Name the role in `description`, size model/effort per `references/docs/agent-model-effort.md`, and read each return before advancing: a missing or blocking return stops (interactive) or takes the auto behavior (*Orchestrating the agents*).
### Bundled dependency precheck
Check every path below exists relative to this SKILL.md's directory. A missing path stops the run: print the `✗ Missing bundled dependency` block from `references/error-messages.md` (*Bundled dependencies*); never guess a prompt.
```text
references/agents/codebase-researcher.md
references/agents/synthesizer.md
references/agents/implementer.md
references/agents/code-reviewer.md
references/agents/ui-reviewer.md
references/agents/fixer.md
references/pipeline-steps.md
references/steps/step-0-preflight.md
references/steps/step-0h-analysis-reuse.md
references/steps/step-0i-caller-payload.md
references/steps/step-1-research.md
references/steps/step-2-plan.md
references/steps/step-3-implement.md
references/steps/step-4-qa.md
references/steps/step-5-deliver.md
references/report-templates.md
references/run-stats.md
references/bug-verification.md
references/skill-index.md
references/error-messages.md
references/docs/sync-conventions.md
references/docs/naming-conventions.md
references/docs/pre-commit-security.md
references/docs/idd-methodology.md
references/docs/github-projects-sync.md
references/docs/run-log-schema.md
references/docs/config-schema.md
references/docs/agent-model-effort.md
references/docs/agent-overrides.md
references/docs/shared-agent-conventions.md
references/docs/platform-github.md
references/docs/terminal-style.md
references/docs/ui-review.md
references/scripts/gi-config.py
references/scripts/gi-runlog.py
references/scripts/gi-secscan.py
references/scripts/gi-branch.py
references/scripts/gi-gh.py
references/scripts/gi-issue.py
references/scripts/gi-state.py
references/scripts/gi-receipt.py
references/scripts/gi-sensitive.py
references/scripts/gi-premise.py
references/scripts/gi-sketch.py
references/scripts/gi-recipe.py
```
---
## Pipeline Overview
6 steps (0-5) — Preflight, Research, Plan, Implement, QA, Deliver — each printing a `[N/5]` line (`●` → `✓`/`✗`); expected output example: `references/report-templates.md` (*Expected Inline Pipeline Output*).
### Step completion reports
Each step closes with `√`/`×` per check and a `Result: PASS | PARTIAL | FAIL` line (*Step Completion Reports* in `references/report-templates.md`, **read it now**). A step is incomplete until `Result:` prints.
---
## Step 0 — Preflight
Open with `● Preflight check for issue #N...`.
### 0a — Fetch issue
GitHub reads share the boundary in `references/scripts/gi-gh.py`. Classify any framed caller payload first (`/auto-pilot` captured it in mode-neutral *Step 1.2b*); *Step 0i — Caller payload gate* in `references/steps/step-0i-caller-payload.md` is its single home — `issue_payload = supplied | partial | absent`, the mandatory live `gh issue view N --json state,comments,updatedAt` under `supplied`, the exact match between retained and live `updatedAt` before 0d, discard-and-refresh on mismatch (identical for individual, array, and keyed-map payloads), never a gate. Then run `python3 references/scripts/gi-issue.py {N} --fields number,title,body,labels,assignees,state,comments,updatedAt`, reading `.issue` unless the gate substitutes a payload. **Capture `updatedAt` here** — 0d's `gh issue edit` bumps it, so *0h* uses this pre-normalization value. Exit 3 stops; anything else degrades to `gh issue view {N} --json number,title,body,labels,assignees,state,comments,updatedAt`. **0d rewrites the body, so it MUST end with `python3 references/scripts/gi-issue.py {N} --invalidate`.** <!-- a:rs-0a-payload-concurrency -->
**Not found:** error, stop. **Closed:** warning, stop.
### 0b — Check for existing work
1. List branches: `git branch -a | grep -i "{N}"`.
2. List open PRs: `gh pr list --state open --json number,title,body,headRefName --limit 20`. A PR targets the issue when its body has `Closes #N`, `Fixes #N` or `Resolves #N`.
3. If an **open** PR targets it: print the `⚠ PR already targets issue` block from `references/error-messages.md` (*Guards*), stop, return `status: pr_in_progress` with its `pr_number` and `branch_name`, and **never close the issue** (*Early exit*).
Only a **merged** PR or a closing commit on the default branch is `already_resolved`.
### 0c — Guards
**Interactive:** warn and ask if assigned elsewhere, or on `wontfix`/`blocked`/`do-not-merge` labels. **Auto:** skip the assignment guard, log blocking labels, never stop.
### 0d — Auto-normalize
If `issue.auto_normalize` is true and the body lacks a `<!-- gitissue:normalized v1 -->` marker:
1. **Security label check (SPEC §1.4)** — before any rewrite scan labels for `security`, `CVE`, `vulnerability` (case-insensitive). On a match:
- **Auto mode (`--auto` / `IDD_AUTO_MODE=1`):** print the `⚠ … Skipping auto-normalization` warning (`references/error-messages.md` → *Security-labeled issue (skip)*), first matching label as `{label}`, continue **without** rewriting.
- **Interactive mode:** same warning, then ask for explicit operator confirmation; default **no**, a decline continuing without normalization.
2. **Normalize inline** — otherwise, structure-only as `/issue-creator` Normalize mode (the resolver does **not** invoke `/issue-creator` as a subprocess):
1. Classify the type and generate the body with the marker.
2. Back up the original body in a comment.
3. Run `gh issue edit`, then `python3 references/scripts/gi-issue.py {N} --invalidate`, then re-fetch.
4. On any failure, **warn** and continue with the original body (`references/error-messages.md` → *Auto-normalization failed*).
### 0e — Workspace (interactive only)
Derive one `{branch_name}` first: `python3 references/scripts/gi-branch.py {N} --from-issue --type {type}`, reading `.branch`. **`--from-issue` is mandatory** — never put the issue title or configured prefix on the command line (`references/steps/step-0-preflight.md` → *Step 0e — Workspace*). `{type}` is one of six classified literals. Exit 3 stops; anything else degrades to `references/docs/naming-conventions.md`.
**Auto mode (`--auto` / `IDD_AUTO_MODE=1`): skip this offer entirely.** A set `IDD_CALLER_WORKTREE=1` uses the validated caller-managed path; otherwise go to *0f*.
**Interactive mode:** offer a git *worktree*, so work never touches the user's tree:
```
◆ Workspace for issue #N
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
Branch: {branch_name}
Worktree: ../{repo}-worktrees/{branch_name with / → -}
Setup: copies your gitignored local config (.env*, and similar),
then runs this project's detected install/bootstrap.
Accepting keeps your current working tree untouched. Declining uses the current working tree with the existing sync and branch behavior.
Resolve in a new worktree? [Y/n]
```
Accept replaces *0f — Create branch* and the mandatory Repo Sync; decline runs mandatory Repo Sync then *0f*. Creation, setup, cleanup: *Step 0e — Workspace*.
### 0f — Create branch
The **in-place path**; worktree paths already checked out the branch. Reuse the derived `{branch_name}` (*0e — Workspace*, `references/docs/naming-conventions.md`). **If it exists:** interactive asks `continue` or `fresh`, auto takes `continue`.
### 0g — Complexity gate (select the pipeline profile)
Decide **before Step 1** how much pipeline this issue earns; the `XS … XL` scale and safety rules: `references/docs/agent-model-effort.md` (*Complexity → pipeline profile*). `resolve.adaptive_effort: false` pins `full`; else read the **pre-work `Effort` band** in `## Metadata`, never later agent output:
- `XS`/`S` asserted (not `(needs review)`) → `light`; `M`/`L`/`XL` → `full`
- `XS`/`S` low-confidence, **or** absent/unparseable → `full` (ambiguous → fuller)
**What `light` collapses — the single home for this rule.** Mechanics: the *`light` profile* subsection of `references/steps/step-1-research.md` and `references/steps/step-2-plan.md`.
| Step | `light` behavior |
|------|------------------|
| 1 — Research | Lighter pass; the already-resolved check still runs. |
| 2 — Plan | Do **not** spawn the synthesizer; derive a minimal plan inline, no design-confirm checkpoint; the sensitive-change gate **still runs**. **Unless *0h* set `analysis_reuse = fresh`**: *Step 2 — Plan → `reuse`* governs, options are **lifted**, and the design-confirm checkpoint **does** apply. |
| 3 — Propose relevant skills | Skip propose/install; `selected_skills = []`. **Leftover teardown still runs** (*Step 3*), except in a parallel lane (`IDD_CALLER_WORKTREE=1`). |
| 4 — QA | Cap the loop at **1** cycle; one reviewer spawn still runs. |
| 5 — Deliver | **Unchanged**. |
GitHub에서 보기