cellstate/entity/
mod.rs

1//! Entity command module — CLI subcommands built from the entity registry.
2//!
3//! Uses clap's builder API (runtime) to construct entity commands from
4//! `ENTITY_REGISTRY` metadata, while infra commands (setup, init, start, etc.)
5//! use clap's derive API (compile-time).
6
7pub mod agent;
8pub mod api_key;
9pub mod artifact;
10pub mod batch;
11pub mod client;
12pub mod config;
13pub mod context;
14pub mod delegation;
15pub mod deployment;
16pub mod edge;
17pub mod event_dag;
18pub mod gates;
19pub mod handoff;
20pub mod lock;
21pub mod message;
22pub mod models;
23pub mod note;
24pub mod output;
25pub mod scope;
26pub mod search;
27pub mod summarization_policy;
28pub mod summarization_request;
29pub mod team;
30pub mod tenant;
31pub mod trajectory;
32pub mod turn;
33pub mod working_set;
34
35use clap::{ArgMatches, Command};
36
37use crate::session::CliSession;
38
39// ── Session-aware arg resolution ──────────────────────────────────────────
40
41/// Resolve an optional CLI arg with session fallback.
42///
43/// Returns the explicit flag value if provided, otherwise the session value.
44/// If neither is set, returns `None`.
45pub fn resolve_arg<'a>(
46    matches: &'a ArgMatches,
47    arg_name: &str,
48    session_value: Option<&'a str>,
49) -> Option<&'a str> {
50    matches
51        .get_one::<String>(arg_name)
52        .map(|s| s.as_str())
53        .or(session_value)
54}
55
56/// Like `resolve_arg` but returns a helpful error if nothing is set.
57pub fn require_arg<'a>(
58    matches: &'a ArgMatches,
59    arg_name: &str,
60    session_value: Option<&'a str>,
61    session_hint: &str,
62) -> anyhow::Result<&'a str> {
63    resolve_arg(matches, arg_name, session_value).ok_or_else(|| {
64        anyhow::anyhow!(
65            "{arg_name} is required — pass --{arg_name} or set it with `cellstate use {session_hint} <id>`"
66        )
67    })
68}
69
70/// Build all entity subcommands.
71pub fn build_entity_commands() -> Vec<Command> {
72    vec![
73        trajectory::build_command(),
74        agent::build_command(),
75        scope::build_command(),
76        artifact::build_command(),
77        note::build_command(),
78        turn::build_command(),
79        context::build_command(),
80        lock::build_command(),
81        message::build_command(),
82        delegation::build_command(),
83        handoff::build_command(),
84        edge::build_command(),
85        team::build_command(),
86        tenant::build_command(),
87        api_key::build_command(),
88        event_dag::build_command(),
89        search::build_command(),
90        batch::build_command(),
91        summarization_policy::build_command(),
92        summarization_request::build_command(),
93        deployment::build_command(),
94        working_set::build_command(),
95        config::build_command(),
96        gates::build_command(),
97        models::build_command(),
98    ]
99}
100
101/// Dispatch an entity subcommand by name.
102pub async fn dispatch(
103    entity_name: &str,
104    matches: &clap::ArgMatches,
105    client: &client::ApiClient,
106    output: &output::OutputConfig,
107    session: &CliSession,
108) -> anyhow::Result<()> {
109    match entity_name {
110        "trajectory" => trajectory::dispatch(matches, client, output, session).await,
111        "agent" => agent::dispatch(matches, client, output, session).await,
112        "scope" => scope::dispatch(matches, client, output, session).await,
113        "artifact" => artifact::dispatch(matches, client, output, session).await,
114        "note" => note::dispatch(matches, client, output, session).await,
115        "turn" => turn::dispatch(matches, client, output, session).await,
116        "context" => context::dispatch(matches, client, output, session).await,
117        "lock" => lock::dispatch(matches, client, output, session).await,
118        "message" => message::dispatch(matches, client, output, session).await,
119        "delegation" => delegation::dispatch(matches, client, output, session).await,
120        "handoff" => handoff::dispatch(matches, client, output, session).await,
121        "edge" => edge::dispatch(matches, client, output, session).await,
122        "team" => team::dispatch(matches, client, output, session).await,
123        "tenant" => tenant::dispatch(matches, client, output, session).await,
124        "api-key" => api_key::dispatch(matches, client, output, session).await,
125        "event-dag" => event_dag::dispatch(matches, client, output, session).await,
126        "search" => search::dispatch(matches, client, output, session).await,
127        "batch" => batch::dispatch(matches, client, output, session).await,
128        "summarization-policy" => {
129            summarization_policy::dispatch(matches, client, output, session).await
130        }
131        "summarization-request" => {
132            summarization_request::dispatch(matches, client, output, session).await
133        }
134        "deployment" => deployment::dispatch(matches, client, output, session).await,
135        "working-set" => working_set::dispatch(matches, client, output, session).await,
136        "config" => config::dispatch(matches, client, output, session).await,
137        "gates" => gates::dispatch(matches, client, output, session).await,
138        "models" => models::dispatch(matches, client, output, session).await,
139        _ => anyhow::bail!("Unknown entity: {entity_name}"),
140    }
141}