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.
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
The first step in the four-CLI workflow: take a template you control (markdown or
.docx) and produce a filled draft to hand to
nda-review-cli for policy review or directly to
docx2pdf-cli for PDF rendering.
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.