{"id":51594378,"url":"https://github.com/drbaher/draft-cli","last_synced_at":"2026-07-11T17:02:18.001Z","repository":{"id":358258760,"uuid":"1240658903","full_name":"DrBaher/draft-cli","owner":"DrBaher","description":"Agent-first placeholder-filler for legal-document templates (markdown/.docx round-trip, typed params, multi-doc bundles). Part of the eight-CLI contract-ops suite — cli.drbaher.com","archived":false,"fork":false,"pushed_at":"2026-06-04T07:33:09.000Z","size":314,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T09:15:03.296Z","etag":null,"topics":["agent-first","ai-agent","cli","contract-automation","contract-ops","contracts","document-generation","docx","legal-tech","legaltech","llm","local-first","markdown","nda","nodejs","placeholders","templates","templating"],"latest_commit_sha":null,"homepage":"https://cli.drbaher.com/tools/draft-cli/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DrBaher.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-16T12:06:21.000Z","updated_at":"2026-06-04T07:33:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/DrBaher/draft-cli","commit_stats":null,"previous_names":["drbaher/draft-cli"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/DrBaher/draft-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DrBaher%2Fdraft-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DrBaher%2Fdraft-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DrBaher%2Fdraft-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DrBaher%2Fdraft-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DrBaher","download_url":"https://codeload.github.com/DrBaher/draft-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DrBaher%2Fdraft-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35368768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-first","ai-agent","cli","contract-automation","contract-ops","contracts","document-generation","docx","legal-tech","legaltech","llm","local-first","markdown","nda","nodejs","placeholders","templates","templating"],"created_at":"2026-07-11T17:02:15.668Z","updated_at":"2026-07-11T17:02:17.993Z","avatar_url":"https://github.com/DrBaher.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/icon.svg\" width=\"120\" alt=\"draft-cli\"\u003e\n\u003c/p\u003e\n\n# draft-cli\n\n\u003e Part of the contract-ops CLI suite. **draft-cli** (fill placeholders) → [**nda-review-cli**](https://github.com/DrBaher/nda-review-cli) (review, redline, negotiate) → [**docx2pdf-cli**](https://github.com/DrBaher/docx2pdf-cli) (DOCX → PDF) → [**sign-cli**](https://github.com/DrBaher/sign-cli) (signing + audit). Storage layer: [**template-vault-cli**](https://github.com/DrBaher/template-vault-cli). Drift detection: [**compare-cli**](https://github.com/DrBaher/compare-cli). [Showcase site](https://cli.drbaher.com/).\n\n[![npm version](https://img.shields.io/npm/v/@drbaher/draft-cli.svg)](https://www.npmjs.com/package/@drbaher/draft-cli)\n[![npm downloads](https://img.shields.io/npm/dw/@drbaher/draft-cli.svg)](https://www.npmjs.com/package/@drbaher/draft-cli)\n[![CI](https://github.com/DrBaher/Draft-CLI/actions/workflows/ci.yml/badge.svg)](https://github.com/DrBaher/Draft-CLI/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nAgent-first placeholder-filler for legal-document templates. Reads bracketed (`[Party A]`) or mustache markup, `.docx` yellow highlights, or generic-name heuristics; substitutes from CLI flags, a JSON params file, a shared `parties.json` registry, or — optionally — values extracted by an LLM from a free-form deal description; writes a ready-to-review draft as text or `.docx` (round-trip, runs and styles preserved). Schema-declared placeholders can be **typed** (`date` / `money` / `party`), **computed** (date arithmetic from another placeholder), **positional** (same text disambiguated by role), and **bundled** (fill multiple templates with one set of values).\n\n**The asymmetry is the architecture**: every step is deterministic and machine-driven except the values themselves — which can come from a flag, a params file, a parties registry, or an LLM extracting them from a prose deal description that a human wrote.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/demo.svg\" alt=\"draft-cli demo: list placeholders then substitute against the Common Paper NDA cover page\" width=\"900\"\u003e\n\u003c/p\u003e\n\n## Run this\n\n```bash\nnpx @drbaher/draft-cli@latest --demo\n```\n\n30 seconds, no file authoring required. Walks through a bracketed-template NDA with three placeholders, demoing the full cascade end-to-end. Or if you want to dive into a real template:\n\n```bash\nnpm i -g @drbaher/draft-cli\ndraft --list-placeholders examples/cp-mutual-nda-coverpage.md\n```\n\n## Where to go next\n\n| If you are… | Start here |\n|---|---|\n| **A new user** evaluating the tool | This README's [Quick start](#quick-start) and [What this gives you](#what-this-gives-you) |\n| **A drafter** filling your first template | [GETTING_STARTED.md](GETTING_STARTED.md) — 10-minute walkthrough of the main flows |\n| **An LLM agent** driving the CLI | [AGENTS.md](AGENTS.md) → `draft --list-placeholders --json` → [PARAM_SCHEMA.md](PARAM_SCHEMA.md) for the locked contract |\n| **A schema author** declaring typed / computed / positional placeholders | [PARAM_SCHEMA.md](PARAM_SCHEMA.md) §5 |\n| **A contributor** | [ARCHITECTURE.md](ARCHITECTURE.md), [CONTRIBUTING.md](CONTRIBUTING.md) |\n| **Adding a new CLI to the suite** | The [build-a-CLI playbook](https://cli.drbaher.com/build-a-cli) — the conventions every suite CLI follows |\n\nConcept deep-dives live in [PARAM_SCHEMA.md](PARAM_SCHEMA.md) (the v1 + v2 contract); architecture in [ARCHITECTURE.md](ARCHITECTURE.md); FAQ in [FAQ.md](FAQ.md).\n\n## Quick start\n\n```bash\n# Install\nnpm i -g @drbaher/draft-cli\n\n# Or run without installing\nnpx @drbaher/draft-cli@latest --demo\n\n# After install, the binary is named `draft`\ndraft --version\ndraft --demo\n```\n\nRequires Node.js ≥ 18. Tested on Ubuntu and macOS, Node 18 / 20 / 22.\n\n### Shell completion\n\n```bash\n# bash\ndraft --completion bash \u003e\u003e ~/.bashrc\n\n# zsh\ndraft --completion zsh \u003e ~/.zsh/completions/_draft\n# ensure ~/.zsh/completions is in fpath, then: autoload -U compinit \u0026\u0026 compinit\n```\n\nCompletes flags, the `--syntax bracket|mustache` value, `--completion bash|zsh`, and file paths for `--params`, `--output`, `--dictionary`.\n\n## What this gives you\n\n- **Five-tier detection cascade** — `[Title Case]` brackets / `{{mustache}}` / `.docx` highlights (yellow/green/cyan/magenta) / heuristic dictionary / optional LLM. First tier with hits wins; the rest are skipped. Deterministic through tier 4.\n- **`.docx` round-trip** — read a `.docx` template, fill placeholders, write `\u003cbasename\u003e-filled.docx` with runs/styles/paragraph-breaks preserved. T3 highlight detection works against real templates (Common Paper, YC SAFE).\n- **Schema file** for canonical keys, alias phrases, defaults, required-ness, and the v2 fields below (`type`, `format`, `currency`, `computed`, `positions`). Without a schema, every detected bracketed phrase is treated as a required parameter.\n- **Typed parameters** — `type: date | money | party` validates and normalizes inputs before substitution. `\"2027-01-15\"` → `\"January 15, 2027\"`; `\"$5M\"` → `\"$5,000,000.00\"`. Bad inputs exit 2 with per-key error.\n- **Computed placeholders** — derive one placeholder's value from another via date arithmetic: `{ \"from\": \"effective_date\", \"op\": \"+\", \"value\": \"2 years\" }`. Cycle detection at schema parse time.\n- **Positional addressing** — same placeholder text with different semantic roles, addressed by position. Validated against the YC SAFE `$[_____________] × 2` case.\n- **`parties.json` registry** — declare known parties once; schemas reference `ref:parties.\u003ckey\u003e.\u003cfield\u003e`. Eliminates duplicating party metadata across templates.\n- **Multi-document bundles** — fill multiple templates with one shared set of values: `draft --bundle deal.bundle.json --params deal.json`. Abort-all on any pre-write error.\n- **LLM-from-deal inference** — `--from-deal PATH` reads a free-form deal description and asks the configured T5 provider (Anthropic / OpenAI / `DRAFT_LLM_*`) to fill the schema's parameters. CLI / `--params` still win over inferred values.\n- **Composable I/O** — stdin (`-`), stdout default, `--output PATH`, `template-vault get` integration for `\u003ccategory\u003e/\u003cname\u003e[@version]` template refs.\n- **Three modes** — `draft` (substitute and emit), `--list-placeholders` (enumerate), `--validate` (completeness check). All support `--json` and `--why`.\n- **Single file, stdlib + `jszip`**, no telemetry, local-first. Network only when the LLM tier is explicitly configured.\n\n## End-to-end transcript\n\n```sh\n$ cat \u003e nda.md \u003c\u003c'EOF'\nThis Agreement is between [Party A] and [Party B], effective [Effective Date].\n[Party A] and [Party B] agree to keep confidential information confidential.\nEOF\n\n$ draft --list-placeholders nda.md\nparty_a  (Party A)  ×2  [tier=bracket]\nparty_b  (Party B)  ×2  [tier=bracket]\neffective_date  (Effective Date)  ×1  [tier=bracket]\n\n$ draft nda.md --party-a \"Acme\" --party-b \"Vendor Inc.\" --effective-date 2026-06-01\nThis Agreement is between Acme and Vendor Inc., effective 2026-06-01.\nAcme and Vendor Inc. agree to keep confidential information confidential.\n\n$ cat \u003e deal.json \u003c\u003c'EOF'\n{\"party_a\": \"Acme\", \"party_b\": \"Vendor Inc.\", \"effective_date\": \"2026-06-01\"}\nEOF\n\n$ draft nda.md --params deal.json --output draft.md --why\ndraft: substituted 3 of 3 placeholders → draft.md\nwhy:\n  input         = nda.md\n  tier          = bracket\n  schema        = (none, inferred)\n  placeholders  = 3 distinct, 5 occurrences\n  resolved      = 3 (0 from CLI, 3 from --params, 0 interactive, 0 default)\n  defaulted     = 0\n  unresolved    = 0\n  unmapped      = 0\n  warnings      = 0\n\n$ draft --validate nda.md --params deal.json \u0026\u0026 echo \"ok\"\nok: 3 parameter(s) resolved\nok\n```\n\n## Detection cascade\n\n`draft-cli` finds placeholders by trying five strategies in order. The **first non-empty tier wins** and the others are skipped.\n\n| Tier | Strategy             | When                                      |\n| ---- | -------------------- | ----------------------------------------- |\n| 1    | `[Title Case]`       | Default. Matches Common Paper / YC SAFE / Bonterms convention. |\n| 2    | `{{Title Case}}`     | Opt-in with `--syntax mustache`.          |\n| 3    | `.docx` highlights   | Auto on `.docx` input. Yellow / green / cyan / magenta runs. |\n| 4    | Heuristic dictionary | Bundled list of generic names (`Acme Corporation`, `John Doe`, `example@example.com`, etc.). Warn-only by default. |\n| 5    | LLM                  | Last resort. Runs only when `.env` or process env configures `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `DRAFT_LLM_*`. |\n\nThe cascade is **deterministic through tier 4**. Tier 5 is the only non-deterministic step and runs only when you've explicitly configured a provider key. Pass `--no-llm` to disable it even when configured. Pass `--no-heuristic` to skip tier 4.\n\nSee [PARAM_SCHEMA.md](PARAM_SCHEMA.md) for the full contract.\n\n## Schema file (optional)\n\nA sibling `\u003ctemplate\u003e.params.json` lets you declare canonical keys, alias phrases, defaults, and required-ness. The **long form** (gated on `_meta`) unlocks the v2 fields:\n\n```json\n{\n  \"_meta\": { \"schema_version\": 1 },\n  \"party_a\":         { \"aliases\": [\"Party A\"], \"required\": true, \"type\": \"party\" },\n  \"effective_date\":  { \"aliases\": [\"Effective Date\"], \"type\": \"date\", \"format\": \"MMMM d, yyyy\" },\n  \"term_end\":        { \"aliases\": [\"Term End\"], \"type\": \"date\",\n                       \"computed\": { \"from\": \"effective_date\", \"op\": \"+\", \"value\": \"2 years\" } },\n  \"purchase_amount\": { \"aliases\": [\"Purchase Amount\"], \"type\": \"money\", \"currency\": \"USD\" },\n  \"blank\":           { \"aliases\": [\"_____________\"], \"type\": \"money\", \"currency\": \"USD\",\n                       \"positions\": [{ \"role\": \"valuation_cap\" }, { \"role\": \"purchase_amount\" }] }\n}\n```\n\nWith a schema, `draft-cli` substitutes **only** declared parameters and leaves other bracketed text untouched. Without one, every detected bracketed phrase is treated as a required parameter.\n\nThe **short form** is just an aliases map: `{ \"party_a\": [\"Party A\"] }`.\n\nSee [PARAM_SCHEMA.md](PARAM_SCHEMA.md) §5 for the full schema contract and the locked design decisions per v2 feature.\n\n## Command reference\n\n```\ndraft \u003ctemplate\u003e              fill placeholders and emit the result\ndraft \u003ccategory\u003e/\u003cname\u003e       pull via `template-vault get`\ndraft -                       template body on stdin\ndraft --demo                  bundled demo, no file needed\ndraft --list-placeholders \u003ct\u003e enumerate placeholders and exit\ndraft --validate \u003ct\u003e --params completeness check, no output\ndraft --bundle \u003cbundle.json\u003e  multi-doc bundle mode (v0.7.0)\n\nOPTIONS\n  --params FILE          JSON file of param values (snake_case keys)\n  --parties PATH         parties.json registry for ref:parties.\u003ckey\u003e.\u003cfield\u003e  (v0.6.0)\n  --bundle PATH          fill multiple templates in one invocation             (v0.7.0)\n  --from-deal PATH       LLM-extract values from a free-form deal description  (v0.8.0)\n  -o, --output PATH      write to PATH (default: stdout). `-` forces stdout.\n                         For .docx input, default is \u003cbasename\u003e-filled.docx   (v0.2.0)\n  --syntax bracket|mustache\n  -i, --interactive      prompt for missing required parameters\n  --why                  structured explanation to stderr\n  --json                 machine-readable result on stdout\n  -q, --silent           suppress all stderr (warnings, --why, notes)\n  --no-heuristic         disable tier 4\n  --yes-heuristic        substitute tier-4 matches without confirmation\n  --no-llm               disable tier 5 + --from-deal even when env is configured\n  --llm                  assert that env is configured (fail-fast if not)\n  --check-llm            one-token roundtrip to verify provider config\n  --diff                 show substitution table without writing output\n  --dictionary PATH      override the bundled heuristic dictionary\n  --\u003cparam-name\u003e VALUE   set a parameter directly (kebab → snake_case)\n  -h, --help             show full help\n  -V, --version          show version\n```\n\nExit codes: `0` ok · `1` i/o · `2` validation (incl. schema / typed parameter / computed / ref / positional) · `3` template-vault failure · `4` llm failure.\n\n## LLM tier (env-gated, opt-in)\n\nWhen tiers 1–4 all find nothing, `draft-cli` falls back to a language model **only if** a provider is configured. Resolution order: the environment first (`.env` in the working directory, then `process.env` — process wins), and if neither configures a provider, the suite-shared **`~/.config/contract-ops/llm.json`** (then the legacy `~/.config/draft-cli/llm.json`). Configure the shared file once and every contract-ops CLI that supports an LLM picks it up; an explicitly-exported env key always overrides it.\n\n```sh\necho 'ANTHROPIC_API_KEY=sk-ant-…' \u003e\u003e .env\ndraft some-freeform-draft.md          # tier 5 auto-runs when 1-4 empty\n```\n\nSupported providers: Anthropic (`ANTHROPIC_API_KEY`), OpenAI (`OPENAI_API_KEY`), or explicit (`DRAFT_LLM_PROVIDER` + `DRAFT_LLM_API_KEY` + optional `DRAFT_LLM_MODEL`) — or a `{ \"provider\", \"api_key\", \"model\" }` object in `~/.config/contract-ops/llm.json`. The LLM receives template text only — no params file, no `.env` contents, no other data. Pass `--no-llm` to disable even when configured.\n\n**v0.8.0 inverse direction — `--from-deal PATH`:** feed prose deal notes and the LLM extracts values for the schema's placeholders (instead of inferring where placeholders are). Uses the same provider config. Errors on provider missing, network failure, or non-JSON response. CLI / `--params` values always win over LLM-inferred ones. See [PARAM_SCHEMA.md](PARAM_SCHEMA.md) §5.\n\n## Composability\n\n`draft-cli` reads from stdin, writes to stdout by default, and exits with distinct codes for each failure class. It composes with `template-vault-cli` on the read side and `nda-review-cli` / `docx2pdf-cli` / `sign-cli` on the write side:\n\n```bash\n# Read side — pull a versioned template and fill it from a free-form deal note\ntemplate-vault get nda/house-mutual@v3 \\\n  | draft - --from-deal deal-notes.txt --params parties.json \u003e filled.md\n\n# Write side — review the draft (a gate), then render the agreed .docx and sign it offline\nnda-review-cli review --file filled.md --playbook policy.json --why\ndocx2pdf agreed.docx agreed.pdf\nsign document agreed.pdf --signer \"Counsel\" --signer-email counsel@counterparty.com\n```\n\nEach tool stays small and replaceable. None of them know about each other beyond the standard `stdin / stdout / argv / exit codes` contract.\n\n## Documentation\n\n- [GETTING_STARTED.md](GETTING_STARTED.md) — 10-minute walkthrough of the main flows.\n- [AGENTS.md](AGENTS.md) — JSON shapes, exit codes, library use; everything an LLM agent driving `draft-cli` needs.\n- [ARCHITECTURE.md](ARCHITECTURE.md) — how the cascade, schema, and substitution pipeline fit together.\n- [PARAM_SCHEMA.md](PARAM_SCHEMA.md) — v1 + v2 schema contract; locked decisions per feature.\n- [FAQ.md](FAQ.md) — common questions about detection rules, T4 heuristics, and the LLM tier.\n- [V2_BRIEFS_REMAINING.md](V2_BRIEFS_REMAINING.md) — design briefs for the four v2 items shipped after v0.1.x; historical now that all four have landed.\n- [SECURITY.md](SECURITY.md) — reporting vulnerabilities.\n- [CHANGELOG.md](CHANGELOG.md) — every shipped version with locked design decisions.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrbaher%2Fdraft-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrbaher%2Fdraft-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrbaher%2Fdraft-cli/lists"}