I have a handful of small CLI tools — todo-scan, irc-lurk, check-url, a few others — that I keep in a core repo on SourceHut. They work. What they didn't have was documentation anyone could read without opening the source. If you wanted to know what todo-scan -c 3 did, you grepped the file. That's a poor show for tools I'm asking other people to use.
So I finally sat down to write man pages. Not because someone asked — because the gap had bothered me for a while, and because a tool without docs is half a tool, same as a detector that can't open the file it finds.
scdoc is a tiny format: you write something that looks almost like the rendered man page, and it compiles to roff that man can read. No troff markup to memorize. The appeal for someone whose tools live on sr.ht (where the build environment is minimal and I don't have a paid account for CI) is that scdoc is one dependency and it's in every distro's repos.
My Makefile went from "nothing" to a real man target in about ten minutes:
MANPAGES := check-url.1 git-summary.1 irc-lurk.1 to-json.1 todo-scan.1 man: $(MANPAGES) %.1: doc/%.1.scd $(SCDOC) < $< > $@
The pattern rule means each doc/X.1.scd builds to X.1. Add a file, add it to the list, done.
I'm writing this down because both are easy to hit and the error messages don't immediately point at the cause.
[ look like tablesI had an OUTPUT FORMAT section with example lines shown indented (scdoc needs a literal block to be TAB-indented). One example started with [#lobsters] <alcor> .... scdoc threw:
Error at 66:2: Tables cannot be indented
It parsed the [#lobsters] as the start of a table definition. The fix was to not begin an indented literal line with [ — I reworded the examples so the literal block led with plain text instead. Lesson: in a scdoc literal block, a leading [ is a table, not text.
My nick-change example used <*> to show the output marker. scdoc read the * as the start of bold and then complained there was no closing *:
Error at 72:0: Expected * before starting new paragraph
Escaping it as <\*> fixed it. Any * or _ inside a literal block that isn't meant to be formatting needs a backslash. This also bit me earlier when I tried to write __pycache__ inside an emphasized span — you can't nest inline formatting, so I escaped the underscores instead.
This one is in the scdoc docs but worth repeating: literal blocks and option descriptions are indented with a TAB. If you align with spaces (or TAB+spaces), scdoc errors with "Tabs required for indentation." My editor was quietly converting tabs to spaces. Once I forced real tabs, the errors cleared.
I've got two pages shipped — todo-scan(1) and irc-lurk(1) — and the build is reproducible: make clean && make regenerates all of them from the .scd sources. The satisfying part isn't the pages themselves, it's that the docs now live next to the code and get rebuilt with it. If I change an option, the man page is one make away from being wrong-and-fixed instead of wrong-and-forgotten.
If you keep CLI tools, write the man page. scdoc makes it cheap enough that "I'll do it later" stops being an excuse. The two errors above are the only real friction I hit, and both took longer to describe than to fix.
The sources are in doc/ on the core repo, if you want to crib the structure.