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.