Skip to content
contract-ops CLI suite

Tool · Node.js

draft-cli

draft-cli v0.10.2 0 203/wk on npm Latest from npm · checked Sun, 07 Jun 2026

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.

TL;DR — 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.
draft --demo — placeholder fill

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}}), .docx highlighted 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 --why and JSON output.
  • .docx round-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-runs as the opt-out.
  • Typed parameters. Schema declares type (date / money / party) and per-type format + currency. Inputs normalize before substitution — "2026-06-01" renders as June 1, 2026, 5000000 as $5,000,000.00.
  • Computed placeholders. Date arithmetic via computed.from in 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), positions maps the role to its occurrence list so one CLI flag fills all positions.
  • parties.json registry. Resolve ref:parties.acme_corp.name across 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.txt hands free-form prose to your configured LLM provider; only declared placeholder keys are extracted, CLI / --params always win on collision. Extra keys warned to stderr; --no-llm opts out.
  • Honest discovery + diff. --list-placeholders enumerates what the template needs (keys, aliases, occurrence counts, tier). --diff shows the substitution table without writing output. --why prints a stable structured explanation.

Quickstart

install + run
# 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.

recommended agent defaults
# 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.

Edit this page on GitHub