Skip to main content

invoice_cli/commands/
agent_info.rs

1use crate::error::Result;
2use crate::output::{print_raw, Ctx};
3use crate::tax;
4
5pub fn run(_ctx: Ctx) -> Result<()> {
6    let profiles: Vec<_> = tax::all_profiles()
7        .into_iter()
8        .map(|p| {
9            serde_json::json!({
10                "code": p.code,
11                "country": p.country,
12                "tax_label": p.tax_label,
13                "default_rate": p.default_rate,
14                "currency": p.currency,
15                "symbol": p.symbol,
16                "tax_invoice_title": p.tax_invoice_title,
17                "supports_reverse_charge": p.supports_reverse_charge,
18            })
19        })
20        .collect();
21
22    // Built outside the json! macro to avoid hitting the proc-macro recursion
23    // limit as the command surface grows.
24    let commands_list: &[(&str, &str)] = &[
25        ("issuer add <slug> --name X --jurisdiction sg|uk|us|eu --address ... [--logo PATH]", "Register an issuer (billing entity). --logo points to a PNG/SVG/JPG rendered in template header"),
26        ("issuer edit <slug> [--name ... --template ... --jurisdiction ... --logo PATH etc]", "Update any subset of an issuer's fields (incl. logo path)"),
27        ("issuer set-template <slug> <template>", "Shorthand: change an issuer's default template"),
28        ("issuer list | ls", "List issuers"),
29        ("issuer show <slug> | get", "Show issuer details"),
30        ("issuer delete <slug> | rm", "Delete an issuer"),
31        ("clients add <slug> --name X --address ... [--default-issuer S --default-template T]", "Register a client, optionally pinning a default issuer/template"),
32        ("clients edit <slug> [--name ... --default-issuer ... --default-template ...]", "Update any subset of a client's fields"),
33        ("clients set-issuer <slug> <issuer-slug>", "Shorthand: pin the default issuer for this client"),
34        ("clients set-template <slug> <template>", "Shorthand: pin the preferred template for this client"),
35        ("clients list | ls", "List clients"),
36        ("clients show <slug> | get", "Show client details"),
37        ("clients delete <slug> | rm", "Delete a client"),
38        ("products add <slug> --description X --unit Y --price N --currency SGD", "Register a reusable product/service line"),
39        ("products edit <slug> [--description ... --price ... etc]", "Update any subset of a product's fields"),
40        ("products list | ls", "List products"),
41        ("products show <slug> | get", "Show product details"),
42        ("products delete <slug> | rm", "Delete a product"),
43        ("invoices new [--as <issuer>] --client <client> --item <spec>... [--discount-rate R | --discount-fixed X]", "Create a new invoice (omit --as when client has a default_issuer). Optional invoice-level discount (percent OR fixed major-units)"),
44        ("invoices edit <number> [--client ... --due ... --terms ... --notes ... --currency ... --pay-link ... --reverse-charge ... --discount-rate ... --discount-fixed ...]", "Edit DRAFT invoice metadata only — issued/paid/void invoices are immutable; use credit-note instead"),
45        ("invoices items <number> add <spec> [--subtitle ... --discount-rate ... --discount-fixed ...]", "Add a line item to a DRAFT invoice (spec: 'product-slug[:qty]' OR 'Description:qty:price[:rate]')"),
46        ("invoices items <number> remove <position> | rm", "Remove the line at zero-indexed position from a DRAFT invoice"),
47        ("invoices items <number> edit <position> [--description ... --subtitle ... --qty ... --unit ... --price ... --tax-rate ... --discount-rate ... --discount-fixed ...]", "Edit any subset of a DRAFT invoice line's fields"),
48        ("invoices credit-note <number> [--full | --item <spec>...] [--notes ... --pay-link ...]", "Issue a credit note against an existing invoice. --full clones source items as positive reversal; --item lets you specify exact refund lines"),
49        ("invoices aging [--as <issuer>]", "Ageing report for unpaid invoices, bucketed 0-30 / 31-60 / 61-90 / 90+ days past due"),
50        ("invoices export [--from YYYY-MM-DD --to YYYY-MM-DD --format csv|json --out PATH --as <issuer>]", "Export invoices for accountant handoff. Defaults to CSV on stdout when --out omitted"),
51        ("invoices duplicate <number> [--client C --as I --due 30d]", "Clone an invoice's line items into a new draft (for recurring billing)"),
52        ("invoices list | ls [--status X] [--as Y] [--overdue]", "List invoices (includes total per invoice). --overdue filters to past-due unpaid invoices"),
53        ("invoices show <number> | get", "Show invoice details"),
54        ("invoices render <number> [--template T] [--out PATH] [--open]", "Render to PDF. Template chain: --template > client.default_template > issuer.default_template > 'vienna'"),
55        ("invoices mark <number> draft|issued|paid|void", "Update invoice status (auto-stamps issued_at/paid_at)"),
56        ("invoices delete <number> [--force] | rm", "Delete an invoice. --force allows deleting non-draft (breaks number-sequence integrity — prefer 'mark void' or credit-note)"),
57        ("template list", "List available PDF templates"),
58        ("template preview <name>", "Render a template with synthetic data"),
59        ("config show | path | set <key> <value>", "View / edit config"),
60        ("agent-info | info", "This manifest"),
61        ("doctor", "Diagnose dependencies and config"),
62        ("skill install", "Install embedded Claude/Codex/Gemini skill"),
63        ("update [--check]", "Self-update — queries crates.io, upgrades via brew/cargo"),
64    ];
65    let mut commands = serde_json::Map::new();
66    for (k, v) in commands_list {
67        commands.insert((*k).to_string(), serde_json::Value::String((*v).to_string()));
68    }
69
70    let manifest = serde_json::json!({
71        "name": "invoice",
72        "version": env!("CARGO_PKG_VERSION"),
73        "description": env!("CARGO_PKG_DESCRIPTION"),
74        "commands": commands,
75        "flags": {
76            "--json": "Force JSON envelope output (auto-enabled when piped)",
77            "--quiet": "Suppress human output"
78        },
79        "exit_codes": {
80            "0": "Success",
81            "1": "Transient error (IO, render) — retry may help",
82            "2": "Config error — fix setup",
83            "3": "Bad input / not found / ambiguous — fix arguments",
84            "4": "Rate limited — wait and retry"
85        },
86        "envelope_schema": {
87            "version": "1",
88            "status": "success | error",
89            "data": "… (success)",
90            "error": "{ code, message, suggestion } (error)"
91        },
92        "config_path": "~/.config/invoice/config.toml",
93        "state_dir": "~/.local/share/invoice/",
94        "database": "~/.local/share/invoice/invoice.db",
95        "templates": ["helvetica-nera", "tiefletter-gold", "monoline", "vienna", "boutique"],
96        "tax_profiles": profiles,
97        "item_spec": "product-slug[:qty]  OR  description:qty:price[:rate]"
98    });
99
100    print_raw(&manifest);
101    Ok(())
102}