use crate::boundary::{Action, Gesture, Query};
use crate::start::{BallSpec, Payload};
pub fn spell(gesture: &Gesture) -> String {
match gesture {
Gesture::Act(action) => spell_action(action),
Gesture::Ask(query) => spell_query(query),
}
}
fn spell_action(action: &Action) -> String {
match action {
Action::Message { content, .. } => format!("/message {content}"),
Action::Interrupt { content, .. } => format!("/interrupt {content}"),
Action::Stop { children, .. } => match children {
true => "/stop children".to_owned(),
false => "/stop".to_owned(),
},
Action::Scan { .. } => "/scan".to_owned(),
Action::Nudge { .. } => "/nudge".to_owned(),
Action::Retarget { .. } => "/retarget".to_owned(),
Action::Close { id, .. } => format!("/close {id}"),
Action::Assign { id, .. } => format!("/assign {id}"),
Action::Release { id, .. } => format!("/release {id}"),
Action::Create { fields, .. } => format!(
"/create {}{}{}",
fields.title,
flag("body", fields.body.as_ref()),
super::balls::spell_fields(&fields.fields)
),
Action::Update { id, fields, .. } => format!(
"/update {id}{}{}{}{}",
flag("title", fields.title.as_ref()),
flag("body", fields.body.as_ref()),
flag("note", fields.note.as_ref()),
super::balls::spell_fields(&fields.fields)
),
Action::Prepare { payload, .. } => spell_payload(payload),
Action::Prompt { goal, .. } => format!("/prompt {goal}"),
Action::Fan(crate::fan::Verb::Spread { n, .. }) => format!("/fan {n}"),
Action::Fan(crate::fan::Verb::Retire { handle, .. }) => format!("/retire {handle}"),
Action::Fan(crate::fan::Verb::Deliver {
handle, summary, ..
}) => {
format!("/deliver {handle} {summary}")
}
Action::DeleteWorkspace { typed, .. } => format!("/delete-workspace {typed}"),
Action::DeleteAgent { typed, .. } if typed.is_empty() => "/delete-agent".to_owned(),
Action::DeleteAgent { typed, .. } => format!("/delete-agent {typed}"),
Action::Monitor(verb) => spell_monitor(verb),
Action::Fleet(verb) => spell_fleet(verb),
Action::AnswerHold { ruling, .. } => format!("/answer {}", ruling.word()),
Action::Floor { raised, .. } => match raised {
true => "/revoke".to_owned(),
false => "/restore".to_owned(),
},
Action::ApplyConfig { file, text } => {
format!("/config {} {text}", super::config::target_words(file))
}
Action::SetMarks { branch, .. } => format!("/marks {branch}"),
Action::PickModel {
role,
provider,
model,
..
} => format!("/model {role} {provider} {model}"),
Action::Fork { attempt, goal, .. } => super::fork::spell(attempt, goal),
Action::Ack => "/ack".to_owned(),
Action::MarkSeen { .. } => "/seen".to_owned(),
Action::ClearTrail => "/clear-trail".to_owned(),
Action::Advertise { tools } => {
format!("/advertise {}", crate::registry::tools::encode(tools))
}
Action::Route(verb) => spell_route(verb),
}
}
fn spell_route(verb: &crate::registry::mailbox::Verb) -> String {
use crate::registry::mailbox::{Verb, capture_value};
match verb {
Verb::Invoke(call) => format!("/invoke {} {} {}", call.client, call.tool, call.input),
Verb::Complete(done) => format!(
"/complete {} {}",
done.invocation,
capture_value(&done.capture)
),
}
}
fn spell_monitor(verb: &crate::monitor::Verb) -> String {
use crate::monitor::Verb;
match verb {
Verb::Arm { model, .. } => format!("/arm {model}"),
Verb::Disarm { .. } => "/disarm".to_owned(),
Verb::Flag { reason, .. } => format!("/flag {reason}"),
}
}
fn spell_fleet(verb: &crate::fleet::Verb) -> String {
use crate::fleet::Verb;
match verb {
Verb::Arm { cap, .. } => format!("/{} {cap}", crate::boundary::codec::FLEET_ARM),
Verb::Disarm { .. } => format!("/{}", crate::boundary::codec::FLEET_DISARM),
}
}
fn spell_query(query: &Query) -> String {
match query {
Query::Workspaces => "/workspaces".to_owned(),
Query::Conversations { .. } => "/conversations".to_owned(),
Query::Balls => "/balls".to_owned(),
Query::WorkspaceBalls { .. } => "/workspace-balls".to_owned(),
Query::WorkDiff { file, .. } => match file {
Some(file) => match &file.handle {
Some(handle) => format!("/work-diff {} {handle} {}", file.ball, file.path),
None => format!("/work-diff {} {}", file.ball, file.path),
},
None => "/work-diff".to_owned(),
},
Query::Science { .. } => "/science".to_owned(),
Query::Transcript { .. } => "/transcript".to_owned(),
Query::Follow { .. } => "/follow".to_owned(),
Query::Steps { .. } => "/steps".to_owned(),
Query::Step { seq, .. } => format!("/step {seq}"),
Query::Files { path, at, .. } => {
let pinned = at
.as_ref()
.map(|c| format!(" --at {c}"))
.unwrap_or_default();
match path {
Some(path) => format!("/files {path}{pinned}"),
None => format!("/files{pinned}"),
}
}
Query::Governing { at, .. } => match at {
Some(commit) => format!("/governing --at {commit}"),
None => "/governing".to_owned(),
},
Query::Rail { .. } => "/rail".to_owned(),
Query::Agent { .. } => "/agent".to_owned(),
Query::Inbox { .. } => "/inbox".to_owned(),
Query::Board => "/board".to_owned(),
Query::Attention => "/attention".to_owned(),
Query::Ops { max } => format!("/ops {max}"),
Query::Search { text } => format!("/search {text}"),
Query::Help { verb } => match verb {
Some(verb) => format!("/help {verb}"),
None => "/help".to_owned(),
},
Query::ReadConfig { file } => format!("/config {}", super::config::target_words(file)),
Query::Marks { .. } => "/marks".to_owned(),
Query::Providers { .. } => "/providers".to_owned(),
Query::Lineages { .. } => "/lineages".to_owned(),
Query::Models { provider, .. } => format!("/models {provider}"),
Query::Clients { .. } => "/clients".to_owned(),
Query::Invocations => "/invocations".to_owned(),
Query::Capture { invocation } => format!("/capture {invocation}"),
}
}
fn spell_payload(payload: &Payload) -> String {
match payload {
Payload::Bare => "/prepare".to_owned(),
Payload::Path { dir } => format!("/prepare dir {}", dir.display()),
Payload::Ball { ball, .. } => match ball {
BallSpec::Existing { .. } => "/prepare ball".to_owned(),
BallSpec::New { title, body } => format!(
"/prepare ball --new {title}{}",
flag("body", Some(body).filter(|b| !b.is_empty()))
),
},
}
}
fn flag(key: &str, value: Option<&String>) -> String {
match value {
Some(text) => format!(" --{key} {text}"),
None => String::new(),
}
}