Markdown Documents skill for Codex
Markdown that renders right everywhere it lands. Codex writes Markdown for the renderer it will actually meet: it reads the repo's lint and format config first, uses GFM syntax where GitHub renders it and the docs generator's own syntax elsewhere, fixes the list-indentation and table-pipe traps, keeps diagrams as Mermaid source, and finishes with markdownlint-cli2, Prettier and an offline lychee pass over links and anchors.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- markdown-documents Installs to ~/.agents/skills/markdown-documents/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install markdown-documents The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project markdown-documents Installs to .agents/skills/markdown-documents/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/markdown-documents.zipunzip -o markdown-documents.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) markdown-documents PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $markdown-documents, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Writes and maintains Markdown documents that render correctly across GitHub, GitLab, docs sites and editors, using CommonMark and GFM rules for headings, lists, tables, code fences, footnotes, alerts, math and Mermaid diagrams, enforced with markdownlint-cli2, Prettier and lychee link checks. Use when asked to write or clean up README, docs or design-doc Markdown, fix tables or lists that render wrong, add diagrams, set up Markdown linting in CI, or find broken links and anchors.
Use it when
- Writing or restructuring README, design docs or docs-site pages in Markdown
- Lists, tables, alerts or code blocks render incorrectly
- Setting up Markdown linting, formatting or link checking
- Adding diagrams, math or footnotes to repository docs
Use something else when
- The task is choosing what a README should say: use readme-writing
- You are building or versioning a whole docs site: use docs-as-code
How to check the result
markdownlint-cli2 and prettier --check pass with the repo config, lychee --offline reports no broken local links or anchors, and the page is previewed in its target renderer.
The mistake it prevents
Using GitHub-only syntax such as [!NOTE] alerts in a docs site that renders them as literal text, or indenting nested list items to the wrong column.
Done well means the file renders the same in every target the repo uses, passes the repo’s lint config with no inline disables added to hide problems, every link and anchor resolves, and the structure (one title, ordered headings, short sections) lets a reader scan it.
Before writing
- Read
AGENTS.md,CONTRIBUTING.mdand any existing.markdownlint*,.prettierrc*,.editorconfigor.vale.ini. Follow what is there; do not introduce a second style. - Identify the renderer(s): GitHub/GitLab web, a docs generator (Docusaurus, MkDocs, Starlight, VitePress), an IDE preview, Pandoc. Features differ; GFM-only syntax may render as literal text elsewhere.
- Check whether the file is generated (header comment, build script). Edit the source, not the output.
Syntax decisions
| Need | Use | Avoid |
|---|---|---|
| Title | One # heading at the top (unless front matter title replaces it) | Several # headings |
| Sections | ##, ### in order, no skipped levels | Bold text as fake headings |
| Emphasis | **strong**, _emphasis_ consistently | Mixing * and _ styles in one repo |
| Code | Fenced blocks with a language tag (```bash) | Indented code blocks, untagged fences |
| Commands the reader copies | One command per line, no $ prompt prefix unless output is shown | Prompts that break copy-paste |
| Callouts on GitHub | > [!NOTE], > [!TIP], > [!IMPORTANT], > [!WARNING], > [!CAUTION] | Emoji-prefixed blockquotes |
| Callouts in a docs site | That generator’s admonition syntax (:::note, !!! note) | GitHub alert syntax, which other renderers show literally |
| Diagrams | ```mermaid fenced block (GitHub, GitLab, most docs sites) | Screenshots of diagrams with no source |
| Math | $inline$ and $$block$$ or ```math on GitHub | Images of equations |
| Footnotes | text[^1] and [^1]: note (GFM) | Manual superscripts |
| Collapsible detail | <details><summary>Title</summary> with a blank line before the Markdown body | Huge always-open logs |
| Line break inside a paragraph | End the line with \ | Two trailing spaces (invisible, stripped by editors) |
Lists
- A nested item must be indented to the content start of its parent: 2 spaces under
- item, 3 under1. item, 4 under10. item. Inconsistent indentation is the top cause of broken nesting. - Put a blank line before a list that follows a paragraph; some renderers merge it otherwise.
- Code blocks or paragraphs inside a list item are indented to the item’s content column.
- Ordered lists: write
1.for every item if the repo lints withonestyle, or number sequentially if it usesordered; check MD029’s config.
Tables
- GFM only. Header row, delimiter row, then body; alignment with
:--,:-:,--:. - Escape literal pipes as
\|, including inside inline code in a cell. - No multi-line cells; use
<br>sparingly, or turn the table into headings with lists when cells need paragraphs. - More than about 5 columns or long prose cells: the table will scroll on GitHub and mobile. Restructure.
- Prettier aligns table columns; let it, rather than hand-aligning.
| Option | Default | Description |
|---|---|---|
| `--retries` | `3` | Attempts before giving up; `0` disables retry |
| `--format` | `text` | One of `text` \| `json` |
Links and anchors
- Relative links between repo files (
../guides/setup.md#install) so they work on forks and branches; absolute URLs only for external resources. - GitHub anchor slugs: lowercase, spaces to hyphens, most punctuation removed; duplicate
headings get
-1,-2. Renaming a heading breaks inbound anchors; search for them. - Link text says where it goes (“installation guide”), never “here”.
- Reference-style links (
[guide][setup]with[setup]: ./setup.md) keep long tables and paragraphs readable. - Images:
; empty alt only for decorative images.
Mermaid
flowchart LR
A[Upload] --> B{Valid?}
B -- yes --> C[Queue job]
B -- no --> D[Return 422]
Keep diagrams under about 15 nodes; split bigger ones. Quote labels that contain
parentheses or special characters: A["Parse (strict)"]. Validate syntax by rendering
with mermaid-cli when available: npx -p @mermaid-js/mermaid-cli mmdc -i flow.mmd -o flow.svg
(downloads a headless browser; needs network). GitHub’s renderer may lag the latest
Mermaid syntax; avoid brand-new diagram types for repo docs.
Front matter
Docs generators read YAML front matter (--- block at line 1). Quote values containing
: or starting with special characters, keep keys the generator expects (title,
description, sidebar_position), and do not add front matter to files GitHub renders
directly unless the repo already does (GitHub shows it as a table).
Tooling
markdownlint-cli2 (configuration in .markdownlint-cli2.jsonc):
{
"config": {
"default": true,
"MD013": false, // line length: off for prose; Prettier handles wrapping
"MD024": { "siblings_only": true }, // duplicate headings allowed in different sections
"MD033": { "allowed_elements": ["details", "summary", "br", "kbd", "sup"] }
},
"globs": ["**/*.md"],
"ignores": ["node_modules/**", "CHANGELOG.md", "vendor/**"]
}
npx markdownlint-cli2 # uses globs from the config
npx markdownlint-cli2 --fix "docs/**/*.md"
npx prettier --check "**/*.md" # --prose-wrap preserve|always|never from .prettierrc
lychee --offline --include-fragments "**/*.md" # local files and #anchors only, no network
lychee "**/*.md" # also external URLs; needs network
npx downloads packages on first use; in an offline sandbox, use the versions in the
repo’s devDependencies or ask before installing. If the repo already uses
markdownlint (the Node library through a different runner) or remark-lint, keep it.
CI step example (GitHub Actions) when the user asks for it:
- uses: DavidAnson/markdownlint-cli2-action@v20
with:
globs: "**/*.md"
Check the action’s current major version in its README before pinning.
Workflow for a document change
- [ ] 1. Read conventions and renderer targets
- [ ] 2. Outline with headings before writing paragraphs
- [ ] 3. Write; keep sections short, put commands in fenced blocks
- [ ] 4. Run lint, format check and offline link check
- [ ] 5. Preview the rendered output in the target renderer
- [ ] 6. Report what changed and anything not verified
For step 5, at minimum render with the repo’s docs tool (npm run docs:build or similar,
from package.json) or view on GitHub after pushing a branch. When neither is available,
say that rendering was not checked.
Gotchas
- A line starting with a number and period (
2026. was a good year) becomes a list item; escape it as2026\.. <in prose (a < b,List<String>) can be eaten as HTML; use inline code.- Underscores inside words are safe in GFM but not in some older renderers; use code
spans for identifiers like
snake_case_name. - Headings need a space after
#(#Titleis a paragraph in CommonMark). - Tabs in indentation behave like 4 spaces for list nesting; mixed tabs break lists.
- Copy-pasted smart quotes and non-breaking spaces inside code blocks break commands.
- HTML blocks end at the first blank line; Markdown inside
<details>needs a blank line after<summary>.
Output
## Docs change: docs/deploy.md
- Restructured into Prerequisites / Deploy / Rollback / Troubleshooting (H2s), 1 H1
- Converted 2 screenshot diagrams to Mermaid; tables reduced from 7 to 4 columns
- Checks: markdownlint-cli2 clean, prettier --check clean, lychee --offline 0 broken
- Not verified: external links (sandbox offline), GitHub rendering of the new alert block What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Markdown Documents"
short_description: "Markdown that renders right everywhere it lands"
default_prompt: "$markdown-documents Clean up docs/ so markdownlint passes without disabling rules everywhere" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.