Skip to content
contract-ops CLI suite

The workflow

From a versioned template to a signed agreement, on your machine.

Nine steps across nine CLIs. Each is a single CLI invocation. The output of one step is the input of the next. Stop at any point — the artifacts you have are useful on their own. Two entry points: ingest foreign paper with extract (step 0), or start from your own versioned template (step 1).

TL;DR — extracttemplate-vault getdraftreviewnegotiatecontract-lintcomparedocx2pdfsigncontract-vault. Nine tools, no SaaS, no shared database. Files in, files out.
parallel gates — both must pass
Tap any step above to see what that CLI does.

0 — Ingest any contract (the open-loop front door)

The rest of this pipeline is a closed loop that handles documents authored from your own templates. extract-cli is how foreign paper gets in: hand it a counterparty's contract in .md / .txt / .html / .docx / .pdf and it returns structured JSON — parties, dates, governing law, and a clause map normalized onto the same canonical vocabulary the rest of the suite speaks. Feed that straight into review (step 3) or compare (step 5). Authoring from your own template instead? Skip to step 1.

step 0 · ingest
# Turn a counterparty's contract into structured JSON
extract counterparty-nda.docx --output extract.json

# Its canonical_title values line up with your vault, so a plain diff finds clause gaps
extract counterparty-nda.docx | jq -r '.clauses[].canonical_title' | sort -u

1 — Store & version

Start in your vault. template-vault-cli keeps your house templates and forked public sources (Common Paper, YC SAFE, Bonterms) in one Git-backed, searchable place. Pull a public template in, or compose your own by forking a base and swapping a clause — every swap records its provenance in meta.json, so a later upgrade pulls parent improvements in without clobbering your local changes. Resolve a versioned template to a path and hand it to the next step.

step 1 · store
# One-time: make a vault and import a public source
template-vault init
template-vault import common-paper-mutual-nda     # → nda/common-paper-mutual

# Compose a house variant; swap one clause in, with provenance recorded
template-vault compose --base nda/common-paper-mutual --as nda/house-mutual
template-vault swap nda/house-mutual --clause "Term and Survival" --from nda/yc

# Resolve the template path to hand to draft-cli
template-vault get nda/house-mutual --path-only

2 — Draft

Take the template from your vault and fill it with draft-cli. Point it at the file (markdown or .docx — the Common Paper Mutual NDA, your own house template, a SAFE, anything with placeholders) and pass the parameter values. --list-placeholders first if you want to see what the template expects; --diff previews the substitutions before writing. Output is markdown or a styling-preserving .docx round-trip.

step 2 · draft
# Discover what the template needs (run once)
draft templates/mutual-nda.docx --list-placeholders

# Fill it, .docx → .docx with styling preserved
draft templates/mutual-nda.docx \
  --party-a "Acme Inc." \
  --party-b "Beta LLC" \
  --purpose "evaluating a partnership" \
  --effective-date "2026-06-01" \
  --term "2 year(s)" \
  --governing-state "California" \
  --output output/nda.docx

# Or feed unstructured prose and let the LLM tier extract values
draft templates/mutual-nda.md --from-deal deal-notes.txt \
  --output output/nda.md

draft-cli generalizes drafting: anything with placeholders can be filled the same way (NDAs, SOWs, SAFEs, board consents). nda-review-cli also ships a few bundled NDA templates and can render them directly via its own draft subcommand — convenient when you're working with one of those known templates and want the review step pre-wired in the same invocation.

3 — Review

Whether the document was drafted by you or arrived from the other side, run it through review to score it against your house policy. Findings come with severity, matched rule patterns, and clause-level evidence. Optionally add --llm to layer in a second-pass adjudication from your model of choice.

step 3 · review
nda-review-cli review --file output/nda.md --why \
  --out-md output/review.md

4 — Negotiate

If the counterparty has the CLI installed too, you can run a structured back-and-forth on a single state file that bounces between you (email, Drive, Git — whatever channel you prefer). Each round is signed and hash-chained; tampering is detected on load. Stance, clause priorities, and non-negotiable redlines all live in your local policy.

step 4 · negotiate
# Party A starts the negotiation
nda-review-cli negotiate init --template common-paper-mutual ... --out neg.json

# Party B counters using their own policy and stance
nda-review-cli negotiate counter --state neg.json --as b --auto

# Party A reviews the diff and accepts
nda-review-cli negotiate diff   --state neg.json --out-md round-2.md
nda-review-cli negotiate accept --state neg.json --as a

# Both sign off; finalize emits the agreed text
nda-review-cli negotiate sign-off --state neg.json --as a --yes
nda-review-cli negotiate sign-off --state neg.json --as b --yes
nda-review-cli negotiate finalize --state neg.json \
  --out-md output/agreed.md --out-docx output/agreed.docx

