Skip to main content

contract_cli/commands/
mod.rs

1use crate::cli::{Cli, Commands, ContractCmd};
2use crate::error::{AppError, Result};
3use crate::output::Ctx;
4
5pub mod agent_info;
6pub mod clauses;
7pub mod clients;
8pub mod config;
9pub mod contracts;
10pub mod doctor;
11pub mod issuers;
12pub mod kinds;
13pub mod pack;
14pub mod skill;
15pub mod template;
16pub mod update;
17
18pub fn dispatch(cli: Cli, ctx: Ctx) -> Result<()> {
19    crate::config::ensure_dirs()?;
20
21    match cli.command {
22        Commands::Issuer(cmd) => issuers::run(cmd, ctx),
23        Commands::Clients(cmd) => clients::run(cmd, ctx),
24        Commands::Contracts(cmd) => contracts::run(cmd, ctx),
25        // Top-level shortcuts (delegate to the contracts handler)
26        Commands::New(args) => contracts::run(ContractCmd::New(args), ctx),
27        Commands::List(args) => contracts::run(ContractCmd::List(args), ctx),
28        Commands::Show { number } => contracts::run(ContractCmd::Show { number }, ctx),
29        Commands::Render(args) => contracts::run(ContractCmd::Render(args), ctx),
30        Commands::Mark { number, status } => {
31            contracts::run(ContractCmd::Mark { number, status }, ctx)
32        }
33        Commands::Sign(args) => contracts::run(ContractCmd::Sign(args), ctx),
34        Commands::Edit(args) => contracts::run(ContractCmd::Edit(args), ctx),
35        Commands::Duplicate(args) => contracts::run(ContractCmd::Duplicate(args), ctx),
36        Commands::Delete(args) => contracts::run(ContractCmd::Delete(args), ctx),
37        Commands::Pack(cmd) => pack::run(cmd, ctx),
38        Commands::Template(cmd) => template::run(cmd, ctx),
39        Commands::Kinds(cmd) => kinds::run(cmd, ctx),
40        Commands::Config(cmd) => config::run(cmd, ctx),
41        Commands::AgentInfo => agent_info::run(ctx),
42        Commands::Skill(cmd) => skill::run(cmd, ctx),
43        Commands::Doctor => doctor::run(ctx),
44        Commands::Update { check } => update::run(ctx, check),
45        Commands::ExitHook { code } => exit_hook(code, ctx),
46    }
47}
48
49/// Hidden conformance hook: deterministically trigger each exit code so the
50/// test suite and conformance.sh can verify the full 0-4 contract.
51fn exit_hook(code: i32, ctx: Ctx) -> Result<()> {
52    match code {
53        0 => {
54            let data = serde_json::json!({ "contract": true, "exit_code": 0 });
55            crate::output::print_success(ctx, &data, |_| println!("contract: success"));
56            Ok(())
57        }
58        1 => Err(AppError::Transient("contract: transient error".into())),
59        2 => Err(AppError::Config("contract: config error".into())),
60        3 => Err(AppError::InvalidInput("contract: bad input".into())),
61        4 => Err(AppError::RateLimited("contract: rate limited".into())),
62        other => Err(AppError::InvalidInput(format!(
63            "unknown contract code: {other}. Valid: 0-4"
64        ))),
65    }
66}
67
68pub(crate) fn split_multiline_arg(value: &str) -> Vec<String> {
69    let normalized = value.replace("\\n", "\n");
70    normalized.split('\n').map(|s| s.to_string()).collect()
71}