lmhtml recipes

Each recipe uses the synthetic pages under the tutorial scratch directory. Compile from the directory containing schema-lmhtml.json; the snippets save reports and outputs below build/ or output/.

Check broken links on a site

Walk each page and each link. Skip external, special and outside-root targets by policy, and ignore a generated noise pattern.

npx --yes --package=@lmctl-ai/lmctl -- lmscript build/check-links.lms
(no output; exit 0)
lmhtml --dsl build/check-links.dslb
{"ok": true, "result": null}
cat build/links.txt
broken-link site/index.html:0: missing.html not found

Find class and ID typos

Compare classes used in markup to CSS selectors and flag selectors that do not match a used ID. Suggestions help locate one-character spelling mistakes.

npx --yes --package=@lmctl-ai/lmctl -- lmscript build/check-classes.lms
(no output; exit 0)
lmhtml --dsl build/check-classes.dslb
{"ok": true, "result": null}
cat build/classes.txt
class-undefined site:0: heding (did you mean heading?)
id-unused site:0: teem (did you mean team?)

Diff two pages

Compare before and after snapshots. The host reports a changed paragraph and a newly added paragraph, ignoring incidental markup noise.

npx --yes --package=@lmctl-ai/lmctl -- lmscript build/diff.lms
(no output; exit 0)
lmhtml --dsl build/diff.dslb
{"ok": true, "result": null}
cat build/diff.txt
diff-changed compare/before/page.html:0: html > body:nth-child(2) > p.intro:nth-child(2): text differs
diff-added compare/after/page.html:0: html > body:nth-child(2) > p:nth-child(3)
summary page:0: 2 differences

Extract common parts into a template

Find repeated subtrees in four sample pages and write a template with slot markers for page-specific content.

npx --yes --package=@lmctl-ai/lmctl -- lmscript build/template.lms
(no output; exit 0)
lmhtml --dsl build/template.dslb
{"ok": true, "result": null}
cat build/template.txt
summary template:0: 2 slots for 4 pages

Change a shared header or footer across pages

Apply one navigation and footer fragment to copies of all four pages. The source files remain untouched.

npx --yes --package=@lmctl-ai/lmctl -- lmscript build/rewrite.lms
(no output; exit 0)
lmhtml --dsl build/rewrite.dslb
{"ok": true, "result": null}
ls output/page*
output/page0.html
output/page1.html
output/page2.html
output/page3.html

Ignore generated noise with link_kind and str_match

Link checks should state what is deliberately outside scope. This version ignores external/special/outside-root targets and a synthetic noise-*.html pattern, while still reporting a missing local page.

npx --yes --package=@lmctl-ai/lmctl -- lmscript build/check-links.lms
(no output; exit 0)
lmhtml --dsl build/check-links.dslb
{"ok": true, "result": null}
cat build/links.txt
broken-link site/index.html:0: missing.html not found

Generate and check a Markdown chapter index

Give each note an h1 and introductory paragraph, then generate its chapter entry once.

lmhtml index durable-memory
lmhtml index durable-memory --check
lmhtml mdcheck durable-memory

Build a templated Markdown site

Save this shell as shell.html. Metadata is escaped; the required content placeholder inserts the converted document body.

<!doctype html><html lang="en"><head><meta charset="utf-8"><title>{{title}}</title></head><body><nav>{{toc}}</nav><main>{{content}}</main></body></html>
lmhtml site durable-memory build/memory-web --template shell.html

Post-process a Markdown document with DSL

The conversion returns the same Doc type used by every existing selector and mutation. Save this as build/plan.lms:

p = _dsl_ schema="schema-lmhtml.json";
let doc = html.from_markdown("# Release plan\n\nChecks before publishing.");
doc.select("h1").at(0).set_attr("id", "release-plan");
html.save(doc, "build/plan.html");
end _dsl_
savedsl(p, "build/plan.dslb");
lmhtml --dsl-schema --json > build/schema-lmhtml.json
lmscript build/plan.lms
lmhtml --dsl build/plan.dslb

Plain content for agents

Use lint to measure unnecessary decoration and entities, normalize to remove them without changing the text, and clean to extract main content for agents. Plain prose has no inline code or emphasis. Copyable commands belong in blocks. Text needs only ampersand and less-than escapes; attributes need only ampersand and quote escapes.

lmhtml lint --stats --json skills tools durable-memory
lmhtml lint --stats --compare BEFORE_DIRECTORY AFTER_DIRECTORY
lmhtml normalize skills tools
lmhtml lint durable-memory
lmhtml clean page.html --base https://example.org/docs/page.html
lmhtml clean page.html --check
lmhtml clean https://example.org/docs/page.html --markdown

clean is deterministic and offline for files. URL input uses curl and requires network access; it runs no JavaScript. Extraction removes navigation, forms, scripts, styles and common advertising/cookie containers, resolves links and prints byte counts and a bytes/4 token estimate on stderr. Heuristics can miss unusual site layouts: inspect extracted content before relying on it. clean --check compares extraction with the page's main content and fails with a diff if content would be dropped.

normalize preserves code-block alignment, removes inline typography, repairs entities and unfolds prose. lint counts violations per file and in total, with densities per 100 words; --config FILE accepts DIRECTORY METRIC MAX_COUNT rows. Defaults are zero, with h2/h3 anchors retained. The DSL equivalents are Doc.normalize() and Doc.clean(base_url).