pub mod administration;
pub(crate) mod code;
pub mod commands;
pub mod models;
pub mod requests;
pub mod route;
pub mod session;
pub mod skills;
pub mod status;
#[derive(Debug, thiserror::Error)]
#[error("{0}")]
pub struct ToolError(pub String);
impl ToolError {
pub fn new(message: impl Into<String>) -> Self {
Self(message.into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Effect {
Read,
Write { inverse: &'static str },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Requires {
Nothing,
Binding,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Reach {
Portable,
HostBound,
SessionBound,
}
pub struct ActionSpec {
pub id: &'static str,
pub cli_leaf: Option<&'static str>,
pub tui_command: Option<&'static str>,
pub effect: Effect,
pub requires: Requires,
pub reach: Reach,
}
pub const ACTIONS: &[ActionSpec] = &[
ActionSpec {
id: "status",
cli_leaf: Some("status"),
tui_command: Some("status"),
effect: Effect::Read,
requires: Requires::Nothing,
reach: Reach::Portable,
},
ActionSpec {
id: "list_models",
cli_leaf: Some("models"),
tui_command: Some("models"),
effect: Effect::Read,
requires: Requires::Nothing,
reach: Reach::Portable,
},
ActionSpec {
id: "route",
cli_leaf: Some("route"),
tui_command: Some("preview"),
effect: Effect::Read,
requires: Requires::Nothing,
reach: Reach::HostBound,
},
ActionSpec {
id: "commands",
cli_leaf: Some("acp commands"),
tui_command: Some("commands"),
effect: Effect::Read,
requires: Requires::Nothing,
reach: Reach::SessionBound,
},
ActionSpec {
id: "route_set",
cli_leaf: None,
tui_command: Some("route"),
effect: Effect::Write {
inverse: "route_reset",
},
requires: Requires::Binding,
reach: Reach::SessionBound,
},
ActionSpec {
id: "route_reset",
cli_leaf: None,
tui_command: Some("route reset"),
effect: Effect::Write {
inverse: "route_set",
},
requires: Requires::Binding,
reach: Reach::SessionBound,
},
];
#[cfg(test)]
mod tests {
use super::{ACTIONS, Effect, Reach};
#[test]
fn every_session_action_is_fully_inventoried() {
for action in ACTIONS {
let Some(command) = action.tui_command else {
continue;
};
assert!(
action.cli_leaf.is_some() || action.reach == Reach::SessionBound,
"`{}` is offered as `/{command}` without a CLI leaf, but is not session-bound",
action.id,
);
}
}
#[test]
fn every_write_names_a_reciprocal_inverse() {
for action in ACTIONS {
let Effect::Write { inverse } = action.effect else {
continue;
};
let row = ACTIONS.iter().find(|candidate| candidate.id == inverse);
assert!(
row.is_some(),
"`{}` names missing inverse `{inverse}`",
action.id
);
let inverse_details = row.and_then(|row| match row.effect {
Effect::Write { inverse } => Some((inverse, row.tui_command)),
Effect::Read => None,
});
assert!(
inverse_details.is_some(),
"`{inverse}` is not a write action"
);
let Some((back, inverse_command)) = inverse_details else {
continue;
};
assert_eq!(back, action.id);
assert!(
action.tui_command.is_none() || inverse_command.is_some(),
"a session-visible write must have a session-visible inverse"
);
}
}
}