1use crate::error::Result;
2use crate::output::{print_raw, Ctx};
3
4pub fn run(_ctx: Ctx) -> Result<()> {
5 let config_path = crate::config::config_path()?.display().to_string();
6 let state_dir = crate::config::state_path()?.display().to_string();
7 let database = crate::config::db_path()?.display().to_string();
8
9 let commands: &[(&str, &str)] = &[
10 ("issuer add <slug> --name X --jurisdiction sg|uk|us|eu --address ...", "Register an issuer (your side of the contract). Shared with invoice-cli."),
11 ("issuer edit <slug> [--name --legal-name --logo --output-dir ...]", "Update issuer fields."),
12 ("issuer list | show <slug> | delete <slug>", "Manage issuers."),
13 ("clients add <slug> --name X --address ... [--legal-name --company-no --jurisdiction]", "Register a counterparty. Legal fields are used by contract party blocks."),
14 ("clients edit <slug> [...]", "Update client fields."),
15 ("clients list | show <slug> | delete <slug>", "Manage clients."),
16 ("new --kind nda|consulting|msa|sow|service --as <issuer> --client <client> [options...]", "Create a new contract from a clause pack."),
17 ("list [--kind X --status Y --as Z]", "List contracts."),
18 ("show <number>", "Show one contract + its clause list."),
19 ("contracts edit <number> [--title --effective --end --term-months ...]", "Edit DRAFT contract metadata (sent/signed are immutable)."),
20 ("render <number> [--template T --out PATH --open --draft | --final]", "Render to PDF. DRAFT watermark is implicit unless status is signed/active or --final is passed."),
21 ("mark <number> draft|sent|signed|active|expired|terminated", "Update status; auto-stamps sent_at / signed_at / terminated_at."),
22 ("sign <number> --side us|them --name 'Full Name' [--title T --date YYYY-MM-DD]", "Record one party's signature. When both sides sign, status auto-bumps to 'signed'."),
23 ("contracts clauses list <number>", "Show clauses currently attached to a contract."),
24 ("contracts clauses add <number> <slug> [--heading --body --from-file --position]", "Add a clause from the pack, or a custom one via --body / --from-file."),
25 ("contracts clauses edit <number> <slug> [--heading --body --from-file]", "Override heading / body of an attached clause."),
26 ("contracts clauses remove <number> <slug>", "Remove a clause."),
27 ("contracts clauses move <number> <slug> <position>", "Re-order a clause."),
28 ("contracts clauses reset <number>", "Reset to pack default."),
29 ("pack list", "List available kind/pack combinations."),
30 ("pack show <kind> [--pack standard]", "Show available clauses for a kind+pack."),
31 ("template list | preview <name> [--kind nda|consulting]", "List or preview templates."),
32 ("contracts duplicate <number> [--client C --as I]", "Clone a contract as a fresh draft."),
33 ("contracts delete <number> [--force]", "Delete a contract. --force allows non-draft."),
34 ("config show | path | set <key> <value>", "View / edit shared accounting config."),
35 ("agent-info | info", "This manifest."),
36 ("doctor", "Diagnose dependencies and DB."),
37 ("skill install", "Install embedded Claude/Codex/Gemini skill."),
38 ("update [--check]", "Self-update."),
39 ];
40 let mut commands_map = serde_json::Map::new();
41 for (k, v) in commands {
42 commands_map.insert(
43 (*k).into(),
44 serde_json::Value::String((*v).into()),
45 );
46 }
47 let templates = crate::typst_assets::list_templates().unwrap_or_default();
48 let packs = crate::clauses::list_packs();
49
50 let manifest = serde_json::json!({
51 "name": "contract",
52 "version": env!("CARGO_PKG_VERSION"),
53 "description": env!("CARGO_PKG_DESCRIPTION"),
54 "commands": commands_map,
55 "flags": {
56 "--json": "Force JSON envelope output (auto when piped)",
57 "--quiet": "Suppress human output",
58 },
59 "exit_codes": {
60 "0": "Success",
61 "1": "Transient error (IO, render)",
62 "2": "Config error",
63 "3": "Bad input / not found / ambiguous",
64 },
65 "envelope_schema": {
66 "version": "1",
67 "status": "success | error",
68 "data": "… (success)",
69 "error": "{ code, message, suggestion } (error)",
70 },
71 "config_path": config_path,
72 "state_dir": state_dir,
73 "database": database,
74 "templates": templates,
75 "packs": packs.into_iter().map(|(k, p)| serde_json::json!({"kind": k, "pack": p})).collect::<Vec<_>>(),
76 "kinds": ["consulting", "nda", "msa", "sow", "service"],
77 "fee_spec": "type:amount:currency — e.g. fixed:8400:SGD | hourly:200:SGD | daily:1500:SGD | retainer:5000:SGD",
78 "first_run": [
79 "contract doctor --json",
80 "contract issuer list --json # CHECK FIRST — shared with invoice-cli",
81 "contract clients list --json # CHECK FIRST — shared with invoice-cli",
82 "# Only if a needed issuer/client is missing, then:",
83 "contract issuer add <slug> --name <display-name> --jurisdiction sg|uk|us|eu --address \"line1\\nline2\"",
84 "contract clients add <slug> --name <client-name> --legal-name <legal> --address \"line1\\nline2\"",
85 "contract new --kind nda --as <issuer> --client <client> --purpose 'description'",
86 "contract render <number> --open",
87 ],
88 "shared_state": {
89 "database": database.clone(),
90 "shared_with": ["invoice-cli (binary: invoice)"],
91 "shared_tables": ["issuers", "clients", "number_series"],
92 "discovery_workflow": "Before creating a new issuer or client, ALWAYS run `contract issuer list --json` and `contract clients list --json` (or the invoice-cli equivalents). The accounting suite's whole point is one source of truth — duplicating entities here pollutes invoicing too. If the entity exists under a different slug, prefer using the existing slug over creating a new one.",
93 "legal_fields_on_clients": "Three columns added in V7 are contract-specific: `legal_name`, `company_no`, `legal_jurisdiction`. If an existing client (from invoice-cli) is missing them, fill via: contract clients edit <slug> --legal-name X --company-no Y --jurisdiction Z."
94 },
95 "examples": [
96 { "goal": "Quick mutual NDA",
97 "command": "contract new --kind nda --as acme --client meridian --purpose 'evaluation of a joint product' --term-years 3" },
98 { "goal": "Consulting agreement with fixed fee",
99 "command": "contract new --kind consulting --as acme --client meridian --purpose 'design a dashboard' --fee fixed:8400:SGD --term-months 3 --deliverable 'Design' --deliverable 'Build'" },
100 { "goal": "Render with no watermark",
101 "command": "contract render NDA-acme-2026-0001 --final --open" },
102 { "goal": "Record both signatures",
103 "command": "contract sign NDA-acme-2026-0001 --side us --name 'B. Djordjevic' --title CEO; contract sign NDA-acme-2026-0001 --side them --name 'Sophie Lin' --title 'Head of Marketing'" },
104 ],
105 "guardrails": [
106 "BEFORE creating any issuer or client, run `contract issuer list` and `contract clients list` — the DB is shared with invoice-cli; duplicates pollute both tools.",
107 "Run doctor before first use.",
108 "Use --json for agents; stdout is data, stderr is diagnostics.",
109 "Drafts render with a DRAFT watermark by default. Use --final to suppress before sending out for signature.",
110 "Once a contract is sent/signed, only the status / signature columns can change.",
111 "These clauses are practical plain-English starting points, not legal advice. Have a lawyer review for material engagements.",
112 ],
113 });
114 print_raw(&manifest);
115 Ok(())
116}