Skip to content

Render Read/Write of any Markdown file as Markdown, not Pygments#234

Open
cboos wants to merge 1 commit into
mainfrom
dev/md-file-rendering
Open

Render Read/Write of any Markdown file as Markdown, not Pygments#234
cboos wants to merge 1 commit into
mainfrom
dev/md-file-rendering

Conversation

@cboos

@cboos cboos commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Closes #232.

Problem

A follow-up to the auto-memory work (#192). We already render auto-memory file bodies as rendered Markdown instead of Pygments-highlighted source — but that specialization was keyed on the memory path, not on the file being Markdown. Any other .md file (a README, a doc, a plan) read or written during a session still rendered as highlighted source.

Change

Generalize the body rendering to every Markdown file:

  • New is_markdown_path() (.md/.markdown, case- and separator-insensitive) in html/utils.py. Memory paths are a subset: is_memory_path ⊂ is_markdown_path.
  • Write of any .md → always rendered as Markdown. A Write carries the file's full content, so there's no partial-slice concern.
  • Read of a .md → rendered as Markdown only for a full read (start_line == 1 and not truncated, via _is_full_read). A partial slice (offset/limit) can begin or end mid-code-fence and would render garbled, so partial reads keep Pygments — where a line-numbered source view is more useful for a slice anyway.
  • Reuses the HTML-escaping markdown helper (render_user_markdown_collapsible): file content is untrusted regardless of being a memory file, so raw <script>/HTML renders as text, not live DOM.

Deliberately unchanged

  • Memory keeps its extra specialization: the 🧠 title, relative-link resolution, and always-Markdown body (even for partial reads). General .md files get neither the 🧠 title nor link rewriting.
  • The Markdown output renderer is untouched (still fenced code blocks) — embedding raw Markdown-in-Markdown risks heading bleed and fence collisions, and the issue is framed around HTML pygmentizing.
  • Edit stays as-is (diff view + partial snippet).

Testing

New test/test_markdown_file_rendering.py (15 tests): is_markdown_path (.md/.markdown, case, Windows, memory-subset, negatives incl. .mdx); Read full→Markdown / partial→Pygments / truncated→Pygments / non-md→Pygments / escaping / no-link-resolution; Write md→Markdown / non-md→Pygments / escaping / no-link-resolution. The full/partial/truncated split is pinned behaviorally through the public format_read_output (no private-symbol import). test_memory_rendering.py regressions all pass — memory behavior unchanged. No snapshot churn (no existing fixture does a non-memory .md Read/Write).

just ci green (unit + TUI + browser + snapshots, ruff, pyright, ty).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved Markdown rendering: complete .md/.markdown reads and writes now display as formatted Markdown with safer HTML escaping.
    • Enhanced memory file handling, including Markdown link resolution.
    • Partial or truncated Markdown reads continue using syntax-highlighted rendering to avoid corrupted Markdown structure.
  • Tests

    • Added coverage for Markdown path detection (including Windows paths and case-insensitivity), read/write rendering behavior, and link rewriting rules.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Markdown path detection and updates read/write tool formatting so .md and .markdown files render as Markdown HTML, with partial reads still using syntax-highlighted output. Adds tests for detection and rendering branches.

Changes

Markdown File Rendering

Layer / File(s) Summary
Markdown path detection utility
claude_code_log/html/utils.py, claude_code_log/html/tool_formatters.py
Defines _MARKDOWN_EXTS = (".md", ".markdown") and is_markdown_path(file_path) in utils.py; imports is_markdown_path into tool_formatters.py.
Read formatter: full-read guard and Markdown rendering
claude_code_log/html/tool_formatters.py
Introduces _is_full_read(ReadOutput) -> bool (checks start_line == 1 and not is_truncated), then reworks format_read_output to render memory paths and full .md reads as Markdown HTML, falling back to render_file_content_collapsible for partial/truncated reads.
Write formatter: Markdown rendering
claude_code_log/html/tool_formatters.py
Updates format_write_input so non-memory .md/.markdown write targets call render_user_markdown_collapsible and return early; non-Markdown writes continue to render_file_content_collapsible.
Tests for Markdown detection and rendering
test/test_markdown_file_rendering.py
Adds tests for extension detection, case-insensitivity, Windows paths, full/partial/truncated read branches, HTML escaping, and no file:// rewriting for non-memory links.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • daaain/claude-code-log#172: Changes how ReadOutput.start_line and is_truncated are computed from parsed tool results, which affects the new full-read rendering branch.
  • daaain/claude-code-log#204: Introduced the memory-path Markdown rendering and link-resolution behavior that this PR extends to non-memory .md/.markdown files.

Poem

🐇 I hop through .md with careful cheer,
Full reads bloom Markdown, crisp and clear.
Partial slices stay safe and sound,
While writes in rabbit-mode are Markdown-bound.
Tiny links remain just where they be —
Hooray for docs that render merrily! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: Markdown reads and writes now render as Markdown instead of Pygments.
Linked Issues check ✅ Passed The changes match #232 by rendering Markdown writes as Markdown and full Markdown reads safely, while keeping partial reads on Pygments.
Out of Scope Changes check ✅ Passed The added path detection, formatter updates, and tests all support the Markdown rendering scope described in the linked issue.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev/md-file-rendering

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Generalize the auto-memory body rendering (#192) to every .md file: a
fully-contained Markdown file should render the usual way (rendered
Markdown) rather than syntax-highlighted source.

- Add is_markdown_path() (.md/.markdown, case- and separator-insensitive).
  Memory paths are a subset, so is_memory_path ⊂ is_markdown_path.
- Write always carries the whole file → always rendered as Markdown for
  any .md path (no partial-slice concern).
- Read renders as Markdown only for a *full* read (start_line == 1 and
  not truncated, via _is_full_read). A partial slice (offset/limit) can
  begin or end mid-code-fence and would render garbled, so partial reads
  keep Pygments — where a line-numbered source view is more useful anyway.
- Reuse the HTML-escaping markdown helper: file content is untrusted
  regardless of being a memory file, so raw <script>/HTML renders as text.

Memory keeps its extra specialization unchanged: the 🧠 title, relative-
link resolution, and always-Markdown body (even partial reads). General
.md files get neither the 🧠 title nor link rewriting. The Markdown
*output* renderer is untouched (it keeps fenced code blocks; embedding
raw Markdown-in-Markdown would risk heading bleed and fence collisions).

_is_full_read's docstring notes the one residual edge (text-only parser
fallback + truncated-from-line-1 → reads as full, cosmetic only). Tests
pin the full/partial/truncated split behaviorally through the public
format_read_output rather than importing the private helper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cboos cboos force-pushed the dev/md-file-rendering branch from 79025fd to 9b0a164 Compare June 23, 2026 22:35
@cboos

cboos commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/test_markdown_file_rendering.py (1)

91-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Decouple partial-read coverage from truncation in this test.

Line 93 currently makes the case both partial and truncated, so this can pass even if the start_line branch regresses. Make it non-truncated to pin the partial-read condition specifically.

Suggested diff
-        html = format_read_output(_read(MD, MD_BODY, start_line=10, total=999))
+        html = format_read_output(_read(MD, MD_BODY, start_line=2, total=3))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/test_markdown_file_rendering.py` around lines 91 - 94, The partial
markdown read test currently mixes the start_line partial-read path with
truncation via total=999, so it can still pass even if the partial-read behavior
regresses. Update test_partial_md_read_keeps_pygments to use a non-truncated
read while keeping start_line > 1, and keep the assertion on
format_read_output(_read(...)) focused on the partial-read branch in MD/MD_BODY
rather than truncation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/test_markdown_file_rendering.py`:
- Around line 91-94: The partial markdown read test currently mixes the
start_line partial-read path with truncation via total=999, so it can still pass
even if the partial-read behavior regresses. Update
test_partial_md_read_keeps_pygments to use a non-truncated read while keeping
start_line > 1, and keep the assertion on format_read_output(_read(...)) focused
on the partial-read branch in MD/MD_BODY rather than truncation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1be2361-a271-405b-8561-c191b3bf91aa

📥 Commits

Reviewing files that changed from the base of the PR and between 8013b61 and 9b0a164.

📒 Files selected for processing (3)
  • claude_code_log/html/tool_formatters.py
  • claude_code_log/html/utils.py
  • test/test_markdown_file_rendering.py

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Specialize Write tool for Markdown files

1 participant