Skip to main content

vtcode_core/plugins/
manifest.rs

1//! VT Code plugin manifest implementation
2//!
3//! This module implements the VT Code plugin manifest format
4//! with support for commands, agents, skills, hooks, MCP, and LSP servers.
5
6use hashbrown::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10/// Author information for plugin manifests
11#[derive(Debug, Clone, Deserialize, Serialize)]
12pub struct PluginAuthor {
13    pub name: String,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub email: Option<String>,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub url: Option<String>,
18}
19
20/// VT Code plugin manifest
21#[derive(Debug, Clone, Deserialize, Serialize)]
22pub struct PluginManifest {
23    /// Required: Unique identifier (kebab-case, no spaces)
24    pub name: String,
25
26    /// Semantic version
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub version: Option<String>,
29
30    /// Brief description of plugin purpose
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub description: Option<String>,
33
34    /// Author information
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub author: Option<PluginAuthor>,
37
38    /// Documentation URL
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub homepage: Option<String>,
41
42    /// Source code URL
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub repository: Option<String>,
45
46    /// License identifier
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub license: Option<String>,
49
50    /// Discovery tags
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub keywords: Option<Vec<String>>,
53
54    /// Additional command files/directories
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub commands: Option<Vec<String>>,
57
58    /// Additional agent files
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub agents: Option<Vec<String>>,
61
62    /// Additional skill directories
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub skills: Option<Vec<String>>,
65
66    /// Hook config path or inline config
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub hooks: Option<HookConfig>,
69
70    /// MCP config path or inline config
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub mcp_servers: Option<McpServerConfig>,
73
74    /// Output style files/directories
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub output_styles: Option<Vec<String>>,
77
78    /// LSP server configuration
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub lsp_servers: Option<LspServerConfig>,
81}
82
83/// Hook configuration for event handling
84#[derive(Debug, Clone, Deserialize, Serialize)]
85#[serde(untagged)]
86pub enum HookConfig {
87    /// Path to hook configuration file
88    Path(String),
89    /// Inline hook configuration
90    Inline(HookConfiguration),
91}
92
93/// MCP server configuration
94#[derive(Debug, Clone, Deserialize, Serialize)]
95#[serde(untagged)]
96pub enum McpServerConfig {
97    /// Path to MCP configuration file
98    Path(String),
99    /// Inline MCP server configuration
100    Inline(HashMap<String, McpServerDefinition>),
101}
102
103/// LSP server configuration
104#[derive(Debug, Clone, Deserialize, Serialize)]
105#[serde(untagged)]
106pub enum LspServerConfig {
107    /// Path to LSP configuration file
108    Path(String),
109    /// Inline LSP server configuration
110    Inline(HashMap<String, LspServerDefinition>),
111}
112
113/// Hook configuration structure
114#[derive(Debug, Clone, Deserialize, Serialize)]
115pub struct HookConfiguration {
116    pub hooks: HashMap<String, Vec<HookDefinition>>,
117}
118
119/// Individual hook definition
120#[derive(Debug, Clone, Deserialize, Serialize)]
121pub struct HookDefinition {
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub matcher: Option<String>,
124    pub hooks: Vec<HookAction>,
125}
126
127/// Hook action types
128#[derive(Debug, Clone, Deserialize, Serialize)]
129#[serde(tag = "type")]
130pub enum HookAction {
131    #[serde(rename = "command")]
132    Command { command: String },
133    #[serde(rename = "prompt")]
134    Prompt { prompt: String },
135    #[serde(rename = "agent")]
136    Agent { agent: String },
137}
138
139/// MCP server definition
140#[derive(Debug, Clone, Deserialize, Serialize)]
141pub struct McpServerDefinition {
142    pub command: String,
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub args: Option<Vec<String>>,
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub env: Option<HashMap<String, String>>,
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub cwd: Option<String>,
149}
150
151/// LSP server definition
152#[derive(Debug, Clone, Deserialize, Serialize)]
153pub struct LspServerDefinition {
154    pub command: String,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub args: Option<Vec<String>>,
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub env: Option<HashMap<String, String>>,
159    pub extension_to_language: HashMap<String, String>,
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub transport: Option<String>,
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub initialization_options: Option<serde_json::Value>,
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub settings: Option<serde_json::Value>,
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub workspace_folder: Option<String>,
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub startup_timeout: Option<u64>,
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub shutdown_timeout: Option<u64>,
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub restart_on_crash: Option<bool>,
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub max_restarts: Option<u32>,
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub logging_config: Option<LspLoggingConfig>,
178}
179
180/// LSP logging configuration
181#[derive(Debug, Clone, Deserialize, Serialize)]
182pub struct LspLoggingConfig {
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub args: Option<Vec<String>>,
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub env: Option<HashMap<String, String>>,
187}