@drbaher/draft-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/FAQ.md ADDED
@@ -0,0 +1,190 @@
1
+ # FAQ
2
+
3
+ ## Why `[Bracketed Title Case]` as the primary syntax instead of mustache?
4
+
5
+ The legal templates `draft-cli` targets — Common Paper, YC SAFE,
6
+ Bonterms, most house templates — use square-bracket placeholders. That's
7
+ the de-facto convention in the legal-tech corner of the world. Mustache
8
+ (`{{...}}`) is the engineering-template convention; it's supported via
9
+ `--syntax mustache` for cases where you've already written a template
10
+ in that flavor.
11
+
12
+ We deliberately don't auto-detect mixed conventions, only warn about
13
+ them. Auto-detection would surprise users whose template includes a
14
+ `{{quote}}` inside a code block.
15
+
16
+ ## Why no computed placeholders like `[Effective Date + 2 years]`?
17
+
18
+ Deferred to v2. The hard part isn't the arithmetic — it's the date
19
+ parsing (handling `2026-06-01`, `June 1, 2026`, `the first business
20
+ day of Q3`, etc.) and the locale/timezone surface. v1 keeps the
21
+ substitution model trivially auditable: each placeholder is replaced
22
+ with the literal string the user supplied. No transformations, no
23
+ locale, no surprises.
24
+
25
+ The schema's long form reserves a future `"computed"` field for this.
26
+
27
+ ## How is this different from a real templating engine like Handlebars or Jinja?
28
+
29
+ Three differences that matter for legal-doc workflows:
30
+
31
+ 1. **Detection cascade.** A templating engine assumes the template
32
+ already has explicit markup (`{{name}}`). `draft-cli` also handles
33
+ `.docx` yellow highlights, generic-name heuristics, and an LLM
34
+ fallback. You can hand it a contract that has *no* markup and get
35
+ a list of likely placeholders.
36
+ 2. **Alias-aware schema.** `[Party A]` and `[Disclosing Party]` and
37
+ `[Originating Party]` are the same role with three names. The schema
38
+ file maps multiple bracketed phrase forms to one canonical key.
39
+ Most templating engines don't have this.
40
+ 3. **Agent-friendly surface.** Every command emits a structured `--why`
41
+ block and supports `--json`. The CLI is built for both humans and
42
+ LLM agents to call.
43
+
44
+ If you're filling in `{{name}}` in an email template, Handlebars is
45
+ fine. If you're filling in `[Party A]` in an NDA and want to know
46
+ which of seven bracketed phrases got substituted from where, this is
47
+ the tool.
48
+
49
+ ## Why is the LLM tier env-gated instead of a CLI flag?
50
+
51
+ Two reasons:
52
+
53
+ 1. **Configuration vs invocation.** Whether you have an LLM provider
54
+ available is a property of your environment, not of any individual
55
+ `draft` invocation. Env is the right surface.
56
+ 2. **Friction-as-consent.** Setting `ANTHROPIC_API_KEY=...` in `.env`
57
+ takes a few seconds and is a deliberate act. Passing `--llm` on the
58
+ command line is easy to muscle-memory into a one-liner without
59
+ thinking about the network call. The env gate forces you to opt in
60
+ once and re-affirm by leaving the key configured.
61
+
62
+ `--no-llm` exists so a script can disable T5 even when env happens to
63
+ be set. `--llm` exists so a script can fail-fast if env is not set
64
+ (instead of silently falling through to T4).
65
+
66
+ ## Why does `--yes-heuristic` even exist? Can't draft-cli just substitute?
67
+
68
+ The heuristic dictionary contains generic-sounding values like
69
+ `Acme Corporation`, `John Doe`, `example@example.com`. If your real
70
+ counterparty happens to be named "Acme Corporation," substituting
71
+ silently would replace their actual name with whatever value you
72
+ supplied for `acme_corporation` — possibly the new party's name, but
73
+ possibly something worse.
74
+
75
+ The default behavior in non-interactive mode is **warn and do
76
+ nothing** — list the matches but leave them untouched. You either
77
+ add the matches to your template's `.params.json` (promoting them to
78
+ declared parameters) or pass `--yes-heuristic` to accept the risk
79
+ non-interactively.
80
+
81
+ In interactive mode, you get a `y/N` prompt per run.
82
+
83
+ ## Why is `draft-cli` Node.js and not Python? The other Python CLI uses pipx.
84
+
85
+ The contract-operations suite has two Python CLIs (`template-vault-cli`,
86
+ `nda-review-cli` — both `pipx`-installed) and two JS CLIs (`docx2pdf-cli`,
87
+ `sign-cli` — both `npm`/`npx`-installed). `draft-cli` joined the JS lane
88
+ during the v1 design conversation, mirroring `docx2pdf-cli`'s posture
89
+ (single-file Node, npm-installed, stdlib + one carefully-chosen dep).
90
+ The decision lives in [CHANGELOG.md](CHANGELOG.md) v0.1.0.
91
+
92
+ The CLI is provider-and-runtime-agnostic from a user perspective: it
93
+ reads stdin, writes stdout, has clear exit codes, and integrates with
94
+ the Python siblings via subprocess (`template-vault get | draft -`).
95
+
96
+ ## Why don't you ship `.docx` output?
97
+
98
+ Round-tripping back to `.docx` (preserving styles, numbered lists, run
99
+ formatting, and the original run boundaries) is its own problem domain.
100
+ The substituted text might be longer or shorter than the original;
101
+ Word's run-and-paragraph model means the substituted text either has
102
+ to inherit the entire surrounding run's formatting, or split itself
103
+ across runs in a way that respects the document's style table. None of
104
+ this is hard, but it's a different feature from "find and replace."
105
+
106
+ For now, `.docx` is **input-only** in v1 — you get text out. If you
107
+ need `.docx` out, run the text through `docx2pdf-cli` first, or open
108
+ the markdown output in Word.
109
+
110
+ ## Why does the canonical key for `[1 year(s)]` look like `_1_year_s`?
111
+
112
+ Because the v1 bracket rule had to handle real Common Paper templates
113
+ (see the v0.1.0 CHANGELOG entry), it admits sentence-shaped placeholders
114
+ with full punctuation. The canonical-key derivation is a permissive slug
115
+ to make sure inferred keys are always valid snake_case identifiers:
116
+ non-alphanumeric runs collapse to `_`, leading digits get a `_` prefix,
117
+ and the result is capped at 60 chars.
118
+
119
+ Real templates with sentence-shaped placeholders should ship a
120
+ `<template>.params.json` schema that maps to clean keys. That's what
121
+ the Common Paper fixture in `tests/fixtures/cp-mutual-nda-coverpage.params.json`
122
+ does — `[1 year(s)]` maps to the clean key `term`.
123
+
124
+ ## Can I extend the heuristic dictionary?
125
+
126
+ Yes. Pass `--dictionary path/to/your.json` — the file must be a JSON
127
+ array of strings. Your file **replaces** the bundled list (not extends
128
+ it). Copy `DEFAULT_HEURISTIC_DICT` out of `draft-cli.mjs` and edit if
129
+ you want a superset.
130
+
131
+ ## What if a template has two `[_____________]` placeholders that should hold different values?
132
+
133
+ The YC SAFE has this exact problem: one `$[_____________]` is the
134
+ purchase amount, another `$[_____________]` is the post-money valuation
135
+ cap. Same bracketed text, different roles.
136
+
137
+ v1's alias-based matching treats them as one parameter. Substituting
138
+ `100000` for `purchase_amount` will replace **both** occurrences.
139
+
140
+ Workarounds for v1:
141
+
142
+ 1. Edit the template before running draft-cli to make the placeholders
143
+ distinguishable: `[Purchase Amount]` and `[Valuation Cap]`. Then
144
+ the schema can map them independently.
145
+ 2. Substitute the first, then run draft-cli again with a different
146
+ value to substitute the second. Awkward but works for two-occurrence
147
+ cases.
148
+ 3. Run draft-cli, then manually fix the second occurrence in the output.
149
+
150
+ Positional-aware placeholder addressing (`[___1]`, `[___2]`) is a v2
151
+ candidate.
152
+
153
+ ## My template has `[COMPANY]` in the signature block. draft-cli ignores it. Why?
154
+
155
+ `draft-cli`'s default detection rule rejects all-caps bracketed runs to
156
+ avoid false positives on section headings like `[CONFIDENTIALITY]`. But
157
+ all-caps signature-block placeholders are real, especially in older
158
+ templates and ones converted from `.docx`.
159
+
160
+ The fix: declare it in the schema file with the all-caps phrase as an
161
+ alias.
162
+
163
+ ```json
164
+ { "company": ["Company Name", "COMPANY"] }
165
+ ```
166
+
167
+ `draft-cli` consults the schema's alias union during detection and
168
+ **rescues** otherwise-rejected runs that are explicitly declared. This
169
+ is documented in [PARAM_SCHEMA.md §5](PARAM_SCHEMA.md). Without the
170
+ schema, the run is silently skipped — which is intentional, but visible
171
+ when you run `--list-placeholders` and don't see the placeholder you
172
+ expected.
173
+
174
+ ## How do I integrate with `template-vault-cli`?
175
+
176
+ `template-vault-cli` ships templates by category and name. `draft-cli`
177
+ recognizes `<category>/<name>[@version]` as a vault ref and shells out
178
+ to `template-vault get`:
179
+
180
+ ```sh
181
+ draft nda/house-mutual --params deal.json
182
+ ```
183
+
184
+ If `template-vault` isn't on `$PATH` or returns non-zero, `draft-cli`
185
+ exits with code `3` and surfaces the vault's error message. You can
186
+ also pipe explicitly:
187
+
188
+ ```sh
189
+ template-vault get nda/house-mutual | draft - --params deal.json
190
+ ```
@@ -0,0 +1,263 @@
1
+ # Getting started
2
+
3
+ A 10-minute walkthrough of the main flows. Assumes you have Node 18+.
4
+
5
+ ## 1. Install (or try without installing)
6
+
7
+ ```sh
8
+ npm install -g @drbaher/draft-cli
9
+ draft --version # → draft-cli 0.1.0
10
+ ```
11
+
12
+ To try without installing globally:
13
+
14
+ ```sh
15
+ npx @drbaher/draft-cli@latest --demo
16
+ ```
17
+
18
+ ## 2. The 30-second demo
19
+
20
+ ```sh
21
+ draft --demo
22
+ ```
23
+
24
+ This substitutes three placeholders in a bundled NDA template and
25
+ prints the result. No file authoring required.
26
+
27
+ ```
28
+ demo: substituting [Party A], [Party B], [Effective Date]
29
+ # Mutual Non-Disclosure Agreement (demo)
30
+
31
+ This Agreement is entered into on 2026-06-01 between Acme Corporation
32
+ and Vendor Inc. (collectively, the "Parties").
33
+ ...
34
+ ```
35
+
36
+ ## 3. Your first real template
37
+
38
+ Save this as `simple-nda.md`:
39
+
40
+ ```markdown
41
+ This Agreement is between [Party A] and [Party B], effective [Effective Date].
42
+ [Party A] and [Party B] agree to keep confidential information confidential.
43
+ ```
44
+
45
+ List the placeholders draft-cli finds:
46
+
47
+ ```sh
48
+ draft --list-placeholders simple-nda.md
49
+ ```
50
+
51
+ Output:
52
+
53
+ ```
54
+ party_a (Party A) ×2 [tier=bracket]
55
+ party_b (Party B) ×2 [tier=bracket]
56
+ effective_date (Effective Date) ×1 [tier=bracket]
57
+ ```
58
+
59
+ Substitute via CLI flags:
60
+
61
+ ```sh
62
+ draft simple-nda.md \
63
+ --party-a "Acme Corporation" \
64
+ --party-b "Vendor Inc." \
65
+ --effective-date 2026-06-01
66
+ ```
67
+
68
+ Substitute via a JSON file:
69
+
70
+ ```sh
71
+ cat > deal.json <<'EOF'
72
+ {"party_a": "Acme Corporation", "party_b": "Vendor Inc.", "effective_date": "2026-06-01"}
73
+ EOF
74
+
75
+ draft simple-nda.md --params deal.json --output draft.md
76
+ cat draft.md
77
+ ```
78
+
79
+ ## 4. The `--why` block
80
+
81
+ ```sh
82
+ draft simple-nda.md --params deal.json --why
83
+ ```
84
+
85
+ `--why` emits a structured stderr block describing what happened:
86
+
87
+ ```
88
+ draft: substituted 3 of 3 placeholders
89
+ why:
90
+ input = simple-nda.md
91
+ tier = bracket
92
+ schema = (none, inferred)
93
+ placeholders = 3 distinct, 5 occurrences
94
+ resolved = 3 (0 from CLI, 3 from --params, 0 interactive, 0 default)
95
+ defaulted = 0
96
+ unresolved = 0
97
+ unmapped = 0
98
+ warnings = 0
99
+ ```
100
+
101
+ The block is parseable but also human-readable. It tells you which
102
+ tier matched, where each value came from, and whether anything got
103
+ defaulted, prompted, or left unresolved.
104
+
105
+ ## 5. The `--json` mode
106
+
107
+ ```sh
108
+ draft simple-nda.md --params deal.json --json
109
+ ```
110
+
111
+ Stdout is a single JSON object that any downstream tool can parse:
112
+
113
+ ```json
114
+ {
115
+ "ok": true,
116
+ "tier": "bracket",
117
+ "output_path": null,
118
+ "output": "This Agreement is between Acme Corporation and Vendor Inc.…",
119
+ "placeholders": [ {…}, {…}, {…} ],
120
+ "sources": { "party_a": "params", "party_b": "params", "effective_date": "params" },
121
+ "warnings": [],
122
+ "unmapped": []
123
+ }
124
+ ```
125
+
126
+ Combine with `--output` to write the substituted text to disk AND get
127
+ the JSON report:
128
+
129
+ ```sh
130
+ draft simple-nda.md --params deal.json --output draft.md --json | jq '.placeholders[].key'
131
+ ```
132
+
133
+ ## 6. Validate before drafting
134
+
135
+ `--validate` runs the same lookup but never writes output. Useful in
136
+ CI / pre-commit hooks:
137
+
138
+ ```sh
139
+ draft --validate simple-nda.md --params deal.json && echo "ok"
140
+ ```
141
+
142
+ Exit code `0` if every required placeholder resolves, `2` otherwise.
143
+ With `--json`, you get a structured `{ok: bool, missing: [...]}` report.
144
+
145
+ ## 7. Alias-aware schema
146
+
147
+ Real legal templates use multiple bracketed phrase forms for the same
148
+ role: `[Party A]` and `[Disclosing Party]` are usually the same person.
149
+ Declare aliases in a sibling `<template>.params.json`:
150
+
151
+ ```sh
152
+ cat > simple-nda.params.json <<'EOF'
153
+ {
154
+ "party_a": ["Party A", "Disclosing Party"],
155
+ "party_b": ["Party B", "Receiving Party"],
156
+ "effective_date": ["Effective Date"]
157
+ }
158
+ EOF
159
+ ```
160
+
161
+ Now `draft simple-nda.md --party-a "Acme"` substitutes **both**
162
+ `[Party A]` and `[Disclosing Party]` everywhere in the template with
163
+ `Acme`.
164
+
165
+ Long form supports defaults:
166
+
167
+ ```json
168
+ {
169
+ "_meta": { "schema_version": 1 },
170
+ "party_a": { "aliases": ["Party A"], "required": true },
171
+ "effective_date": { "aliases": ["Effective Date"], "required": false, "default": "the date first written above" }
172
+ }
173
+ ```
174
+
175
+ The presence of a top-level `_meta` key tells the parser to expect long
176
+ form. With `required: false` and a `default`, a missing CLI value falls
177
+ through to the default and validation still passes.
178
+
179
+ ## 8. Real-world Common Paper template
180
+
181
+ ```sh
182
+ curl -fsSL https://raw.githubusercontent.com/CommonPaper/Mutual-NDA/main/Mutual-NDA-coverpage.md \
183
+ -o coverpage.md
184
+
185
+ draft --list-placeholders coverpage.md
186
+ ```
187
+
188
+ You'll see sentence-shaped placeholders like `[Today's date]`,
189
+ `[1 year(s)]`, and the full
190
+ `[Evaluating whether to enter into a business relationship with the other party.]`.
191
+ The keys are ugly because they're auto-derived. Add a schema file to
192
+ give them clean names:
193
+
194
+ ```sh
195
+ cat > coverpage.params.json <<'EOF'
196
+ {
197
+ "purpose": ["Evaluating whether to enter into a business relationship with the other party."],
198
+ "effective_date": ["Today’s date"],
199
+ "term": ["1 year(s)"],
200
+ "governing_state": ["Fill in state"],
201
+ "jurisdiction": ["Fill in city or county and state, i.e. “courts located in New Castle, DE”"]
202
+ }
203
+ EOF
204
+
205
+ draft coverpage.md \
206
+ --purpose "Evaluating whether to acquire Vendor Inc." \
207
+ --effective-date 2026-06-01 \
208
+ --term "2 years" \
209
+ --governing-state "Delaware" \
210
+ --jurisdiction "New Castle County, Delaware" \
211
+ --output draft.md
212
+ ```
213
+
214
+ The substituted draft preserves Markdown structure (the
215
+ `### Effective Date` heading, the checkbox markers `- [x]`, the
216
+ signature table) byte-for-byte except for the substituted placeholders.
217
+
218
+ ## 9. `.docx` input
219
+
220
+ Pass a `.docx` directly:
221
+
222
+ ```sh
223
+ draft your-template.docx --party-a "Acme" --output draft.md
224
+ ```
225
+
226
+ Tier 1 (bracket) tries first against the extracted text. If the
227
+ `.docx` has no bracketed markup, tier 3 (highlight) catches
228
+ yellow / green / cyan / magenta highlighted runs as placeholders.
229
+ Output is plain markdown; `.docx` output round-trip is a v2 feature.
230
+
231
+ ## 10. Tab completion (optional)
232
+
233
+ ```sh
234
+ # bash
235
+ draft --completion bash >> ~/.bashrc
236
+
237
+ # zsh
238
+ draft --completion zsh > ~/.zsh/completions/_draft
239
+ ```
240
+
241
+ After reloading your shell, tab-completion fills in flags, the
242
+ `--syntax bracket|mustache` argument, and file paths for `--params`,
243
+ `--output`, and `--dictionary`. The dynamic `--<param-name>` flags
244
+ aren't completed (they depend on the template), but every static flag
245
+ is.
246
+
247
+ ## 11. Compose with the rest of the suite
248
+
249
+ ```sh
250
+ template-vault get nda/house-mutual \
251
+ | draft - --params deal.json \
252
+ | nda-review review - --playbook house \
253
+ | docx2pdf - draft.pdf
254
+ ```
255
+
256
+ Each tool reads from stdin, writes to stdout, and exits with distinct
257
+ codes. Add `--why` or `--json` at any step to inspect what happened.
258
+
259
+ ---
260
+
261
+ For the full command reference, see [README.md](README.md#command-reference).
262
+ For the parameter contract details, see [PARAM_SCHEMA.md](PARAM_SCHEMA.md).
263
+ For architectural rationale, see [ARCHITECTURE.md](ARCHITECTURE.md).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DrBaher
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.