fix(cli): skip non-Markdown files named explicitly on check/fix - #760
Conversation
The directory walk and glob expansion both gate on isMarkdown, so a non-Markdown file like .gitattributes is skipped when discovered. An explicitly named path bypassed that gate: resolveArg added the file unconditionally after the symlink/regular-file checks. The result was that `mdsmith check .gitattributes` lints a git-config file as Markdown (parsing `#` comments as ATX headings, scoring comment prose for readability) and `mdsmith fix .gitattributes` rewrites it — inserting blank lines around headings, including inside the merge-driver block mdsmith generates there. The rewrite is not even idempotent-to- clean, so the file ends up modified and still failing. Gate the explicit-file branch of resolveArg on isMarkdown, matching walkDir and resolveGlob. MDS048's .gitattributes managed-block check is unaffected: it reads .gitattributes from disk itself while linting a Markdown file, so the file never needs to be in the linted set. Adds unit coverage (explicit non-Markdown skipped; mixed args keep only Markdown) and e2e coverage (fix leaves a non-Markdown file byte-for-byte unchanged; check exits clean; a mixed run still lints the Markdown file). Fixes #759 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011giDUosuDeqRDVWgmWyaj5
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files
☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Skipping a directly named non-Markdown path (issue #759) was correct but silent: `mdsmith check .gitattributes` / `mdsmith fix .gitattributes` resolved to zero files and exited 0 with no output, so an explicit argument that did nothing looked like a clean pass. Add ResolveOpts.OnSkipNonMarkdown, a hook fired only in resolveArg's explicit-file branch — never for entries filtered out by a directory walk or glob expansion, where silent filtering is the intended, pre-existing behavior and the user named the directory or pattern rather than the file. check and fix wire the hook to a stderr warning (`mdsmith: skipping "X": not a Markdown file (expected .md, .markdown)`), de-duplicated per path. The warning is gated to the text format and suppressed under --quiet: check and fix emit their diagnostics — including --format json and --format sarif — on stderr, so a prose line on that stream would corrupt the structured output. Adds a unit test for the resolver hook (fires for the explicit file only, not walked entries), unit tests for the warner (text emits + dedup; suppressed for quiet/json/sarif), and e2e coverage for the warning being present in text mode and absent under --quiet and --format json. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011giDUosuDeqRDVWgmWyaj5
|
🟢 Merge Queue — picked up This PR is in the queue and will be batched with other Next: No action needed — you'll get another comment when CI starts on the batch. View merge queue run. |
|
🔵 Merge Queue — CI running Merged into batch branch Next: No action needed — you'll be notified when CI completes. |
|
✅ Merge Queue — merged This PR landed on Next: Done — nothing more to do here. |
Summary
Fixes #759. When a non-Markdown file was named explicitly on the command line,
mdsmith checklinted it as Markdown andmdsmith fixrewrote it. The directory walk and glob expansion already skip such files; only the explicit-path branch was missing the gate.The damage was worst on
.gitattributes, because mdsmith generates the merge-driver block there — sofixrewrote a file it owns (inserting blank lines around#comment lines it mis-parsed as headings), and the result was not even idempotent-to-clean.Root cause
internal/lint/files.goroutes each positional argument three ways, but only two gated onisMarkdown:walkDir) — gated ✅resolveGlob) — gated ✅resolveArg) — added the file unconditionally ❌Change
Commit 1 — skip the file. Gate the explicit-file branch of
resolveArgonisMarkdown, so an explicit path behaves exactly like the walk. Extension is the only signal available at resolve time (.md,.markdown, from themdpathsingle-source-of-truth). Allcheck/fix/list/metricscallers share this resolver, so the behavior is uniform.Commit 2 — warn instead of a silent no-op. Skipping was correct but silent:
mdsmith fix .gitattributesresolved to zero files and exited 0 with no output, so an explicit argument that did nothing looked like a clean pass. AddedResolveOpts.OnSkipNonMarkdown, a hook fired only in the explicit-file branch (never for walk/glob filtering, which stays silent by design).check/fixwire it to a stderr warning:The warning is de-duplicated per path, gated to the text format, and suppressed under
--quiet— becausecheck/fixemit their diagnostics (including--format json/sarif) on stderr, so a prose line on that stream would corrupt structured output.MDS048 is unaffected. The
.gitattributesmanaged-block rule reads.gitattributesfrom disk itself (bytelimit.ReadFileLimited) while linting a Markdown file; the file never needs to be in the linted set.Behavior before / after
A mixed run (
check README.md .gitattributes) still lintsREADME.md, drops the non-Markdown path, and warns about it.Tests
internal/lint/files_test.go): explicit non-Markdown file skipped; mixed args keep only Markdown;OnSkipNonMarkdownfires for the explicit file only, never for a walked entry.cmd/mdsmith/main_unit_test.go): the warner emits + de-dupes in text mode; returns nil (suppressed) for quiet/json/sarif.cmd/mdsmith/e2e_nonmarkdown_test.go):fixleaves.gitattributesbyte-for-byte unchanged and warns;checkexits clean and warns; the warning is absent under--quietand-f json; a mixed run reports the Markdown file and never anchors a diagnostic to the config file.Verification
go test ./...,go vet,golangci-lint— all cleanmdsmith check .—checked=563 failures=0Out of scope
The issue's secondary observation (an MDS048 scoped-vs-unscoped block disagreement) is explicitly marked not currently reproducible by the reporter. Not addressed here.
Docs
docs/reference/cli/check.mdandfix.mddocument that non-Markdown paths are skipped, that naming one explicitly prints a warning, and that--quiet/json/sarifsuppress it.🤖 Generated with Claude Code
https://claude.ai/code/session_011giDUosuDeqRDVWgmWyaj5