1use crate::Claude;
2use crate::command::ClaudeCommand;
3use crate::error::Result;
4use crate::exec::{self, CommandOutput};
5use crate::types::Scope;
6
7#[derive(Debug, Clone, Default)]
22pub struct PluginListCommand {
23 json: bool,
24 available: bool,
25}
26
27impl PluginListCommand {
28 #[must_use]
29 pub fn new() -> Self {
30 Self::default()
31 }
32
33 #[must_use]
35 pub fn json(mut self) -> Self {
36 self.json = true;
37 self
38 }
39
40 #[must_use]
42 pub fn available(mut self) -> Self {
43 self.available = true;
44 self
45 }
46}
47
48impl ClaudeCommand for PluginListCommand {
49 type Output = CommandOutput;
50
51 fn args(&self) -> Vec<String> {
52 let mut args = vec!["plugin".to_string(), "list".to_string()];
53 if self.json {
54 args.push("--json".to_string());
55 }
56 if self.available {
57 args.push("--available".to_string());
58 }
59 args
60 }
61
62 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
63 exec::run_claude(claude, self.args()).await
64 }
65}
66
67#[derive(Debug, Clone)]
84pub struct PluginInstallCommand {
85 plugin: String,
86 scope: Option<Scope>,
87}
88
89impl PluginInstallCommand {
90 #[must_use]
91 pub fn new(plugin: impl Into<String>) -> Self {
92 Self {
93 plugin: plugin.into(),
94 scope: None,
95 }
96 }
97
98 #[must_use]
100 pub fn scope(mut self, scope: Scope) -> Self {
101 self.scope = Some(scope);
102 self
103 }
104}
105
106impl ClaudeCommand for PluginInstallCommand {
107 type Output = CommandOutput;
108
109 fn args(&self) -> Vec<String> {
110 let mut args = vec!["plugin".to_string(), "install".to_string()];
111 if let Some(ref scope) = self.scope {
112 args.push("--scope".to_string());
113 args.push(scope.as_arg().to_string());
114 }
115 args.push(self.plugin.clone());
116 args
117 }
118
119 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
120 exec::run_claude(claude, self.args()).await
121 }
122}
123
124#[derive(Debug, Clone)]
126pub struct PluginUninstallCommand {
127 plugin: String,
128 scope: Option<Scope>,
129}
130
131impl PluginUninstallCommand {
132 #[must_use]
133 pub fn new(plugin: impl Into<String>) -> Self {
134 Self {
135 plugin: plugin.into(),
136 scope: None,
137 }
138 }
139
140 #[must_use]
142 pub fn scope(mut self, scope: Scope) -> Self {
143 self.scope = Some(scope);
144 self
145 }
146}
147
148impl ClaudeCommand for PluginUninstallCommand {
149 type Output = CommandOutput;
150
151 fn args(&self) -> Vec<String> {
152 let mut args = vec!["plugin".to_string(), "uninstall".to_string()];
153 if let Some(ref scope) = self.scope {
154 args.push("--scope".to_string());
155 args.push(scope.as_arg().to_string());
156 }
157 args.push(self.plugin.clone());
158 args
159 }
160
161 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
162 exec::run_claude(claude, self.args()).await
163 }
164}
165
166#[derive(Debug, Clone)]
168pub struct PluginEnableCommand {
169 plugin: String,
170 scope: Option<Scope>,
171}
172
173impl PluginEnableCommand {
174 #[must_use]
175 pub fn new(plugin: impl Into<String>) -> Self {
176 Self {
177 plugin: plugin.into(),
178 scope: None,
179 }
180 }
181
182 #[must_use]
184 pub fn scope(mut self, scope: Scope) -> Self {
185 self.scope = Some(scope);
186 self
187 }
188}
189
190impl ClaudeCommand for PluginEnableCommand {
191 type Output = CommandOutput;
192
193 fn args(&self) -> Vec<String> {
194 let mut args = vec!["plugin".to_string(), "enable".to_string()];
195 if let Some(ref scope) = self.scope {
196 args.push("--scope".to_string());
197 args.push(scope.as_arg().to_string());
198 }
199 args.push(self.plugin.clone());
200 args
201 }
202
203 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
204 exec::run_claude(claude, self.args()).await
205 }
206}
207
208#[derive(Debug, Clone)]
210pub struct PluginDisableCommand {
211 plugin: Option<String>,
212 scope: Option<Scope>,
213 all: bool,
214}
215
216impl PluginDisableCommand {
217 #[must_use]
219 pub fn new(plugin: impl Into<String>) -> Self {
220 Self {
221 plugin: Some(plugin.into()),
222 scope: None,
223 all: false,
224 }
225 }
226
227 #[must_use]
229 pub fn all() -> Self {
230 Self {
231 plugin: None,
232 scope: None,
233 all: true,
234 }
235 }
236
237 #[must_use]
239 pub fn scope(mut self, scope: Scope) -> Self {
240 self.scope = Some(scope);
241 self
242 }
243}
244
245impl ClaudeCommand for PluginDisableCommand {
246 type Output = CommandOutput;
247
248 fn args(&self) -> Vec<String> {
249 let mut args = vec!["plugin".to_string(), "disable".to_string()];
250 if self.all {
251 args.push("--all".to_string());
252 }
253 if let Some(ref scope) = self.scope {
254 args.push("--scope".to_string());
255 args.push(scope.as_arg().to_string());
256 }
257 if let Some(ref plugin) = self.plugin {
258 args.push(plugin.clone());
259 }
260 args
261 }
262
263 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
264 exec::run_claude(claude, self.args()).await
265 }
266}
267
268#[derive(Debug, Clone)]
270pub struct PluginUpdateCommand {
271 plugin: String,
272 scope: Option<Scope>,
273}
274
275impl PluginUpdateCommand {
276 #[must_use]
277 pub fn new(plugin: impl Into<String>) -> Self {
278 Self {
279 plugin: plugin.into(),
280 scope: None,
281 }
282 }
283
284 #[must_use]
286 pub fn scope(mut self, scope: Scope) -> Self {
287 self.scope = Some(scope);
288 self
289 }
290}
291
292impl ClaudeCommand for PluginUpdateCommand {
293 type Output = CommandOutput;
294
295 fn args(&self) -> Vec<String> {
296 let mut args = vec!["plugin".to_string(), "update".to_string()];
297 if let Some(ref scope) = self.scope {
298 args.push("--scope".to_string());
299 args.push(scope.as_arg().to_string());
300 }
301 args.push(self.plugin.clone());
302 args
303 }
304
305 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
306 exec::run_claude(claude, self.args()).await
307 }
308}
309
310#[derive(Debug, Clone)]
312pub struct PluginValidateCommand {
313 path: String,
314}
315
316impl PluginValidateCommand {
317 #[must_use]
318 pub fn new(path: impl Into<String>) -> Self {
319 Self { path: path.into() }
320 }
321}
322
323impl ClaudeCommand for PluginValidateCommand {
324 type Output = CommandOutput;
325
326 fn args(&self) -> Vec<String> {
327 vec![
328 "plugin".to_string(),
329 "validate".to_string(),
330 self.path.clone(),
331 ]
332 }
333
334 async fn execute(&self, claude: &Claude) -> Result<CommandOutput> {
335 exec::run_claude(claude, self.args()).await
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342 use crate::command::ClaudeCommand;
343
344 #[test]
345 fn test_plugin_list() {
346 let cmd = PluginListCommand::new().json().available();
347 assert_eq!(
348 ClaudeCommand::args(&cmd),
349 vec!["plugin", "list", "--json", "--available"]
350 );
351 }
352
353 #[test]
354 fn test_plugin_install() {
355 let cmd = PluginInstallCommand::new("my-plugin").scope(Scope::User);
356 assert_eq!(
357 ClaudeCommand::args(&cmd),
358 vec!["plugin", "install", "--scope", "user", "my-plugin"]
359 );
360 }
361
362 #[test]
363 fn test_plugin_uninstall() {
364 let cmd = PluginUninstallCommand::new("old-plugin");
365 assert_eq!(
366 ClaudeCommand::args(&cmd),
367 vec!["plugin", "uninstall", "old-plugin"]
368 );
369 }
370
371 #[test]
372 fn test_plugin_enable() {
373 let cmd = PluginEnableCommand::new("my-plugin").scope(Scope::Project);
374 assert_eq!(
375 ClaudeCommand::args(&cmd),
376 vec!["plugin", "enable", "--scope", "project", "my-plugin"]
377 );
378 }
379
380 #[test]
381 fn test_plugin_disable_specific() {
382 let cmd = PluginDisableCommand::new("my-plugin");
383 assert_eq!(
384 ClaudeCommand::args(&cmd),
385 vec!["plugin", "disable", "my-plugin"]
386 );
387 }
388
389 #[test]
390 fn test_plugin_disable_all() {
391 let cmd = PluginDisableCommand::all();
392 assert_eq!(
393 ClaudeCommand::args(&cmd),
394 vec!["plugin", "disable", "--all"]
395 );
396 }
397
398 #[test]
399 fn test_plugin_update() {
400 let cmd = PluginUpdateCommand::new("my-plugin").scope(Scope::Local);
401 assert_eq!(
402 ClaudeCommand::args(&cmd),
403 vec!["plugin", "update", "--scope", "local", "my-plugin"]
404 );
405 }
406
407 #[test]
408 fn test_plugin_validate() {
409 let cmd = PluginValidateCommand::new("/path/to/manifest");
410 assert_eq!(
411 ClaudeCommand::args(&cmd),
412 vec!["plugin", "validate", "/path/to/manifest"]
413 );
414 }
415}