Skip to content

TOOLING: Replace regex image-reference parsing with a real markdown parser #39

Description

@TheSevenPens

Problem

The asset scripts in .agents/skills/manage-gitbook-assets/scripts/ find image references with regex. Markdown link syntax is not a regular language, so this has already produced one real bug and remains fragile.

What already went wrong

All three scripts originally shared:

r'!\[.*?\]\((.*?)\)'

That stops at the first closing parenthesis. Many assets here are named like image-000209 (1).png, and markdown wraps paths containing spaces in angle brackets:

![](<../.gitbook/assets/image-000209 (1).png>)
                                        ^ the regex ended the match here

so the parsed basename was image-000209 (1 — a name that matches no file. A reference parsed that way looks unreferenced, which meant cleanup_orphans.py could quarantine an image that was actually in use. Eleven references in the repo parsed incorrectly under that regex.

The current parser in assetrefs.py is better but still regex:

RE_MD_ANGLE = re.compile(r'!\[[^\]]*\]\(\s*<([^>]+)>\s*\)')
RE_MD_PLAIN = re.compile(r'!\[[^\]]*\]\(\s*((?:[^()<>\s]|\([^()]*\))+)\s*\)')
RE_HTML     = re.compile(r'<img[^>]+src=["\']([^"\']+)["\']')

RE_MD_PLAIN handles exactly one level of balanced parentheses. That covers today's filenames and nothing beyond them.

Current exposure

12 assets have parentheses in their names:

0C2DnfPTcmE_00-03-27_Nicolas11x12_..._What_To_Look_Out_For (1).png
Diag Wobble Huion Kamvas 16 GEN3 (GS1563).png
DigiDraw-Turing_Basic_Wobble_(T610)_2026_05_18.png
fedora-drawtab-ui (1).png  .. (5).png
simple-nib-remover-huion-(Large).jpg
unsupported-video-format (Large).jpeg
Wacom-ACK20002-store-photo (1).webp
wacom-macos-high-memory-1 (Large).jpeg

All 12 are currently referenced as HTML <img src="...">, where the path sits inside a quoted attribute and the parenthesis problem does not arise. So this is latent, not live — every reference resolves today.

It becomes live the moment any of them is referenced with plain markdown ![](...) syntax, which GitBook may produce on any future edit of those pages. Nobody would notice: the failure is silent, and its symptom is an in-use image being quarantined.

Other cases regex will not cover

  • Nesting deeper than one paren level
  • Reference-style links: ![alt][ref] with the target defined elsewhere
  • Escaped brackets or parens in alt text
  • Image references inside fenced code blocks, which should be ignored but currently match
  • <img> with unquoted attributes, or src after other attributes containing >

Direction

Avoid regex. Parse the markdown properly and walk the resulting tree for image nodes, so the parser handles escaping, nesting, angle-bracket paths and code blocks by construction.

Candidates for a stdlib-plus-one-dependency approach:

  • markdown-it-py — CommonMark compliant, exposes a token stream, actively maintained
  • mistune — fast, AST output
  • marko — CommonMark compliant, clean AST

Whichever is chosen, the HTML <img> case still needs handling, since GitBook writes a lot of raw HTML. html.parser from the stdlib is enough for that and is more correct than the current attribute regex.

Complementary mitigation

Independently of the parser, renaming the 12 assets to drop parentheses and (Large) export suffixes would remove the ambiguity at the source. That is a smaller change and could land first, but it does not fix the parser for names added later.

Acceptance

  • assetrefs.py extracts references via a parser, not regex
  • Round-trips the existing 933 references with no change in results
  • Correctly ignores image syntax inside fenced code blocks
  • Handles ![](<path with (parens) and spaces>) and reference-style links

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions