Documentation
HTMLP provides a prompt file format and a Rust interface for typed requests and usage accounting. Use either independently, or render checked files into attributed requests.
- Prompt files: sections, token budgets, reasons, signatures, and variables.
- Rust interface: attributed fragments, tools, checked rendering, and token usage.
- API reference: generated directly from the current Rust source.
Install
Version 0.2.0-alpha.4 is distributed through GitHub, not crates.io. For the command-line checker:
cargo install --git https://github.com/alexmckenley/htmlp --tag v0.2.0-alpha.4 --features cli
For typed requests, add this Cargo dependency; add tokens to use the built-in tokenizer:
[dependencies]
htmlp = { git = "https://github.com/alexmckenley/htmlp", tag = "v0.2.0-alpha.4", features = ["runtime"] }
Prompt files
HTMLP 0.2 uses two elements: htmlp and section, plus {{name}} string placeholders. Content is literal Markdown. All declared limits require a nonblank reason.
Files and sections
<htmlp max-tokens="2k" reason="Shared context.">
<section id="system" max-tokens="500" reason="Loaded on every request.">
Make one change. Run its tests.
</section>
</htmlp>
The root limit covers the entire rendered text. A section limit covers all its descendants. Tags, attributes, and comments do not count. Whitespace inside the root does count; indentation is preserved.
1k is 1,000 tokens; 1.5k is 1,500. Counts use cl100k_base by default. Token counts depend on the tokenizer; this is not a universal model limit. The Rust API accepts a custom TokenCounter.
Limit each item
<section id="checks" max-tokens="10k" per-item="1k" reason="Keep checks concise.">
<section id="test">Run the affected tests.</section>
<section id="commit">Explain the change.</section>
</section>
Place this section inside an htmlp root. The total is capped at 10,000 tokens; each item at 1,000. per-item applies to each direct child section. The smaller of that cap and the child's own cap wins. One reason explains the limits declared on that element.
Sign budgets
Run htmlp sign ./prompts after authoring. Each declared budget gets a short sig covering max-tokens, per-item, and reason. Checking rejects missing or stale signatures and asks you to rerun htmlp sign for intentional changes. Content edits need no re-signing.
Review changes before signing; run only check in CI. The checksum is not approval: anyone can re-sign with the same reason or remove a constraint.
Variables
<htmlp max-tokens="1k" reason="Request context.">
Answer: {{question}}
</htmlp>
Variables have no limits or attributes. Static checks defer counts for sections containing variables. Rendering requires every referenced string binding and enforces file and section limits on the final text. Repeated names reuse the same binding; values are inserted literally.
htmlp sign request.htmlp
htmlp render request.htmlp --vars values.json
Write {{name}} for a literal {{name}}. JSON measurements use tokens: null and deferred: true until rendering.
values.json: {"question":"What changed?"}
Commands
htmlp sign ./prompts # accept current budgets explicitly
htmlp check ./prompts # each .htmlp file independently
htmlp check rules.htmlp --json
htmlp watch ./prompts # editor diagnostics on changes
htmlp parse rules.htmlp # syntax-checked JSON AST
htmlp compile rules.htmlp # static budgets checked; variables deferred
htmlp render rules.htmlp # checked prompt text
No ancestor rules or external configuration are loaded. Scans skip symlinks and .git, node_modules, target, dist, and vendor. A path with no matching files fails.
Rust and other languages
Use parse or parse_file for typed Document, Section, and Variable values. Construct nodes in memory with Document::new, Section::new, and Node::text. Call Document::sign() to accept in-memory budgets, then lint before use. sign_source preserves file formatting.
get_element_by_id returns a typed reference. sections() returns direct child sections. to_string() extracts text with visible {{name}} placeholders; use render for checked substitutions. IDs such as system are labels, not SDK roles.
Other languages can invoke the CLI and consume JSON. Native bindings and build-tool plugins are not included in this alpha.
Runtime prompts
Enable the Rust runtime feature for attributed PromptFragments, typed text/image/tool content, and ModelRequests. Every fragment requires a source category and role. request.estimate() provides categorized byte-based estimates with an explicit image reserve; TokenUsage keeps provider-reported usage separate. Missing attribution is None, not zero.
render_checked returns immutable text, its tokenizer measurement, and non-overlapping spans with section ancestry. section(id) selects from a fully checked document; PromptFragment::checked bridges it into a request. Category attributes are not yet supported in markup.
Rust interface guide and examples · Plain Markdown reference
Library features
| Feature | Enables |
|---|---|
| Default | Typed nodes, parsing, signing, linting, and rendering with a supplied TokenCounter. |
runtime | Typed requests, attribution, estimates, and reported usage. Includes JSON support; no provider SDK or tokenizer tables. |
tokens | Embedded Cl100k tokenizer. |
json | JSON serialization for document types. |
schema | JSON Schema generation; includes json. Add runtime for request and usage schemas. |
cli | Command-line binary; includes json and tokens. |
Strict markup
Use lowercase tags, quoted attributes, and unique section IDs. Empty elements can self-close: <section id="notes" />. Otherwise use matching closing tags. Escape literal < as < and & as &, including inside Markdown code fences. Unknown elements and attributes fail. No browser error recovery is performed. Self-closing non-void elements follow XML rules, so ordinary HTML parsing is not equivalent.
Limits and reasons are editable source. Require review or CI policy to protect changes to budgets. A reason explains a constraint; it cannot prevent an agent from changing it.