#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum AgentMode {
#[default]
Build,
Plan,
}
impl AgentMode {
pub fn label(self) -> &'static str {
match self {
Self::Build => "Build",
Self::Plan => "Plan",
}
}
pub fn prompt_prefix(self) -> &'static str {
match self {
Self::Build => "build> ",
Self::Plan => "plan> ",
}
}
pub fn toggle(self) -> Self {
match self {
Self::Build => Self::Plan,
Self::Plan => Self::Build,
}
}
pub fn contract(self) -> &'static str {
match self {
Self::Build => "inspect, patch small, validate, repair",
Self::Plan => "read-only planning; no file mutation",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum ThinkingMode {
Off,
Low,
#[default]
Medium,
High,
XHigh,
}
impl ThinkingMode {
pub fn label(self) -> &'static str {
match self {
Self::Off => "none",
Self::Low => "low",
Self::Medium => "medium",
Self::High => "high",
Self::XHigh => "xhigh",
}
}
pub fn next(self) -> Self {
match self {
Self::Off => Self::Low,
Self::Low => Self::Medium,
Self::Medium => Self::High,
Self::High => Self::XHigh,
Self::XHigh => Self::Off,
}
}
pub fn previous(self) -> Self {
match self {
Self::Off => Self::XHigh,
Self::Low => Self::Off,
Self::Medium => Self::Low,
Self::High => Self::Medium,
Self::XHigh => Self::High,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum PaneId {
#[default]
Transcript,
Sidebar,
Prompt,
}
impl PaneId {
pub fn next(self) -> Self {
match self {
Self::Transcript => Self::Sidebar,
Self::Sidebar => Self::Prompt,
Self::Prompt => Self::Transcript,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum OverlayKind {
Help,
CommandPalette,
ContextPicker,
AgentFlow,
ProviderSetup,
ThemeEditor,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum FlowLane {
#[default]
Overview,
Context,
Kanban,
Runs,
}
impl FlowLane {
pub fn label(self) -> &'static str {
match self {
Self::Overview => "Overview",
Self::Context => "Context",
Self::Kanban => "Kanban",
Self::Runs => "Runs",
}
}
pub fn next(self) -> Self {
match self {
Self::Overview => Self::Context,
Self::Context => Self::Kanban,
Self::Kanban => Self::Runs,
Self::Runs => Self::Overview,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum TaskStatus {
Todo,
Doing,
Done,
Blocked,
}
impl TaskStatus {
pub fn label(self) -> &'static str {
match self {
Self::Todo => "todo",
Self::Doing => "doing",
Self::Done => "done",
Self::Blocked => "blocked",
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ResourceSnapshot {
pub session_messages: usize,
pub prompt_history: usize,
pub run_history: usize,
pub kanban_tasks: usize,
pub active_model_task: bool,
pub active_validation_task: bool,
}
impl ResourceSnapshot {
pub const MAX_SESSION_MESSAGES: usize = 800;
pub const MAX_PROMPT_HISTORY: usize = 100;
pub const MAX_RUN_HISTORY: usize = 100;
pub const MAX_KANBAN_TASKS: usize = 200;
pub fn compact_lines(&self) -> [String; 4] {
[
usage_line(
"messages",
self.session_messages,
Self::MAX_SESSION_MESSAGES,
),
usage_line("prompts", self.prompt_history, Self::MAX_PROMPT_HISTORY),
usage_line("runs", self.run_history, Self::MAX_RUN_HISTORY),
usage_line("tasks", self.kanban_tasks, Self::MAX_KANBAN_TASKS),
]
}
}
fn usage_line(label: &str, count: usize, limit: usize) -> String {
let percent = if limit == 0 {
0
} else {
count.saturating_mul(100) / limit
};
format!("{label}: {count}/{limit} {percent}%")
}