check-broken-links
Walk markdown in configured repos, classify broken external and relative links, maintain one tracking issue per repo.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Walk markdown in configured repos, classify broken external and relative links, maintain one tracking issue per repo.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | check-broken-links |
| description | Walk markdown in configured repos, classify broken external and relative links, maintain one tracking issue per repo. |
Scan markdown in each configured target repo, classify broken links, and keep one tracking issue per repo up to date.
Everything persistent lives under ./state/ (gitignored, on a persistent volume):
./state/MEMORY.md — config: target org and repo list../state/skip-patterns.txt — learned ignore rules. See Ignore rules below for format../state/repos/<owner>-<repo>.json — per-repo bookkeeping. Structure matches State in reconcile-state.ts.Target repos are cloned to ./repos/<owner>/<repo>/; reuse with git pull if the clone exists.
Read ./state/MEMORY.md. If it doesn't exist, or the target list is empty:
gh repo list <org> --limit 200 --json name,description; let them confirm, edit, or say "all"../state/MEMORY.md under ## Targets, one owner/repo per line.Do not link-check until MEMORY.md has a concrete target list.
For owner/repo in the target list:
a. Refresh the clone. If missing, gh repo clone <owner>/<repo> repos/<owner>/<repo>. Otherwise git -C repos/<owner>/<repo> pull --ff-only.
b. Determine the tracking issue's state. The wrapper does no gh calls, so this lookup happens here:
state/repos/<owner>-<repo>.json has trackingIssueNumber, query gh issue view <n> --repo <owner>/<repo> --json state --jq .state and map OPEN→open, CLOSED→closed.[Bug]: broken links):
gh issue list --repo <owner>/<repo> \
--search '"[Bug]: broken links" in:title' \
--state all --limit 5 --json number,state,title
Filter to entries where title exactly equals [Bug]: broken links (the search is fuzzy):
absent.open. Patch trackingIssueNumber: <n> into the state file before running the wrapper.state.lastProcessedClosedIssueNumber:
<n> → the reset for this issue was already applied on a prior run; treat as absent.closed. Don't patch trackingIssueNumber (wrapper resets on closed).c. Scan tracking issue comments for ignore directives (only if state is open or closed):
gh issue view <n> --repo <owner>/<repo> --json comments \
--jq '.comments[] | {body, author: .author.login, createdAt}'
For any comment that says "ignore X" (e.g. "ignore flaky.example.com, it rotates IPs"), translate to a regex and append to state/skip-patterns.txt with a #-prefixed rationale line above. Dedupe before appending — re-running on the same comment must not duplicate.
d. Run the wrapper:
npx tsx .claude/skills/check-broken-links/run.ts \
--repo-root repos/<owner>/<repo> \
--state-file state/repos/<owner>-<repo>.json \
--tracking-issue-state <open|closed|absent> \
--plan-out /tmp/plan-<owner>-<repo>.json \
--body-out /tmp/body-<owner>-<repo>.md \
[--skip-patterns-file state/skip-patterns.txt]
The wrapper reads prior state, scans links, reconciles, persists next state, and writes the action plan and (if needed) issue body.
e. Execute the plan at /tmp/plan-<owner>-<repo>.json:
none → done.open → gh issue create --repo <owner>/<repo> --title "<plan.title>" --body-file <plan.bodyFile>. Capture the new issue number n from the URL and patch trackingIssueNumber: n into the state file.update → gh issue edit <plan.issueNumber> --repo <owner>/<repo> --body-file <plan.bodyFile>.close → gh issue close <plan.issueNumber> --repo <owner>/<repo> --comment "<plan.comment>".The wrapper deliberately omits trackingIssueNumber from the open-case state file. Patch it in only after gh issue create succeeds; a failed call leaves state recoverable.
f. After a closed run: patch lastProcessedClosedIssueNumber: <n> into the state file (where <n> is the issue number found in step 2b). This marks the reset as done so future title-based lookups for the same closed issue return absent instead of re-triggering the reset.
The title is the constant [Bug]: broken links — same for every repo, since gh already shows the issue under its owner/repo. Don't change this; the title-based recovery in step 2 depends on it. The wrapper produces the body; you just pass --body-file <plan.bodyFile> to gh.
gh repo clone / git pull fails → log, skip the repo, continue.gh issue create/edit/close fails after the wrapper succeeded → log and continue. Reconciliation is idempotent over the same findings, so the next run retries. Exception: a failed open must not patch trackingIssueNumber, so next run sees absent and retries../repos/<owner>/<repo>/ beyond what git pull does.The user can teach the bot a new ignore rule two ways:
Either way, append to state/skip-patterns.txt. Format is one regex per line; blank lines and #-prefixed lines are ignored by the wrapper, so use # lines for rationale immediately above each pattern. Example:
# Rotates IPs, repeatedly false-positive (chat with user 2026-04-12)
flaky\.example\.com
# Internal-only host
^https://internal\.local/
This file is the only place ignore rules live. Don't edit code or SKILL.md to add them.