1use clap::{Parser, Subcommand};
2
3const HELP_FOOTER: &str = "\
4Tips:
5 • Run `contract agent-info | jq` for the full capability manifest (commands, flags, exit codes)
6 • The DB is SHARED with invoice-cli — always `contract issuer list` / `contract clients list` before creating entities
7 • Pipe any command to jq for structured data: `contract list | jq '.data'`
8 • Template chain at render: --template > contract.default_template > \"helvetica-nera\"
9 • Drafts render with a DRAFT watermark; use --final for a clean signing copy
10 • `contract doctor` verifies typst, the DB, packs, and templates before you start
11
12Examples:
13 contract new --kind nda --as acme --client meridian --purpose 'evaluating a joint venture' --term-years 3
14 Quick mutual NDA with a 3-year confidentiality term
15
16 contract new --kind consulting --as acme --client meridian --fee fixed:8400:SGD --term-months 3 \\
17 --purpose 'design a dashboard' --deliverable 'Designs' --deliverable 'Build' --ip-assignment client
18 Consulting agreement with a fixed fee and deliverables
19
20 contract render NDA-acme-2026-0001 --final --open
21 Render a clean, watermark-free PDF and open it
22
23 contract sign NDA-acme-2026-0001 --side us --name 'B. Djordjevic' --title Director
24 Record one side's signature (status auto-bumps to 'signed' when both sides sign)
25
26 contract template list | jq '.data'
27 Inspect available PDF templates as structured JSON";
28
29#[derive(Parser, Debug)]
30#[command(
31 name = "contract",
32 version,
33 about = "Beautiful contracts from the CLI — NDA, consulting, MSA, SOW, service",
34 after_long_help = HELP_FOOTER,
35 after_help = HELP_FOOTER
36)]
37pub struct Cli {
38 #[arg(long, global = true)]
40 pub json: bool,
41 #[arg(long, global = true)]
43 pub quiet: bool,
44 #[command(subcommand)]
45 pub command: Commands,
46}
47
48#[derive(Subcommand, Debug)]
49pub enum Commands {
50 #[command(visible_alias = "issuers", subcommand)]
52 Issuer(IssuerCmd),
53
54 #[command(visible_alias = "client", subcommand)]
56 Clients(ClientCmd),
57
58 #[command(subcommand)]
60 Contracts(ContractCmd),
61
62 #[command(name = "new")]
64 New(ContractNewArgs),
65
66 #[command(name = "list", alias = "ls")]
68 List(ContractListArgs),
69
70 #[command(name = "show", alias = "get")]
72 Show { number: String },
73
74 #[command(name = "render")]
76 Render(ContractRenderArgs),
77
78 #[command(name = "mark")]
80 Mark { number: String, status: String },
81
82 #[command(name = "sign")]
84 Sign(SignArgs),
85
86 #[command(name = "edit")]
88 Edit(ContractEditArgs),
89
90 #[command(name = "duplicate", visible_alias = "clone")]
92 Duplicate(DuplicateArgs),
93
94 #[command(name = "delete", visible_alias = "rm")]
96 Delete(DeleteArgs),
97
98 #[command(visible_alias = "packs", subcommand)]
100 Pack(PackCmd),
101
102 #[command(visible_alias = "templates", subcommand)]
104 Template(TemplateCmd),
105
106 #[command(visible_alias = "kind", subcommand)]
108 Kinds(KindsCmd),
109
110 #[command(subcommand)]
112 Config(ConfigCmd),
113
114 #[command(visible_alias = "info")]
116 AgentInfo,
117
118 #[command(subcommand)]
120 Skill(SkillCmd),
121
122 Doctor,
124
125 Update {
127 #[arg(long)]
129 check: bool,
130 },
131
132 #[command(name = "contract", hide = true)]
134 ExitHook { code: i32 },
135}
136
137#[derive(Subcommand, Debug)]
140pub enum IssuerCmd {
141 #[command(visible_alias = "new")]
143 Add {
144 slug: String,
145 #[arg(long)]
146 name: String,
147 #[arg(long)]
148 legal_name: Option<String>,
149 #[arg(long, default_value = "sg")]
150 jurisdiction: String,
151 #[arg(long)]
152 tax_id: Option<String>,
153 #[arg(long)]
154 company_no: Option<String>,
155 #[arg(long)]
156 address: String,
157 #[arg(long)]
158 email: Option<String>,
159 #[arg(long)]
160 phone: Option<String>,
161 #[arg(long)]
163 logo: Option<String>,
164 #[arg(long)]
166 output_dir: Option<String>,
167 },
168 Edit {
169 slug: String,
170 #[arg(long)]
171 name: Option<String>,
172 #[arg(long)]
173 legal_name: Option<String>,
174 #[arg(long)]
175 jurisdiction: Option<String>,
176 #[arg(long)]
177 tax_id: Option<String>,
178 #[arg(long)]
179 company_no: Option<String>,
180 #[arg(long)]
181 address: Option<String>,
182 #[arg(long)]
183 email: Option<String>,
184 #[arg(long)]
185 phone: Option<String>,
186 #[arg(long)]
187 logo: Option<String>,
188 #[arg(long)]
189 logo_clear: bool,
190 #[arg(long)]
191 output_dir: Option<String>,
192 },
193 #[command(visible_alias = "ls")]
194 List,
195 #[command(visible_alias = "get")]
196 Show { slug: String },
197 #[command(visible_alias = "rm")]
198 Delete { slug: String },
199}
200
201#[derive(Subcommand, Debug)]
204pub enum ClientCmd {
205 #[command(visible_alias = "new")]
206 Add {
207 slug: String,
208 #[arg(long)]
209 name: String,
210 #[arg(long)]
212 legal_name: Option<String>,
213 #[arg(long)]
215 company_no: Option<String>,
216 #[arg(long)]
218 jurisdiction: Option<String>,
219 #[arg(long)]
220 attn: Option<String>,
221 #[arg(long)]
222 country: Option<String>,
223 #[arg(long)]
224 address: String,
225 #[arg(long)]
226 email: Option<String>,
227 #[arg(long)]
228 notes: Option<String>,
229 },
230 Edit {
231 slug: String,
232 #[arg(long)]
233 name: Option<String>,
234 #[arg(long)]
235 legal_name: Option<String>,
236 #[arg(long)]
237 company_no: Option<String>,
238 #[arg(long)]
239 jurisdiction: Option<String>,
240 #[arg(long)]
241 attn: Option<String>,
242 #[arg(long)]
243 country: Option<String>,
244 #[arg(long)]
245 address: Option<String>,
246 #[arg(long)]
247 email: Option<String>,
248 #[arg(long)]
249 notes: Option<String>,
250 },
251 #[command(visible_alias = "ls")]
252 List,
253 #[command(visible_alias = "get")]
254 Show { slug: String },
255 #[command(visible_alias = "rm")]
256 Delete { slug: String },
257}
258
259#[derive(clap::Args, Debug, Clone)]
262pub struct ContractNewArgs {
263 #[arg(long)]
265 pub kind: String,
266 #[arg(long = "as")]
268 pub r#as: Option<String>,
269 #[arg(long)]
271 pub client: String,
272 #[arg(long)]
274 pub title: Option<String>,
275 #[arg(long)]
277 pub effective: Option<String>,
278 #[arg(long, conflicts_with = "term_months")]
280 pub end: Option<String>,
281 #[arg(long)]
283 pub term_months: Option<i64>,
284 #[arg(long, conflicts_with_all = ["end", "term_months"])]
286 pub term_years: Option<i64>,
287 #[arg(long)]
289 pub governing_law: Option<String>,
290 #[arg(long)]
292 pub venue: Option<String>,
293 #[arg(long)]
297 pub fee: Option<String>,
298 #[arg(long)]
300 pub fee_schedule: Option<String>,
301 #[arg(long)]
303 pub mutuality: Option<String>,
304 #[arg(long)]
306 pub disclosing_side: Option<String>,
307 #[arg(long)]
309 pub purpose: Option<String>,
310 #[arg(long = "deliverable")]
312 pub deliverables: Vec<String>,
313 #[arg(long)]
315 pub ip_assignment: Option<String>,
316 #[arg(long)]
318 pub termination_notice_days: Option<i64>,
319 #[arg(long = "term")]
322 pub terms: Vec<String>,
323 #[arg(long)]
325 pub pack: Option<String>,
326 #[arg(long = "include")]
328 pub include: Vec<String>,
329 #[arg(long = "exclude")]
331 pub exclude: Vec<String>,
332 #[arg(long)]
334 pub notes: Option<String>,
335 #[arg(long)]
337 pub template: Option<String>,
338}
339
340#[derive(clap::Args, Debug, Clone)]
341pub struct ContractListArgs {
342 #[arg(long)]
343 pub kind: Option<String>,
344 #[arg(long)]
345 pub status: Option<String>,
346 #[arg(long = "as")]
347 pub issuer: Option<String>,
348}
349
350#[derive(clap::Args, Debug, Clone)]
351pub struct ContractRenderArgs {
352 pub number: String,
353 #[arg(long)]
355 pub template: Option<String>,
356 #[arg(long, short)]
358 pub out: Option<String>,
359 #[arg(long)]
361 pub open: bool,
362 #[arg(long)]
364 pub draft: bool,
365 #[arg(long = "final", conflicts_with = "draft")]
368 pub final_render: bool,
369}
370
371#[derive(clap::Args, Debug, Clone)]
372pub struct SignArgs {
373 pub number: String,
374 #[arg(long)]
376 pub side: String,
377 #[arg(long)]
378 pub name: String,
379 #[arg(long)]
380 pub title: Option<String>,
381 #[arg(long)]
383 pub date: Option<String>,
384 #[arg(long)]
386 pub force: bool,
387}
388
389#[derive(clap::Args, Debug, Clone)]
390pub struct ContractEditArgs {
391 pub number: String,
392 #[arg(long)]
393 pub client: Option<String>,
394 #[arg(long)]
395 pub title: Option<String>,
396 #[arg(long)]
397 pub effective: Option<String>,
398 #[arg(long, conflicts_with = "term_months")]
400 pub end: Option<String>,
401 #[arg(long)]
402 pub term_months: Option<i64>,
403 #[arg(long)]
404 pub governing_law: Option<String>,
405 #[arg(long)]
406 pub venue: Option<String>,
407 #[arg(long)]
408 pub fee: Option<String>,
409 #[arg(long)]
410 pub fee_schedule: Option<String>,
411 #[arg(long = "term")]
413 pub terms: Vec<String>,
414 #[arg(long)]
415 pub notes: Option<String>,
416 #[arg(long)]
417 pub template: Option<String>,
418}
419
420#[derive(clap::Args, Debug, Clone)]
421pub struct DuplicateArgs {
422 pub number: String,
423 #[arg(long)]
424 pub client: Option<String>,
425 #[arg(long = "as")]
426 pub r#as: Option<String>,
427}
428
429#[derive(clap::Args, Debug, Clone)]
430pub struct DeleteArgs {
431 pub number: String,
432 #[arg(long)]
434 pub force: bool,
435}
436
437#[derive(Subcommand, Debug)]
438pub enum ContractCmd {
439 #[command(visible_alias = "create")]
441 New(ContractNewArgs),
442 #[command(visible_alias = "ls")]
444 List(ContractListArgs),
445 #[command(visible_alias = "get")]
447 Show { number: String },
448 Edit(ContractEditArgs),
450 Render(ContractRenderArgs),
452 Mark { number: String, status: String },
454 Sign(SignArgs),
456 #[command(subcommand)]
458 Clauses(ClauseCmd),
459 #[command(visible_alias = "clone")]
461 Duplicate(DuplicateArgs),
462 #[command(visible_alias = "rm")]
464 Delete(DeleteArgs),
465}
466
467#[derive(Subcommand, Debug)]
468pub enum ClauseCmd {
469 #[command(visible_alias = "ls")]
471 List { number: String },
472 Add {
474 number: String,
475 slug: String,
476 #[arg(long)]
478 heading: Option<String>,
479 #[arg(long)]
481 body: Option<String>,
482 #[arg(long = "from-file")]
484 from_file: Option<String>,
485 #[arg(long)]
487 position: Option<i64>,
488 },
489 Edit {
491 number: String,
492 slug: String,
493 #[arg(long)]
494 heading: Option<String>,
495 #[arg(long)]
496 body: Option<String>,
497 #[arg(long = "from-file")]
498 from_file: Option<String>,
499 },
500 #[command(visible_alias = "rm")]
502 Remove { number: String, slug: String },
503 Move {
505 number: String,
506 slug: String,
507 position: i64,
508 },
509 Reset { number: String },
511}
512
513#[derive(Subcommand, Debug)]
516pub enum PackCmd {
517 #[command(visible_alias = "ls")]
519 List,
520 #[command(visible_alias = "get")]
522 Show {
523 kind: String,
524 #[arg(long, default_value = "standard")]
525 pack: String,
526 },
527}
528
529#[derive(Subcommand, Debug)]
530pub enum TemplateCmd {
531 #[command(visible_alias = "ls")]
533 List,
534 #[command(visible_alias = "suggest")]
536 Find {
537 #[arg(required = true, num_args = 1..)]
539 query: Vec<String>,
540 },
541 Preview {
543 name: String,
544 #[arg(long, default_value = "consulting")]
546 kind: String,
547 #[arg(long, short)]
548 out: Option<String>,
549 },
550}
551
552#[derive(Subcommand, Debug)]
553pub enum KindsCmd {
554 #[command(visible_alias = "ls")]
556 List,
557 #[command(visible_alias = "suggest")]
559 Find {
560 #[arg(required = true, num_args = 1..)]
562 query: Vec<String>,
563 },
564}
565
566#[derive(Subcommand, Debug)]
567pub enum ConfigCmd {
568 Show,
570 Path,
572 Set { key: String, value: String },
574}
575
576#[derive(Subcommand, Debug)]
577pub enum SkillCmd {
578 Install,
580 Status,
582}