No counterparty CLI install? Send them the .docx directly and skip ahead. Negotiation is opt-in; the workflow doesn't depend on it.

5 — Lint, then gate for drift

Two complementary gates run before anything goes into a signing envelope. First, contract-lint checks the document's internal consistency — leftover placeholders, broken cross-references, undefined/unused defined terms, numbering gaps, and party-name/date inconsistencies — and exits non-zero so CI can block on it. Where compare gates drift between versions, contract-lint gates defects within one document.

step 5a · contract-lint
# Catch internal defects before the file goes anywhere
contract-lint ready-to-sign.md --check --fail-on error
# exit 0 = clean · 1 = findings at/above the threshold · 2 = bad usage

Then compare-cli does a clause-aware diff between the text you agreed to and the artifact you're about to sign — catching the quiet shift between "final version" and "the file the paralegal actually sent back." The verdict is an exit code your script or CI can branch on, so a human only gets pulled in when the drift is substantive.

step 5b · compare
# Did the ready-to-sign file drift from the agreed text?
compare output/agreed.md ready-to-sign.pdf --json
# exit 0 = safe to sign · 2 = substantive drift (review!) · 3 = cosmetic only · 4 = clauses moved

Deterministic and cross-format (.docx / .pdf / .md / text), with no LLM tier — the same two inputs always produce the same verdict, which is what makes it defensible as a gate. If the drift is only cosmetic or a reordering, you've confirmed the agreement didn't change and can proceed.

6 — Convert to PDF

Once you have an agreed .docx, hand it to docx2pdf-cli. Single-file or batch, parallel processing, font validation, and a hybrid backend strategy that picks the right converter for what's on your machine. No silent fallbacks — failures are loud, fonts are checked, the output is honest.

step 6 · convert
docx2pdf output/agreed.docx output/agreed.pdf

# or for a folder full of contracts:
docx2pdf --concurrency 4 --out-dir ./pdfs ./drafts/*.docx

7 — Sign

Hand the PDF to sign-cli. Pick a provider — the built-in local provider produces real PAdES-signed PDFs fully offline (no signup, no API keys), or route through Dropbox Sign, DocuSign, or SignWell for an external trust anchor. Each signer gets a per-recipient approval token with a TTL. Every signing event lands in a hash-chained audit log, and the receipt bundle includes RFC 3161 timestamps so you can prove what was signed and when, even years later.

step 7 · sign
sign request create \
  --title "Mutual NDA" \
  --document output/agreed.pdf \
  --signer "name:Alice Founder,email:alice@acme.com" \
  --signer "name:Bob Counsel,email:bob@beta.com" \
  --provider signwell

# Or skip the convert + sign steps and seal a DOCX in one shot (offline, PAdES)
sign document output/agreed.docx \
  --signer "Alice Founder" --signer-email alice@acme.com \
  --name-signature true --auto-place first \
  --out output/agreed.sealed.pdf

# Verify a signed PDF later, offline (works on any PAdES-signed PDF)
sign pdf inspect --pdf output/agreed.signed.pdf
sign audit verify --request-id <id>

8 — Manage (post-signature)

Signing isn't the end — it's the start of the obligations. Register the executed contract in contract-vault-cli (it ingests the same extract output that drove intake), and it surfaces what you'd otherwise miss: auto-renewal notice deadlines, expirations, and payment dates — as a queryable portfolio and an .ics calendar. risk --strict is a CI gate for a missed auto-renewal notice.

step 8 · manage
# Register the signed contract (ingests extract-cli output)
extract output/agreed.signed.pdf | contract-vault ingest -

# Project the next quarter of renewals / notice deadlines to a calendar
contract-vault due --within 90d --format ics > deadlines.ics

# Renewal-exposure gate (non-zero on a missed auto-renewal notice)
contract-vault risk --within 30d --strict

What you end up with

  • output/agreed.md — the human-readable agreed text
  • output/agreed.docx — the same content as a Word document
  • output/agreed.pdf — the rendered PDF
  • output/agreed.signed.pdf — the signed PDF, with embedded signatures
  • A negotiation state file with every round, every amendment, every signature, hash-chained
  • A signing receipt bundle with all audit events and timestamps
  • A registered deal in your contract-vault — git-tracked, with a renewals/notice .ics calendar

All of these live on your machine. Nothing is held by a vendor. You can archive them, version-control them, or hand them to opposing counsel as evidence of what was agreed and when. That's the whole point.

Skipping or substituting steps

Each tool stands alone. If you already have a filled Word document, skip the vault + draft steps. If you trust the ready-to-sign file, skip the compare gate. If you sign with a notary or a different e-signature provider, skip the signing step — you'll still have a clean PDF from the convert step. The point isn't that you must use all nine; it's that they compose cleanly when you do.

Edit this page on GitHub