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