plugins/dev/skills/review-comments/SKILL.md
Systematically pull, categorize, and address all PR review comments — code change requests, questions, and suggestions. This skill fetches comments via gh api, groups them by file, implements fixes, handles disagreements diplomatically, and pushes a single commit. You should not try to handle PR review feedback manually — this skill ensures nothing gets missed. **ALWAYS consult this skill when** the user says 'address comments', 'fix review feedback', 'handle PR comments', 'respond to reviewers', 'address review', 'review feedback', or mentions that a PR has unresolved comments or review threads. Also used by /oneshot Phase 5 to process reviewer feedback before merging.
npx skillsauth add coalesce-labs/catalyst review-commentsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Pull PR review comments and feedback, understand the reviewer's intent, implement fixes, and push updates. The goal is to resolve all actionable feedback in a single pass so the PR can move forward.
If $ARGUMENTS provides a PR number, use it. Otherwise, detect the current PR:
PR_NUMBER=$(gh pr view --json number --jq '.number' 2>/dev/null)
If no PR is found, ask the user for the PR number.
Gather all review feedback from the PR:
# Get repo info
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
# Get PR review comments (inline code comments) — includes file path and line
gh api "repos/${REPO}/pulls/${PR_NUMBER}/comments" \
--jq '.[] | {id: .id, path: .path, line: .line, body: .body, user: .user.login, created: .created_at, in_reply_to: .in_reply_to_id}'
# Get PR reviews (top-level review bodies with approval state)
gh api "repos/${REPO}/pulls/${PR_NUMBER}/reviews" \
--jq '.[] | {id: .id, state: .state, body: .body, user: .user.login}'
# Get issue comments (general PR conversation)
gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" \
--jq '.[] | {id: .id, body: .body, user: .user.login, created: .created_at}'
Group comments into threads using in_reply_to_id — read the full thread before acting on any
individual comment, since later replies may refine or resolve earlier ones.
| Category | Action | |----------|--------| | Code change requested | Implement the fix | | Question / clarification | Read context and draft a reply | | Suggestion (optional) | Evaluate — implement if it improves the code, explain trade-off if not | | Approval / praise | No action needed | | Already resolved | Skip (check if thread is marked resolved) |
For each actionable comment, note:
For each actionable comment, in order:
Handling disagreements: If a reviewer's suggestion would introduce a regression, reduce type safety, or conflict with project conventions, don't silently ignore it. Draft a respectful reply explaining the trade-off and let the user decide whether to post it. Present it as:
Reviewer @name suggested X on file.ts:42.
I think this would [concern]. Draft reply:
"Thanks for the suggestion! I considered X but went with Y because [reason].
Happy to discuss if you feel strongly about this."
Post this reply? [y/N]
When CATALYST_PHASE is set or --headless is passed as an argument, this skill runs in a
mode safe for claude --bg workers (no stdin available):
resolveReviewThread mutation (same as the interactive path). Unchanged.Post this reply? [y/N] prompt is SKIPPED in headless mode.
Instead, the thread is left unresolved and a structured record is appended to the ticket's
worker directory under the orchestrator dir:
${CATALYST_ORCHESTRATOR_DIR:-${ORCH_DIR:-.}}/workers/${CATALYST_TICKET:-unknown}/.review-escalations.jsonl:
{"prNumber":42,"threadId":"T1","path":"a.ts","line":5,"finding":"…","why":"…"}
(Resolve CATALYST_ORCHESTRATOR_DIR first — a claude --bg worker receives that var, not
ORCH_DIR, which is only exported inside the recovery-pass skill's own prelude. Keying off
ORCH_DIR alone wrote the record to ./workers/<ticket> in the worktree, where the recovery-pass
caller could never find it — CTL-1496.) The caller (recovery-pass worker) reads
.review-escalations.jsonl from that same path to author a curated escalation brief — one line per
genuine judgment call — and escalates only those, with the PR number and thread linked.CATALYST_PHASE is set nor --headless is
passed, the existing [y/N] prompt behaviour is unchanged.After all changes are made, stage only the files that were modified to address comments:
# Stage specific changed files (NOT git add -A which could catch unrelated changes)
git add path/to/changed-file1.ts path/to/changed-file2.ts
git commit -m "address review comments from PR #${PR_NUMBER}"
git push
After pushing fixes (or posting replies for disagreements), resolve each addressed thread on GitHub so it no longer blocks merge under branch protection rules that require resolved conversations.
Read and follow "${CLAUDE_PLUGIN_ROOT}/references/review-thread-resolution.md" for the full
workflow. Summary:
resolveReviewThread mutationResolution rules:
## PR Review Comments — #${PR_NUMBER}
### Comments Addressed
1. **@reviewer** on `path/to/file.ts:42`
- Comment: "This should use optional chaining instead of non-null assertion"
- Action: Changed `user!.name` to `user?.name ?? ''`
2. **@reviewer** on `path/to/file.ts:89`
- Comment: "Missing error handling for the API call"
- Action: Added try/catch with proper error propagation
### Questions Answered
3. **@reviewer** on general
- Question: "Why did you choose X over Y?"
- Reply: {drafted reply — post via gh api if requested}
### Disagreements (Needs Decision)
4. **@reviewer** on `path/to/file.ts:120`
- Suggestion: "Use a map instead of switch"
- Analysis: The switch is more readable here and has exhaustiveness checking.
- Draft reply ready — awaiting your decision.
### No Action Needed
5. **@reviewer**: "LGTM" (approval)
### Summary
- Code changes: {N}
- Questions answered: {N}
- Disagreements flagged: {N}
- Skipped (resolved/approval): {N}
- Commit: {short hash} pushed to branch
development
Migrate a single-harness repo to the dual-harness layout so both Claude Code and Codex load the same instructions and skills — AGENTS.md as the portable canonical doc, a thin CLAUDE.md `@AGENTS.md` bridge, and a `.agents/skills` dir with a `.claude/skills` symlink onto it. Use when asked to migrate to dual-harness, make this repo work in both Claude and Codex, or for agent metadata cleanup.
tools
Goal-driven senior-engineer pipeline-unstick sweep (CTL-1176 rung 3). Given the stuck/failed/needs-human set (or ONE ticket handed by the recovery router), its GOAL is to get the pipeline MOVING again — not to fix one ticket's review findings (that is phase-remediate). It runs AFTER the eyes (diagnostician evidence) and the hands (deterministic unstuck-sweep seams) have already tried, and it CONSUMES their output from a recovery-pass.json brief rather than re-diagnosing or redoing their narrow work. It acts like a senior engineer with full tool access — it resolves merge conflicts, rebases, force-pushes, merges green PRs, and re-dispatches stalled phases AUTONOMOUSLY — and escalates to the operator ONLY for a genuine value judgment / something that degrades other functionality / a real cost-benefit trade-off / a serious architecture change / an ADR conflict. On escalation it AUTHORS the operator inbox row + the push notification (executive-voiced). Dispatched as a `claude --bg` job by phase-agent-dispatch via slash command, AND invocable bare by the operator as a sweep — hence `user-invocable: true`. Ships behind CATALYST_RECOVERY_PASS (off by default — no live behavior change until shadow/enforce).
tools
Diagnose and fix Catalyst setup issues. Validates tools, database, config, OTel, direnv, and thoughts. Automatically fixes what it can — creates directories, initializes the database, sets WAL mode, runs migrations. Use for new installs, upgrades, or when something isn't working.
tools
--- name: phase-triage description: Phase agent that triages a Linear ticket — expands acronyms, classifies (feature/bug/docs/refactor/chore), identifies genuine blockers (a semantic second-pass over the backlog — NOT a prose scrape; CTL-838), estimates scope, writes triage.json, and posts a triage analysis comment to Linear. Triage completion is signaled by that comment plus the local triage.json — there is no `triaged` label. Emits phase.triage.complete.<TICKET> on success and phase.triage.fai