원클릭으로
issue-triage
Decision-ready triage — classify, dedupe, and emit a verdict + next action per new GitHub issue
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Decision-ready triage — classify, dedupe, and emit a verdict + next action per new GitHub issue
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Audit every enabled skill's upstream file dependencies for staleness — flags chained skills about to consume yesterday's article or a long-dead topic file
Audit .github/workflows and composite actions with zizmor + actionlint, classify findings against the prior audit, auto-fix Critical/High regressions, and open a PR only when something actually changed.
Weekly API cost report — computes dollar costs from token usage, flags anomalies, forecasts burn, and prescribes concrete optimizations
Weekly partial-correlation of compute economics against a Hyperliquid macro basket — DePIN-token proxy track runs every week (n>180d), sweep-P&L track defers until n≥30 joined days
Score frontier LLMs (Claude, GPT, Gemini, DeepSeek, Grok) on a private compute-markets task corpus, track score deltas across releases, flag public-vs-private divergence
Save a note as one or more atomic notes under memory/notes/ (and optionally Supernotes). Splits bundled inputs into separate atomic files.
| name | Issue Triage |
| description | Decision-ready triage — classify, dedupe, and emit a verdict + next action per new GitHub issue |
| var | |
| tags | ["dev"] |
${var} — Repo (
owner/repo) to triage. If empty, triages all watched repos.
If ${var} is set and does not match owner/repo, abort with issue-triage: invalid var — expected owner/repo and exit. If ${var} is set, target only that repo.
A maintainer's scarce resource is decision time, not labels. For every new issue this skill emits one of four verdicts so the maintainer can act in under 30 seconds:
| Verdict | Meaning | Action taken |
|---|---|---|
| ACCEPT | Clear, in-scope, actionable | Apply type + priority labels (+ area if inferable); suggest reviewer if CODEOWNERS resolves |
| NEEDS-INFO | Missing repro, version, env, or scope | Apply needs-info; post ≤3 specific questions |
| DUPLICATE | Overlaps an open or recently-closed issue | Apply duplicate; reference the original; close only if high-confidence |
| DECLINE | Off-topic, out-of-scope, or spam | Apply wontfix or invalid; suggest alternative venue if any |
Reads repos from memory/watched-repos.md:
# memory/watched-repos.md
- owner/repo
- another-owner/another-repo
If the file is missing and ${var} is empty, log ISSUE_TRIAGE_OK no-watched-repos and exit.
Read memory/MEMORY.md for context.
Read memory/triaged-issues.json (if present; else treat as {}) for previously-triaged issue numbers per repo.
Read the last 7 days of memory/logs/ as a fallback dedup signal.
If ${var} is set → targets = [${var}]. Else targets = non-comment, non-blank lines from memory/watched-repos.md with - prefix stripped.
Per-run budget: ≤10 new issues per repo (tunable — raise/lower as repo volume demands). If more, triage the N oldest and log the overflow.
For each target repo, first detect write access (determines whether this run can apply labels/comments/close, or must run advisory-only):
# One probe per repo. push OR triage permission is enough to label/comment/close.
# Capture stdout AND exit status separately so a genuine "no access" (a clean
# `false` from GitHub) is distinguished from a transient command failure
# (rate limit, network hiccup, outage), which must NOT be read as no-access.
probe=$(gh api "repos/owner/repo" --jq '.permissions.push or .permissions.triage' 2>/dev/null)
status=$?
Branch on status and probe — there are three distinct outcomes, and only an explicit false may trigger advisory mode:
status != 0 (probe errored) — the call failed transiently; permission is unknown, not denied. Skip this repo for the entire run: do not classify, do not apply any write, and do not record anything to memory/triaged-issues.json for it. Recording on an errored run would permanently skip those issues on future runs (§6/§8) even though no labels/comments were ever applied. Log ISSUE_TRIAGE_WARN probe-failed owner/repo and move to the next target.status == 0 and probe == "false" — GitHub answered and the token lacks write/triage permission → set advisory[owner/repo] = true. In advisory mode the run still classifies and emits verdicts, but must not attempt any gh label create, gh issue edit, gh issue comment, or gh issue close call — those fail with HTTP 404 / does not have the correct permissions and are pure noise. Instead it records verdicts to a memory report and surfaces them via notify (see §5–6, §9).status == 0 and probe == "true" — run normally (apply labels + comments + close per the rules below).# Cache existing label set once per repo so we know which labels to auto-create
gh label list -R owner/repo --json name,color --limit 200 > .cache/labels-${owner}-${repo}.json
# Open issues opened in the last 48h (gh issue list excludes PRs by default)
gh issue list -R owner/repo --state open \
--json number,title,body,labels,author,createdAt,comments,reactionGroups \
--search "created:>=$(date -u -d '48 hours ago' +%Y-%m-%d)" \
--limit 25
Filter out issues whose number is already present in memory/triaged-issues.json["owner/repo"] or already carries any of type:*, priority:*, needs-info, duplicate, wontfix, invalid, urgent, bug, feature, question, docs, chore, security, good-first-issue. (Pre-existing labels = already triaged.)
If zero candidates across all repos, log ISSUE_TRIAGE_OK no-new-issues and exit.
For each candidate, query recent issues (open + closed) on the same repo:
gh search issues "is:issue" --repo owner/repo --limit 50 \
--json number,title,state,url,createdAt
Mark as likely duplicate if any of:
#123, "same as", "related to")If duplicate: verdict = DUPLICATE, skip classification, record the referenced issue number.
From title + body + first 3 comments, emit a classification record:
bug, feature, question, docs, chore, securityp0 (security / data loss / outage / blocker), p1 (high-impact bug or high-demand feature), p2 (normal), p3 (nice-to-have)area is identifiedVerdict:
type=security OR priority=p0 → verdict ACCEPT with urgent label addedneeds-info=true → verdict NEEDS-INFOAdvisory mode (advisory[owner/repo] == true): skip this entire step. Do not create or add labels — collect the would-be label set into the issue's advisory record (§6) instead.
Collect the full label set for the issue. For each label:
| Label | Color | Description |
|---|---|---|
bug, feature, question, docs, chore, security | #1d76db | type: |
priority:p0 | #b60205 | priority: critical |
priority:p1 | #d93f0b | priority: high |
priority:p2 | #fbca04 | priority: normal |
priority:p3 | #c5def5 | priority: low |
needs-info | #fbca04 | awaiting reporter response |
urgent | #b60205 | security or p0 |
duplicate | #cfd3d7 | duplicate of another issue |
good-first-issue | #7057ff | well-scoped for newcomers |
wontfix, invalid | #e4e669 | declined |
# Wrap label creation in try/log — if the API returns 422 (already-exists race, protected label, etc.),
# log ISSUE_TRIAGE_LABEL_SKIPPED: <name> and continue rather than aborting the whole run.
gh label create "<name>" -R owner/repo --color <hex> --description "<text>" \
|| echo "ISSUE_TRIAGE_LABEL_SKIPPED: <name>" # only if missing
gh issue edit <N> -R owner/repo --add-label "<comma-separated-set>" \
|| echo "ISSUE_TRIAGE_LABEL_SKIPPED: issue=<N>" # one call per issue
Batch all labels for an issue into one --add-label call to save API quota. A failure on a single label skips only that label (or that issue's labeling) — the rest of the triage (comment, state update) proceeds.
Advisory mode (advisory[owner/repo] == true): do not post any comment or close any issue. Instead append the verdict to an advisory report at memory/triage-advisory/${owner}-${repo}.md (create the dir if missing) so the maintainer can act manually:
## <YYYY-MM-DD> advisory triage (no write access — verdicts only)
- #<N> — <VERDICT> — <type>/<priority>[, area=<area>] — would-label: <label set> — <one-sentence rationale> — <issue url>
Still record the issue in memory/triaged-issues.json (§8) so it is not re-triaged, and surface the advisory batch via notify (§9). Skip the rest of this step.
One comment, one verdict — no boilerplate. Use the template that matches the verdict:
ACCEPT:
**Triage:** ACCEPT — <type>/<priority>[, area=<area>]
<one-sentence rationale>.
Suggested reviewer: <@handle from CODEOWNERS or most-recent committer to the touched files> ← omit this line if not inferable
NEEDS-INFO:
**Triage:** NEEDS-INFO
<one-sentence rationale>.
To proceed we need: 1) <specific question>, 2) <specific question>[, 3) <specific question>]
DUPLICATE:
**Triage:** DUPLICATE of #<N> — <linked title>
<one-sentence reason the match is high-confidence>.
DECLINE:
**Triage:** DECLINE
<one-sentence reason>. <Alternative venue or tool if any>.
gh issue comment <N> -R owner/repo --body "<rendered comment>"
Close rule: only close on DUPLICATE and only when the match is high-confidence (title overlap ≥ 85 % AND identifying string match OR explicit reference). Otherwise leave open with duplicate label:
gh issue close <N> -R owner/repo --reason "not planned" --comment "Closing — duplicate of #<N>."
Never close on ACCEPT, NEEDS-INFO, or DECLINE.
When the issue body or stack trace names a file path path/to/file.ext, try:
gh api "/repos/owner/repo/contents/.github/CODEOWNERS" --jq '.content' 2>/dev/null | base64 -d
gh log -R owner/repo -- path/to/file.ext 2>/dev/null | head -1 # most recent author if CODEOWNERS absent
If neither resolves, omit the line. Do not guess.
Write memory/triaged-issues.json:
{"owner/repo": [{"n": 42, "at": "2026-04-20"}, ...]}
Record only repos that were actually processed this run — those that ran normally or in advisory mode. Never record a repo whose §2 probe errored (it was skipped without classification); recording it would permanently suppress its issues on future runs even though nothing was triaged. Drop entries older than 90 days to keep the file bounded.
Fire ./notify only if the run produced any: urgent / p0 / security / DUPLICATE-close, or any advisory-mode verdicts (since no labels/comments were written, notify + the advisory report are the only output surface). Skip notify for routine p2/p3 triage on write-access repos.
For advisory repos, include a one-line pointer:
- owner/repo — N advisory verdicts (no write access) → memory/triage-advisory/owner-repo.md
*Issue Triage — ${today}*
Triaged N across M repos. Urgent: k. Duplicates closed: d. Needs-info: i.
- owner/repo#123 — urgent (security): <title>
- owner/repo#124 — duplicate of #99 (closed)
If nothing actionable, no notification.
Append to memory/logs/${today}.md:
### issue-triage
- Repos: <list>
- Triaged: N (accept=a, needs-info=ni, duplicate=d, decline=de)
- Urgent / p0: <comma-separated URLs, or "none">
- Skipped (already triaged): <count>
- Overflow (budget > 10): <repos with count, or "none">
- Advisory (no write access): <per-repo count, or "none">
- Source status: <per-repo: ok | fail — reason>
Terminal log lines:
ISSUE_TRIAGE_OKISSUE_TRIAGE_ERROR source-status=<...> (no notification)Use gh CLI for all GitHub operations — it handles auth internally and bypasses curl sandboxing. If gh errors on a single repo, record fail — <reason> in source status and skip that repo; do not abort the whole run. If gh is unavailable entirely, WebFetch cannot substitute (auth required) — log ISSUE_TRIAGE_ERROR gh-unavailable and exit.
gh issue list excludes them; if a query is switched to gh search issues, keep is:issue in the filter.good-first-issue on security or p0 items.memory/triaged-issues.json or already carrying a triage label (§2).false — run advisory-only and record verdicts to memory/triage-advisory/.