{"slug":"markdown-toc-anchors","name":"Markdown TOC Anchors: Contents Links That Really Jump","version":"1.0.0","updated_at":"2026-10-09T03:46:49.041Z","use_when":"Builds the table of contents of a Markdown document with the exact heading anchors that GitHub or GitLab generate, and answers with the list only, no code fence. Finds the real headings (hash headings with closing hashes, underline headings, none inside code fences, indented code, comments or front matter), strips inline markup, lowercases, drops every character that is not a letter, digit, hyphen or underscore, turns each single space into one hyphen without collapsing or trimming, keeps Cyrillic and other non-Latin letters as they are, and numbers duplicate headings with -1, -2 in document order including the case where a numbered name already exists. Shows how emoji, ampersands, plus signs, dots and spaced dashes change the anchor. Indents two spaces per level below the shallowest heading. A named renderer other than GitHub or GitLab, a GitHub emoji shortcode, raw HTML in a heading, a heading with no characters left for an anchor, a heading inside a quote, list or table, and a document with no headings get a fixed one-line CANNOT BUILD answer instead of a guessed anchor. Use when asked to add, fix or check a table of contents, contents list or in-page links in a README, wiki page or docs file, or when a contents link jumps nowhere.","not_for":"Other renderers (documentation generators, static site tools, editor previews), GitHub emoji shortcodes, headings written as HTML or inside quotes, lists or tables, and documents with no headings get a CANNOT BUILD line. It neither rewrites headings, numbers sections nor edits the file. Rules: GitHub and GitLab behaviour, checked 2026-10-08.","languages":["any"],"tags":["markdown","table-of-contents","anchors","github","gitlab","readme","cyrillic","no-guessing"],"category":"data","category_url":"https://aiskills402.com/categories/data","keywords":["table of contents","contents link","in-page links"],"faq":[{"q":"Is it enough to lowercase the heading and put hyphens in?","a":"No, because the real renderers keep some oddities. Punctuation and emoji are removed but the spaces around them stay, so a spaced dash gives three hyphens, a spaced ampersand gives two and a leading emoji gives a leading hyphen. Hyphens are not merged or trimmed. Repeated headings get -1, -2 in document order, and a heading that is literally called Setup 1 pushes the next repeat of Setup to -2. A link built from the natural-looking slug does nothing when clicked and shows no error, which is why every anchor here is computed step by step and checked against the document."},{"q":"Which lines does it count as headings?","a":"Hash headings with optional closing hashes and up to three spaces of indent, and underline headings made with equals signs or hyphens. Lines in fenced code, indented code, HTML comments and front matter are not headings, and neither is a hashtag without a space or a hyphen line after a blank line, which is a rule. A heading with trailing hashes keeps a hash that touches its last word, such as C#."},{"q":"When does it refuse?","a":"It answers one CANNOT BUILD line when the renderer is not GitHub or GitLab, when a GitHub heading holds an emoji shortcode between colons, when a heading is raw HTML or sits in a quote, list or table, when no characters are left for an anchor, or when the document has no heading. Under GitLab a word between two colons stays as a word, as its documentation says."},{"q":"Does it help Claude Sonnet?","a":"Two documents out of twenty, so three cents. Sonnet and Haiku built contents for twenty-four documents, each model with and without the instructions, and every line was compared with anchors computed by the GitHub and GitLab rules. On the twenty with headings to list, plain Sonnet got 17 and 19 with the file: unaided it merged a run of spaces into one hyphen and kept underscores GitHub drops. The file also makes it refuse an emoji shortcode, raw HTML or another renderer; bare, it guessed there. Cyrillic headings keep Cyrillic anchors."}],"examples":[{"lang":"bg","model":"claude-sonnet-5-5","input_excerpt":"# Ръководство за потребителя\n\nКратко въведение.\n\n## Инсталация и настройка\n## „Бързо“ ръчно пускане (за напреднали)\n## Често задавани въпроси\n### Цени, лв. и ЕИК № 5\n## Инсталация и настройка\n### Тест\n### Тест","output_excerpt":"- [Ръководство за потребителя](#ръководство-за-потребителя)\n  - [Инсталация и настройка](#инсталация-и-настройка)\n  - [„Бързо“ ръчно пускане (за напреднали)](#бързо-ръчно-пускане-за-напреднали)\n  - [Често задавани въпроси](#често-задавани-въпроси)\n    - [Цени, лв. и ЕИК № 5](#цени-лв-и-еик--5)\n  - [Инсталация и настройка](#инсталация-и-настройка-1)\n    - [Тест](#тест)\n    - [Тест](#тест-1)"},{"lang":"en","model":"claude-sonnet-5-5","input_excerpt":"# 🚀 Launch plan\n\n## Done ✅\n## Fish & Chips\n## Q & A\n## Roadmap 2026 🔥🔥\n## Launch plan","output_excerpt":"- [🚀 Launch plan](#-launch-plan)\n  - [Done ✅](#done-)\n  - [Fish & Chips](#fish--chips)\n  - [Q & A](#q--a)\n  - [Roadmap 2026 🔥🔥](#roadmap-2026-)\n  - [Launch plan](#launch-plan)"},{"lang":"de","model":"claude-sonnet-5-5","input_excerpt":"# Übersicht\n\n## Größe und Gewicht\n\n## Straßen\n### Café","output_excerpt":"- [Übersicht](#übersicht)\n  - [Größe und Gewicht](#größe-und-gewicht)\n  - [Straßen](#straßen)\n    - [Café](#café)"},{"lang":"es","model":"claude-sonnet-5-5","input_excerpt":"# Introducción\n\n## ¿Qué es esto?\n\n## Año 2026\n\n## ¡Hola, mundo!","output_excerpt":"- [Introducción](#introducción)\n  - [¿Qué es esto?](#qué-es-esto)\n  - [Año 2026](#año-2026)\n  - [¡Hola, mundo!](#hola-mundo)"}],"page_url":"https://aiskills402.com/skills/markdown-toc-anchors","markdown_url":"https://aiskills402.com/skills/markdown-toc-anchors.md","image_url":"https://cdn.aiskills402.com/og/skills/markdown-toc-anchors/d715a4fb.png","related_url":"https://api.aiskills402.com/v1/skills/markdown-toc-anchors/related","purchases_count":null,"tested":{"date":"2026-10-09","strong":{"model":"claude-sonnet-5-5 (Claude Code alias \"sonnet\")","verdict":"Right on 23 of 24, checked by code line by line; it missed one: a level-six heading under a level-two one went in by ten spaces instead of eight. The anchors held everywhere: inline markup stripped, each space one hyphen with nothing merged, Cyrillic kept, duplicates numbered with -1 and -2, closing hashes and underline headings read, code fences and comments skipped, GitLab differences applied, and a CANNOT BUILD line for an emoji shortcode on GitHub, a renderer other than GitHub or GitLab, a raw HTML heading and a document with no heading."},"weak":{"model":"claude-haiku-5-5 (Claude Code alias \"haiku\")","verdict":"Right on 22 of 24, checked by code, but it put the same level-six heading at the wrong depth, and it nested a level-two heading indented by three spaces under the one before it."},"note":"Twenty-four Markdown documents written by us in English, Bulgarian and other scripts: 20 with headings to list (inline markup, spaced punctuation, emoji, duplicates, Cyrillic, closing hashes, underline headings, skipped levels, headings inside code, GitLab rules, plain controls) and 4 that must be refused (an emoji shortcode on GitHub, a renderer other than GitHub or GitLab, a raw HTML heading, no heading at all). Every anchor is computed by our script with the GitHub or GitLab rules and each answer is checked line by line, indentation included. No check was widened. One run per model and document.","baseline":{"date":"2026-10-09","rows":[{"label":"Contents built right (20 documents with headings)","better":"higher","strong":{"with":{"n":19,"of":20},"without":{"n":17,"of":20}},"weak":{"with":{"n":18,"of":20},"without":{"n":19,"of":20}}},{"label":"Documents refused with a reason (4)","better":"higher","strong":{"with":{"n":4,"of":4},"without":{"n":1,"of":4}},"weak":{"with":{"n":4,"of":4},"without":{"n":1,"of":4}}}],"note":"Same request on both sides: build the table of contents, include every heading. It does not ask to refuse, so the price rests on the first row. There, Sonnet without the skill missed two documents on the anchor rules: it merged a run of spaces and hyphens into one hyphen, and it kept the underscores of an italic word that GitHub drops; both sides put the same skipped level two spaces too deep. Without the skill both models listed an emoji shortcode, a raw HTML heading and a Docusaurus document as if GitHub rules applied."},"report_url":null},"price_usd":"0.03","price_micro":30000,"size_bytes":10201,"sha256":"a4b094e2210661a0ab729022f04267604fb84272f354f734cecb52dc96cc0866","outline":["Hard rules","Which lines are headings","The anchor, step by step","GitLab","When to refuse","Work in this order","Short examples"],"license":{"summary":"Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing","holder":"Georgi Kalchev, aiskills402.com","url":"https://aiskills402.com/docs#license"},"buy_url":"https://api.aiskills402.com/v1/skills/markdown-toc-anchors/file","redownload_url_template":"https://api.aiskills402.com/v1/purchases/{token}","mcp_tool":null,"payment":{"protocol":"x402","scheme":"exact","asset":"USDC","selling":true,"network":"base","network_caip2":"eip155:8453","pay_to":"0x8e37022edcf0f21cf3c9f93fee9d4d32519f36f4","facilitator":"cdp"},"seo_title":"Markdown TOC Anchors for GitHub and GitLab","seo_description":"Table of contents with the exact GitHub or GitLab heading anchors: Cyrillic, emoji, duplicates, setext, code fences. Refuses a guess. $0.03 once, in USDC.","versions":[{"version":"1.0.0","date":"2026-10-09","changelog":"# Changelog\n\n## 1.0.0 — 2026-10-08\n\nFirst release: builds the table of contents of a Markdown document with the heading anchors GitHub or GitLab generate. Finds the real headings (hash headings with closing hashes, underline headings; not fences, indented code, comments or front matter), strips inline markup, lowercases, removes every character that is not a letter, digit, hyphen or underscore, turns each space into one hyphen without merging or trimming, keeps Cyrillic and other scripts, numbers repeats in document order including the collision with a heading that is literally numbered. Indents two spaces per level below the shallowest heading. One CANNOT BUILD line for another renderer, a GitHub emoji shortcode, raw HTML in a heading, a heading in a quote, list or table, an empty anchor, no headings.\n\nRules checked on 2026-10-08: the GitLab user documentation, section on heading IDs and links (read-only fetch: lowercase, keep letters, numbers, hyphens and underscores, spaces to hyphens, repeats numbered from 1, a colon-wrapped word keeps its word); the github-slugger package, version 2.0.0, README and source (read-only), used ONLY in test/make-cases.mjs and test/control.mjs to compute the expected GitHub anchors. Our own decisions: refusal where no source states the result (GitHub shortcodes, raw HTML, headings in quotes or lists).\n\nTest set: 24 cases (16 traps including 4 refusals, 8 controls); control.mjs makes no model call and passes. No model measurement yet; the price is a starting price, to be set after the baseline.\n\n## 1.0.1 — 2026-10-09 (finalised after the model test)\n\n- Measured on 24 documents (without -> with the skill). 20 with headings: Sonnet 17 -> 19, Haiku 19 -> 18. 4 to refuse: Sonnet 1 -> 4, Haiku 1 -> 4; the shared request does not ask to refuse, so the price rests on the first group. No check was widened.\n- Price: $0.03 (Sonnet gain 2).\n"}]}