use clap::{Parser, Subcommand};
const HELP_FOOTER: &str = "\
Tips:
• Run `contract agent-info | jq` for the full capability manifest (commands, flags, exit codes)
• The DB is SHARED with invoice-cli — always `contract issuer list` / `contract clients list` before creating entities
• Pipe any command to jq for structured data: `contract list | jq '.data'`
• Template chain at render: --template > contract.default_template > \"helvetica-nera\"
• Drafts render with a DRAFT watermark; use --final for a clean signing copy
• `contract doctor` verifies typst, the DB, packs, and templates before you start
Examples:
contract new --kind nda --as acme --client meridian --purpose 'evaluating a joint venture' --term-years 3
Quick mutual NDA with a 3-year confidentiality term
contract new --kind consulting --as acme --client meridian --fee fixed:8400:SGD --term-months 3 \\
--purpose 'design a dashboard' --deliverable 'Designs' --deliverable 'Build' --ip-assignment client
Consulting agreement with a fixed fee and deliverables
contract render NDA-acme-2026-0001 --final --open
Render a clean, watermark-free PDF and open it
contract sign NDA-acme-2026-0001 --side us --name 'B. Djordjevic' --title Director
Record one side's signature (status auto-bumps to 'signed' when both sides sign)
contract template list | jq '.data'
Inspect available PDF templates as structured JSON";
#[derive(Parser, Debug)]
#[command(
name = "contract",
version,
about = "Beautiful contracts from the CLI — NDA, consulting, MSA, SOW, service",
after_long_help = HELP_FOOTER,
after_help = HELP_FOOTER
)]
pub struct Cli {
#[arg(long, global = true)]
pub json: bool,
#[arg(long, global = true)]
pub quiet: bool,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
#[command(visible_alias = "issuers", subcommand)]
Issuer(IssuerCmd),
#[command(visible_alias = "client", subcommand)]
Clients(ClientCmd),
#[command(subcommand)]
Contracts(ContractCmd),
#[command(name = "new")]
New(ContractNewArgs),
#[command(name = "list", alias = "ls")]
List(ContractListArgs),
#[command(name = "show", alias = "get")]
Show { number: String },
#[command(name = "render")]
Render(ContractRenderArgs),
#[command(name = "mark")]
Mark { number: String, status: String },
#[command(name = "sign")]
Sign(SignArgs),
#[command(name = "edit")]
Edit(ContractEditArgs),
#[command(name = "duplicate", visible_alias = "clone")]
Duplicate(DuplicateArgs),
#[command(name = "delete", visible_alias = "rm")]
Delete(DeleteArgs),
#[command(visible_alias = "packs", subcommand)]
Pack(PackCmd),
#[command(visible_alias = "templates", subcommand)]
Template(TemplateCmd),
#[command(visible_alias = "kind", subcommand)]
Kinds(KindsCmd),
#[command(subcommand)]
Config(ConfigCmd),
#[command(visible_alias = "info")]
AgentInfo,
#[command(subcommand)]
Skill(SkillCmd),
Doctor,
Update {
#[arg(long)]
check: bool,
},
#[command(name = "contract", hide = true)]
ExitHook { code: i32 },
}
#[derive(Subcommand, Debug)]
pub enum IssuerCmd {
#[command(visible_alias = "new")]
Add {
slug: String,
#[arg(long)]
name: String,
#[arg(long)]
legal_name: Option<String>,
#[arg(long, default_value = "sg")]
jurisdiction: String,
#[arg(long)]
tax_id: Option<String>,
#[arg(long)]
company_no: Option<String>,
#[arg(long)]
address: String,
#[arg(long)]
email: Option<String>,
#[arg(long)]
phone: Option<String>,
#[arg(long)]
logo: Option<String>,
#[arg(long)]
output_dir: Option<String>,
},
Edit {
slug: String,
#[arg(long)]
name: Option<String>,
#[arg(long)]
legal_name: Option<String>,
#[arg(long)]
jurisdiction: Option<String>,
#[arg(long)]
tax_id: Option<String>,
#[arg(long)]
company_no: Option<String>,
#[arg(long)]
address: Option<String>,
#[arg(long)]
email: Option<String>,
#[arg(long)]
phone: Option<String>,
#[arg(long)]
logo: Option<String>,
#[arg(long)]
logo_clear: bool,
#[arg(long)]
output_dir: Option<String>,
},
#[command(visible_alias = "ls")]
List,
#[command(visible_alias = "get")]
Show { slug: String },
#[command(visible_alias = "rm")]
Delete { slug: String },
}
#[derive(Subcommand, Debug)]
pub enum ClientCmd {
#[command(visible_alias = "new")]
Add {
slug: String,
#[arg(long)]
name: String,
#[arg(long)]
legal_name: Option<String>,
#[arg(long)]
company_no: Option<String>,
#[arg(long)]
jurisdiction: Option<String>,
#[arg(long)]
attn: Option<String>,
#[arg(long)]
country: Option<String>,
#[arg(long)]
address: String,
#[arg(long)]
email: Option<String>,
#[arg(long)]
notes: Option<String>,
},
Edit {
slug: String,
#[arg(long)]
name: Option<String>,
#[arg(long)]
legal_name: Option<String>,
#[arg(long)]
company_no: Option<String>,
#[arg(long)]
jurisdiction: Option<String>,
#[arg(long)]
attn: Option<String>,
#[arg(long)]
country: Option<String>,
#[arg(long)]
address: Option<String>,
#[arg(long)]
email: Option<String>,
#[arg(long)]
notes: Option<String>,
},
#[command(visible_alias = "ls")]
List,
#[command(visible_alias = "get")]
Show { slug: String },
#[command(visible_alias = "rm")]
Delete { slug: String },
}
#[derive(clap::Args, Debug, Clone)]
pub struct ContractNewArgs {
#[arg(long)]
pub kind: String,
#[arg(long = "as")]
pub r#as: Option<String>,
#[arg(long)]
pub client: String,
#[arg(long)]
pub title: Option<String>,
#[arg(long)]
pub effective: Option<String>,
#[arg(long, conflicts_with = "term_months")]
pub end: Option<String>,
#[arg(long)]
pub term_months: Option<i64>,
#[arg(long, conflicts_with_all = ["end", "term_months"])]
pub term_years: Option<i64>,
#[arg(long)]
pub governing_law: Option<String>,
#[arg(long)]
pub venue: Option<String>,
#[arg(long)]
pub fee: Option<String>,
#[arg(long)]
pub fee_schedule: Option<String>,
#[arg(long)]
pub mutuality: Option<String>,
#[arg(long)]
pub disclosing_side: Option<String>,
#[arg(long)]
pub purpose: Option<String>,
#[arg(long = "deliverable")]
pub deliverables: Vec<String>,
#[arg(long)]
pub ip_assignment: Option<String>,
#[arg(long)]
pub termination_notice_days: Option<i64>,
#[arg(long = "term")]
pub terms: Vec<String>,
#[arg(long)]
pub pack: Option<String>,
#[arg(long = "include")]
pub include: Vec<String>,
#[arg(long = "exclude")]
pub exclude: Vec<String>,
#[arg(long)]
pub notes: Option<String>,
#[arg(long)]
pub template: Option<String>,
}
#[derive(clap::Args, Debug, Clone)]
pub struct ContractListArgs {
#[arg(long)]
pub kind: Option<String>,
#[arg(long)]
pub status: Option<String>,
#[arg(long = "as")]
pub issuer: Option<String>,
}
#[derive(clap::Args, Debug, Clone)]
pub struct ContractRenderArgs {
pub number: String,
#[arg(long)]
pub template: Option<String>,
#[arg(long, short)]
pub out: Option<String>,
#[arg(long)]
pub open: bool,
#[arg(long)]
pub draft: bool,
#[arg(long = "final", conflicts_with = "draft")]
pub final_render: bool,
}
#[derive(clap::Args, Debug, Clone)]
pub struct SignArgs {
pub number: String,
#[arg(long)]
pub side: String,
#[arg(long)]
pub name: String,
#[arg(long)]
pub title: Option<String>,
#[arg(long)]
pub date: Option<String>,
#[arg(long)]
pub force: bool,
}
#[derive(clap::Args, Debug, Clone)]
pub struct ContractEditArgs {
pub number: String,
#[arg(long)]
pub client: Option<String>,
#[arg(long)]
pub title: Option<String>,
#[arg(long)]
pub effective: Option<String>,
#[arg(long, conflicts_with = "term_months")]
pub end: Option<String>,
#[arg(long)]
pub term_months: Option<i64>,
#[arg(long)]
pub governing_law: Option<String>,
#[arg(long)]
pub venue: Option<String>,
#[arg(long)]
pub fee: Option<String>,
#[arg(long)]
pub fee_schedule: Option<String>,
#[arg(long = "term")]
pub terms: Vec<String>,
#[arg(long)]
pub notes: Option<String>,
#[arg(long)]
pub template: Option<String>,
}
#[derive(clap::Args, Debug, Clone)]
pub struct DuplicateArgs {
pub number: String,
#[arg(long)]
pub client: Option<String>,
#[arg(long = "as")]
pub r#as: Option<String>,
}
#[derive(clap::Args, Debug, Clone)]
pub struct DeleteArgs {
pub number: String,
#[arg(long)]
pub force: bool,
}
#[derive(Subcommand, Debug)]
pub enum ContractCmd {
#[command(visible_alias = "create")]
New(ContractNewArgs),
#[command(visible_alias = "ls")]
List(ContractListArgs),
#[command(visible_alias = "get")]
Show { number: String },
Edit(ContractEditArgs),
Render(ContractRenderArgs),
Mark { number: String, status: String },
Sign(SignArgs),
#[command(subcommand)]
Clauses(ClauseCmd),
#[command(visible_alias = "clone")]
Duplicate(DuplicateArgs),
#[command(visible_alias = "rm")]
Delete(DeleteArgs),
}
#[derive(Subcommand, Debug)]
pub enum ClauseCmd {
#[command(visible_alias = "ls")]
List { number: String },
Add {
number: String,
slug: String,
#[arg(long)]
heading: Option<String>,
#[arg(long)]
body: Option<String>,
#[arg(long = "from-file")]
from_file: Option<String>,
#[arg(long)]
position: Option<i64>,
},
Edit {
number: String,
slug: String,
#[arg(long)]
heading: Option<String>,
#[arg(long)]
body: Option<String>,
#[arg(long = "from-file")]
from_file: Option<String>,
},
#[command(visible_alias = "rm")]
Remove { number: String, slug: String },
Move {
number: String,
slug: String,
position: i64,
},
Reset { number: String },
}
#[derive(Subcommand, Debug)]
pub enum PackCmd {
#[command(visible_alias = "ls")]
List,
#[command(visible_alias = "get")]
Show {
kind: String,
#[arg(long, default_value = "standard")]
pack: String,
},
}
#[derive(Subcommand, Debug)]
pub enum TemplateCmd {
#[command(visible_alias = "ls")]
List,
#[command(visible_alias = "suggest")]
Find {
#[arg(required = true, num_args = 1..)]
query: Vec<String>,
},
Preview {
name: String,
#[arg(long, default_value = "consulting")]
kind: String,
#[arg(long, short)]
out: Option<String>,
},
}
#[derive(Subcommand, Debug)]
pub enum KindsCmd {
#[command(visible_alias = "ls")]
List,
#[command(visible_alias = "suggest")]
Find {
#[arg(required = true, num_args = 1..)]
query: Vec<String>,
},
}
#[derive(Subcommand, Debug)]
pub enum ConfigCmd {
Show,
Path,
Set { key: String, value: String },
}
#[derive(Subcommand, Debug)]
pub enum SkillCmd {
Install,
Status,
}