LLMSkillHub

sinhasheshant / citable-markdown

Make specific lines, paragraphs, list items, tables and code blocks in markdown citable with short readable ^ids, cite them with links that carry a verifying quote, and check every reference with a script that reports dangling, stale and moved references instead of silently pointing at the wrong text. Use when writing or editing markdown that refers to other parts of itself or other files, when a person asks about "line 14" or "that paragraph", when reviewing docs for broken internal links, or when an agent must cite exactly which passage supports a claim.

documentswritingmarkdownreferencescitationsblock-idanchorslinksobsidianline-numbersverification
0Compare
SKILL.md
9.4 KB

Citable markdown

Plain markdown can only link to headings, heading links break when the heading
is renamed, and broken links fail silently. Line numbers drift on every edit.
This skill gives any block a short readable name, makes every citation carry
a quote that proves what it points at, and checks both with a script.

When to reach for this

  • You are writing a markdown document that refers back to its own parts
    ("as noted above", "see the failure table") or to passages in other .md files.

  • A person refers to a line or passage ("line 14 is wrong", "that paragraph
    about retries") and you need to find it exactly, even if the file has changed.

  • You must cite which passage supports a claim, in a way someone can check.
  • You are reviewing or editing markdown docs and want to know which internal
    links are broken or now point at the wrong text.

Not for: links to web pages (use normal URLs), or editing code files (use your
edit tool's exact-match replacement).

The convention

Anchor: make a block citable by giving it an id.

TargetHow
A paragraph, one list item, or one table rowend its last line with a space and ^id
A headingnothing needed (GitHub-style slug: ## Retry plan is #retry-plan), or end it with ^id for a name that survives renames
A table, fenced code block, quote, or whole listput ^id alone on the line after it, then a blank line
Most OCR failures come from rotated pages. ^ocr-cause

| Case | Score |
|---|---|
| scan-07 | 4 |

^failure-table

Ids: lowercase letters, digits and hyphens, short and meaningful
(ocr-cause, retry-plan). They must be unique within a file.

Reference: cite with a normal markdown link to a fragment, and put a short
quote from the target in the link title:

The [likely cause](#ocr-cause "rotated pages") matches the [table](#failure-table "scan-07 | 4").
In another file: [the plan](plan.md#retry-plan "rerun only scanned").
A line, when no anchor exists: [line 14](#L14 "exact words on that line").
A range: [lines 14 to 18](#L14-L18 "words from that range").

The quote is what makes a reference checkable: an id says where, the quote
says what should be there. Matching ignores case, extra whitespace, and the
* _ ` emphasis marks.

Process

Every step below reads or runs something. If a tool call fails (a file can't be
read, a command can't run), say so first in your answer, and never describe a
file you couldn't read or a step you couldn't do as if it were done.

  1. Look before adding. List what is already citable in the files you'll
    touch, so you reuse ids instead of inventing duplicates:

     python3 SKILL_DIR/scripts/check_refs.py --list path/to/file.md

    SKILL_DIR is the directory holding this SKILL.md (for example
    .claude/skills/citable-markdown). Output is one line per target:
    file:line ^id "first words" for anchors, #slug for headings.
    Add --json for the full text of each target.

  2. Add an anchor only where something cites it. Don't put ids on every
    paragraph. When you write a citation, give its target an id if it has none,
    following the table above. Choose a name from the content (^ocr-cause, not
    ^p3 or a random string).

  3. Write each reference with a quote. Copy 3 to 8 consecutive words
    verbatim from the target, picking words that are distinctive to it. Open
    the target and copy them; don't write the quote from memory. Always write
    link text: [](#id) renders as nothing in most viewers.

  4. Prefer ids over line numbers. Use #L14 only for files you can't or
    shouldn't add anchors to, or to record what a person told you. Always add a
    quote to a line reference; without one it is only a guess.

  5. Resolve a person's line reference. When someone says "line 14 says X",
    don't trust the number. Write or imagine it as [x](#L14 "X") and run the
    checker. A relocated result tells you where X is now. A stale result means
    X isn't in the file (or appears more than once): ask the person, don't guess.

  6. Verify. Run the checker on every file you changed:

     python3 SKILL_DIR/scripts/check_refs.py path/to/docs/

    Success looks like OK: 12 references checked, all resolved and exit code 0.
    Otherwise each problem is printed as file:line: severity code: message.
    Fix each one:

    CodeMeaningWhat to do
    dangling (error)no ^id, heading slug, file or line with that nameFix the typo, add the missing anchor, or remove the link
    stale (error)target exists but doesn't contain the quote, or a line quote is found 0 or 2+ timesRead the target. If the text changed, decide whether the citation still holds; update the quote or retarget it. Never just delete the quote to silence it
    duplicate-id (error)the same ^id twice in a fileRename one, then update the links that meant it
    orphan-anchor (error)a standalone ^id with no block aboveMove it directly under its block
    relocated (warning)a line ref's quote is now on another line, found exactly onceUpdate the line number, or run with --fix to rewrite it
    unverified (info)a line ref without a quoteAdd a quote, or replace it with an anchor

    Re-run until it prints OK. Use --strict to make warnings and
    unverified fail too (useful in CI). Use --json to parse results.

    If the checker can't run (no Python, no shell, the command fails or
    errors before printing results): try once more at most, then stop. Your
    edits stand, but they are unchecked. Tell the user exactly that: which
    files you changed, the references you added, what went wrong running the
    checker, and the command for them to run:
    python3 SKILL_DIR/scripts/check_refs.py path/to/docs/. Don't keep retrying
    and don't check the references by eye and call them verified.

  7. Renaming an id. Search for every use first, then change the anchor and
    all references together, then re-run the checker:

     grep -rn -e '#old-id' -e '\^old-id' path/to/docs/

What not to do

  • Don't report work your tools didn't do. "I examined the workspace" after
    every read failed is a false report, even when the user asked you to confirm
    it is done. Say what failed and what is still to do.

  • Don't say the references are verified unless the checker printed OK.
    "Done", "all references resolved" or "verified" after a checker that failed
    or never ran is a false report. Say the edits are unchecked, and why.

  • Don't cite a line number you haven't just read. Line numbers drift with
    every edit above them, and models are unreliable at recalling them. Read the
    file, then cite, with a quote.

  • Don't treat "the link resolves" as "the citation is right". A #id can
    still exist after its text changed. Only the quote check proves the target
    still says what you claimed.

  • Don't silence a stale error by deleting or loosening the quote. That
    hides exactly the drift this exists to catch. Re-read the target and decide.

  • Don't use random or hash ids (^a8f3c2, UUIDs). People can't read,
    remember, or type them, and they add clutter. Use names from the content.

  • **Don't put ^id in the middle of a line, inside a code block, or inside
    inline code.** It is only recognized at the end of a line, or alone on a line
    after a block. Inside code it is ignored and links to it are reported dangling.

  • Don't anchor everything up front. Ids that nothing cites are noise. Add
    them when you add the citation.

  • Don't write empty link text [](#id). It renders as nothing outside a
    smart renderer.

  • Don't run --fix without reviewing the diff. It only rewrites line numbers
    for relocated references, but check the new location is what was meant.

Limits

  • Heading slugs follow GitHub's rules approximately. If an anchor and a
    heading slug share a name, the ^id wins.

  • On GitHub, links to ^id targets render but don't scroll anywhere; only
    heading links jump. The checker still validates them.

  • Quotes are matched within the target block only, except for line references,
    which fall back to a whole-file search to find where the text moved.