use std::collections::HashMap; use serde::{Deserialize, Serialize}; use super::common::AgentStatus; use super::common::SplitDirection; use super::panes::PaneInfo; use super::workspaces::WorkspaceWorktreeInfo; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginLinkParams { pub path: String, #[serde(default = "super::common::default_true")] pub enabled: bool, #[serde(default, skip_serializing_if = "Option::is_none")] pub source: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct PluginListParams { #[serde(default, skip_serializing_if = "Option::is_none")] pub plugin_id: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginUnlinkParams { pub plugin_id: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginSetEnabledParams { pub plugin_id: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct InstalledPluginInfo { pub plugin_id: String, pub name: String, pub version: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, pub manifest_path: String, pub plugin_root: String, pub enabled: bool, #[serde(default, skip_serializing_if = "Option::is_none")] pub platforms: Option>, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub build: Vec, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub actions: Vec, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub events: Vec, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub panes: Vec, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub link_handlers: Vec, #[serde(default)] pub source: PluginSourceInfo, /// Warnings collected at link time or on registry load (e.g. unknown event names, /// missing manifest file). Non-fatal — the entry is kept and surfaced by plugin.list. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub warnings: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginSourceInfo { #[serde(default)] pub kind: PluginSourceKind, #[serde(default, skip_serializing_if = "Option::is_none")] pub owner: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub repo: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub subdir: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub requested_ref: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub resolved_commit: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub managed_path: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub installed_unix_ms: Option, } impl Default for PluginSourceInfo { fn default() -> Self { Self { kind: PluginSourceKind::Local, owner: None, repo: None, subdir: None, requested_ref: None, resolved_commit: None, managed_path: None, installed_unix_ms: None, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "snake_case")] pub enum PluginSourceKind { #[default] Local, Github, } pub(crate) fn plugin_managed_path_component(value: &str) -> String { value .chars() .map(|ch| { if ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | '.') { ch } else { '_' } }) .collect() } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginManifestBuild { #[serde(default, skip_serializing_if = "Option::is_none")] pub platforms: Option>, pub command: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginManifestAction { pub id: String, pub title: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub contexts: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub platforms: Option>, pub command: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginManifestEventHook { pub on: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub platforms: Option>, pub command: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginManifestPane { pub id: String, pub title: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub platforms: Option>, #[serde(default)] pub placement: PluginPanePlacement, pub command: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginManifestLinkHandler { pub id: String, pub title: String, pub pattern: String, pub action: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub platforms: Option>, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct PluginActionListParams { #[serde(default, skip_serializing_if = "Option::is_none")] pub plugin_id: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct PluginLogListParams { #[serde(default, skip_serializing_if = "Option::is_none")] pub plugin_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub limit: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginActionInvokeParams { pub action_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub plugin_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub context: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginCommandLogInfo { pub log_id: String, pub plugin_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub action_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub event: Option, pub command: Vec, pub status: PluginCommandStatus, pub started_unix_ms: u64, #[serde(default, skip_serializing_if = "Option::is_none")] pub finished_unix_ms: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub exit_code: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub stdout: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub stderr: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub error: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum PluginCommandStatus { Running, Succeeded, Failed, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum PluginPlatform { Linux, Macos, Windows, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum PluginActionContext { Global, Workspace, Tab, Pane, Selection, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginInvocationContext { #[serde(default, skip_serializing_if = "Option::is_none")] pub workspace_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub workspace_label: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub workspace_cwd: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub worktree: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub tab_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub tab_label: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub focused_pane_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub focused_pane_cwd: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub focused_pane_agent: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub focused_pane_status: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub selected_text: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub invocation_source: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub correlation_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub clicked_url: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub link_handler_id: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginActionInfo { pub plugin_id: String, pub action_id: String, pub title: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub contexts: Vec, pub command: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub platforms: Option>, } impl PluginActionInfo { pub fn qualified_id(&self) -> String { format!("{}.{}", self.plugin_id, self.action_id) } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginPaneOpenParams { pub plugin_id: String, pub entrypoint: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub placement: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub workspace_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub target_pane_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub direction: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub cwd: Option, #[serde(default)] pub focus: bool, #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub env: HashMap, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "snake_case")] pub enum PluginPanePlacement { #[default] Overlay, Split, Tab, Zoomed, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginPaneFocusParams { pub pane_id: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginPaneCloseParams { pub pane_id: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginPaneInfo { pub plugin_id: String, pub entrypoint: String, pub pane: PaneInfo, }