Tool · Node.js
draft-cli
Fill placeholders in a legal-document template with parameter values. Five-tier detection
(literal brackets, mustache, .docx highlights, generic-name
heuristic, optional LLM), a locked parameter-schema contract, and a .docx
round-trip that preserves runs and styling. The drafting step at the start of the contract
pipeline.
npm i -g @drbaher/draft-cli, then
draft template.md --party-a "Acme" --output filled.md. Hand
.docx in and out for a styling-preserving round-trip. Run
--list-placeholders first to see what the template needs.
Try it live
Edit the template or the params and hit Run — it fills the placeholders for real, in a sandbox.
Runs the real CLI on your input in a sandbox — no setup, nothing stored. Open in a new tab ↗
What it does
- Five-tier detection cascade. Bracket syntax (
[Party A]), mustache ({{party_a}}),.docxhighlighted runs, a generic-name heuristic dictionary, and (only when configured) an LLM tier. First tier with ≥1 hit wins; the rest are skipped. Active tier is reported in--whyand JSON output. .docxround-trip with run preservation. Input.docx, output.docx. The Word package is unzipped, substitutions land inside the same<w:t>runs detection found, and everything else (relationships, images, headers, content types) passes through unchanged. v0.9.0 added smart run-merging for placeholders split across runs (the Common Paper case) with--strict-runsas the opt-out.- Typed parameters. Schema declares
type(date/money/party) and per-typeformat+currency. Inputs normalize before substitution —"2026-06-01"renders asJune 1, 2026,5000000as$5,000,000.00. - Computed placeholders. Date arithmetic via
computed.fromin the schema.expiry_date=effective_date + term, derived once at substitution time. No spreadsheet glue. - Positional addressing. When the same role appears under different labels (YC SAFE: "Investor" on the cover page, "[Investor]" in the body),
positionsmaps the role to its occurrence list so one CLI flag fills all positions. parties.jsonregistry. Resolveref:parties.acme_corp.nameacross templates so the same legal entity definition is the single source of truth.- Multi-document bundles. One parameter set, many output documents. NDA + SAFE + Side Letter all share the same
party_a,party_b,effective_date; bundle config maps each template to its output path. - LLM-from-deal inference (opt-in).
--from-deal deal-notes.txthands free-form prose to your configured LLM provider; only declared placeholder keys are extracted, CLI /--paramsalways win on collision. Extra keys warned to stderr;--no-llmopts out. - Honest discovery + diff.
--list-placeholdersenumerates what the template needs (keys, aliases, occurrence counts, tier).--diffshows the substitution table without writing output.--whyprints a stable structured explanation.
Quickstart
# Install
npm i -g @drbaher/draft-cli
draft --version
# What placeholders does this template need?
draft template.md --list-placeholders
# Fill a markdown template; print to stdout
draft template.md --party-a "Acme Corp" --party-b "Globex"
# Fill a Word document; round-trip styling preserved
draft contract.docx --params deal.json --output filled.docx
# Preview substitutions without writing output
draft template.md --params deal.json --diff
# Validate completeness (CI / agent precheck)
draft template.md --params deal.json --validate
# Infer values from unstructured prose (LLM, when configured)
draft nda.md --from-deal deal-notes.txt --output draft.md Why a separate drafting tool
nda-review-cli ships a few bundled NDA templates and can
render them via draft — fine when you're drafting an NDA against
one of those known templates. draft-cli generalizes the same idea:
any markdown or .docx file with placeholders, any parameter set,
any output format. Side letters, SAFEs, employment agreements, board consents, vendor MSAs
— same shape, same substitution discipline, same --why /
--diff / --validate affordances.
The locked PARAM_SCHEMA.md contract is the load-bearing part: schemas don't change between minor versions, so an agent or pipeline that reads the schema at startup can rely on the surface shape. --json output mirrors the same shape.
For agents and automation
Agent affordances are baked in. AGENTS.md documents the output envelope, exit codes, and failure → recovery loop. --list-placeholders --json is a structured manifest of what a template needs (keys, aliases, types, defaults). --validate is the precheck before substitution. The LLM tier is opt-in and isolated under its own provider config (ANTHROPIC_API_KEY / OPENAI_API_KEY / explicit DRAFT_LLM_*); no network call without consent.
# Discover schema, then fill — error-fast at validation
draft template.md --list-placeholders --json
draft template.md --params deal.json --validate
draft template.md --params deal.json --output filled.docx --json Where it fits in the workflow
An early step in the nine-CLI workflow: take a template from your vault
(template-vault-cli) — or any markdown / .docx
you control — and produce a filled draft to hand to
nda-review-cli for policy review or directly to
docx2pdf-cli for PDF rendering. Before signing, the filled
draft becomes the BASE that compare-cli gates against in
step 5 — making sure the artifact you're about to seal still matches what you drafted.
See the full workflow for the chained commands.
It's also useful on its own for any templated drafting — SOWs, invoice prefaces, board materials, anything with placeholders. The contract pipeline is one application.
Repo
github.com/DrBaher/draft-cli · MIT licensed · Node.js · npm-installable.