1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CommandDef {
12 pub name: String,
13 pub description: String,
14 pub usage: String,
15 pub flags: Vec<FlagDef>,
16 pub examples: Vec<ExampleDef>,
17 pub exit_codes: Vec<ExitCodeDef>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct FlagDef {
23 pub name: String,
24 #[serde(rename = "type")]
25 pub flag_type: String,
26 pub required: bool,
27 pub description: String,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub default: Option<String>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ExampleDef {
35 pub description: String,
36 pub command: String,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct ExitCodeDef {
42 pub code: i32,
43 pub description: String,
44}
45
46#[derive(Debug, Serialize, Deserialize)]
48pub struct CommandsListResponse {
49 #[serde(rename = "schemaVersion")]
50 pub schema_version: u32,
51 #[serde(rename = "type")]
52 pub response_type: String,
53 pub ok: bool,
54 pub commands: Vec<CommandDef>,
55}
56
57#[derive(Debug, Serialize, Deserialize)]
59pub struct HelpResponse {
60 #[serde(rename = "schemaVersion")]
61 pub schema_version: u32,
62 #[serde(rename = "type")]
63 pub response_type: String,
64 pub ok: bool,
65 pub command: String,
66 pub usage: String,
67 pub flags: Vec<FlagDef>,
68 pub examples: Vec<ExampleDef>,
69 #[serde(rename = "exitCodes")]
70 pub exit_codes: Vec<ExitCodeDef>,
71}
72
73#[derive(Debug, Serialize, Deserialize)]
75pub struct SchemaResponse {
76 #[serde(rename = "schemaVersion")]
77 pub schema_version: u32,
78 #[serde(rename = "type")]
79 pub response_type: String,
80 pub ok: bool,
81 pub command: String,
82 pub schema: Value,
83}
84
85pub fn get_command_definitions() -> Vec<CommandDef> {
87 vec![
88 CommandDef {
90 name: "api call".to_string(),
91 description: "Call a Slack API method".to_string(),
92 usage: "slack-rs api call <method> [key=value]... [flags]".to_string(),
93 flags: vec![
94 FlagDef {
95 name: "--json".to_string(),
96 flag_type: "boolean".to_string(),
97 required: false,
98 description: "Send as JSON body (default: form-urlencoded)".to_string(),
99 default: None,
100 },
101 FlagDef {
102 name: "--get".to_string(),
103 flag_type: "boolean".to_string(),
104 required: false,
105 description: "Use GET method (default: POST)".to_string(),
106 default: None,
107 },
108 FlagDef {
109 name: "--raw".to_string(),
110 flag_type: "boolean".to_string(),
111 required: false,
112 description: "Output raw Slack API response (without envelope)".to_string(),
113 default: None,
114 },
115 FlagDef {
116 name: "--profile".to_string(),
117 flag_type: "string".to_string(),
118 required: false,
119 description: "Profile name".to_string(),
120 default: Some("default".to_string()),
121 },
122 ],
123 examples: vec![
124 ExampleDef {
125 description: "Get user info".to_string(),
126 command: "slack-rs api call users.info user=U123456 --get".to_string(),
127 },
128 ExampleDef {
129 description: "Post message".to_string(),
130 command: "slack-rs api call chat.postMessage channel=C123 text=Hello"
131 .to_string(),
132 },
133 ],
134 exit_codes: vec![
135 ExitCodeDef {
136 code: 0,
137 description: "Success".to_string(),
138 },
139 ExitCodeDef {
140 code: 1,
141 description: "API call failed".to_string(),
142 },
143 ],
144 },
145 CommandDef {
147 name: "auth login".to_string(),
148 description: "Authenticate with Slack via OAuth".to_string(),
149 usage: "slack-rs auth login [profile_name] [flags]".to_string(),
150 flags: vec![
151 FlagDef {
152 name: "--client-id".to_string(),
153 flag_type: "string".to_string(),
154 required: false,
155 description: "OAuth client ID".to_string(),
156 default: None,
157 },
158 FlagDef {
159 name: "--bot-scopes".to_string(),
160 flag_type: "string".to_string(),
161 required: false,
162 description: "Bot scopes (comma-separated or 'all')".to_string(),
163 default: None,
164 },
165 FlagDef {
166 name: "--user-scopes".to_string(),
167 flag_type: "string".to_string(),
168 required: false,
169 description: "User scopes (comma-separated or 'all')".to_string(),
170 default: None,
171 },
172 ],
173 examples: vec![ExampleDef {
174 description: "Login with default profile".to_string(),
175 command: "slack-rs auth login".to_string(),
176 }],
177 exit_codes: vec![
178 ExitCodeDef {
179 code: 0,
180 description: "Success".to_string(),
181 },
182 ExitCodeDef {
183 code: 1,
184 description: "Login failed".to_string(),
185 },
186 ],
187 },
188 CommandDef {
190 name: "auth status".to_string(),
191 description: "Show authentication status".to_string(),
192 usage: "slack-rs auth status [profile_name]".to_string(),
193 flags: vec![],
194 examples: vec![ExampleDef {
195 description: "Check status".to_string(),
196 command: "slack-rs auth status".to_string(),
197 }],
198 exit_codes: vec![
199 ExitCodeDef {
200 code: 0,
201 description: "Success".to_string(),
202 },
203 ExitCodeDef {
204 code: 1,
205 description: "Command failed".to_string(),
206 },
207 ],
208 },
209 CommandDef {
211 name: "auth list".to_string(),
212 description: "List all profiles".to_string(),
213 usage: "slack-rs auth list".to_string(),
214 flags: vec![],
215 examples: vec![ExampleDef {
216 description: "List profiles".to_string(),
217 command: "slack-rs auth list".to_string(),
218 }],
219 exit_codes: vec![
220 ExitCodeDef {
221 code: 0,
222 description: "Success".to_string(),
223 },
224 ExitCodeDef {
225 code: 1,
226 description: "Command failed".to_string(),
227 },
228 ],
229 },
230 CommandDef {
232 name: "auth logout".to_string(),
233 description: "Remove authentication for a profile".to_string(),
234 usage: "slack-rs auth logout [profile_name]".to_string(),
235 flags: vec![],
236 examples: vec![ExampleDef {
237 description: "Logout".to_string(),
238 command: "slack-rs auth logout".to_string(),
239 }],
240 exit_codes: vec![
241 ExitCodeDef {
242 code: 0,
243 description: "Success".to_string(),
244 },
245 ExitCodeDef {
246 code: 1,
247 description: "Command failed".to_string(),
248 },
249 ],
250 },
251 CommandDef {
253 name: "conv list".to_string(),
254 description: "List conversations".to_string(),
255 usage: "slack-rs conv list [flags]".to_string(),
256 flags: vec![
257 FlagDef {
258 name: "--types".to_string(),
259 flag_type: "string".to_string(),
260 required: false,
261 description: "Conversation types (comma-separated)".to_string(),
262 default: None,
263 },
264 FlagDef {
265 name: "--limit".to_string(),
266 flag_type: "integer".to_string(),
267 required: false,
268 description: "Maximum number of conversations".to_string(),
269 default: None,
270 },
271 FlagDef {
272 name: "--filter".to_string(),
273 flag_type: "string".to_string(),
274 required: false,
275 description: "Filter (key:value format, can be repeated)".to_string(),
276 default: None,
277 },
278 FlagDef {
279 name: "--format".to_string(),
280 flag_type: "string".to_string(),
281 required: false,
282 description: "Output format (json, jsonl, table, tsv)".to_string(),
283 default: Some("json".to_string()),
284 },
285 FlagDef {
286 name: "--raw".to_string(),
287 flag_type: "boolean".to_string(),
288 required: false,
289 description: "Output raw response (without envelope)".to_string(),
290 default: None,
291 },
292 FlagDef {
293 name: "--profile".to_string(),
294 flag_type: "string".to_string(),
295 required: false,
296 description: "Profile name".to_string(),
297 default: Some("default".to_string()),
298 },
299 ],
300 examples: vec![
301 ExampleDef {
302 description: "List all conversations".to_string(),
303 command: "slack-rs conv list".to_string(),
304 },
305 ExampleDef {
306 description: "List with filter".to_string(),
307 command: "slack-rs conv list --filter is_member:true".to_string(),
308 },
309 ],
310 exit_codes: vec![
311 ExitCodeDef {
312 code: 0,
313 description: "Success".to_string(),
314 },
315 ExitCodeDef {
316 code: 1,
317 description: "API call failed".to_string(),
318 },
319 ],
320 },
321 CommandDef {
323 name: "conv search".to_string(),
324 description: "Search conversations by name".to_string(),
325 usage: "slack-rs conv search <pattern> [flags]".to_string(),
326 flags: vec![FlagDef {
327 name: "--profile".to_string(),
328 flag_type: "string".to_string(),
329 required: false,
330 description: "Profile name".to_string(),
331 default: Some("default".to_string()),
332 }],
333 examples: vec![ExampleDef {
334 description: "Search conversations".to_string(),
335 command: "slack-rs conv search general".to_string(),
336 }],
337 exit_codes: vec![
338 ExitCodeDef {
339 code: 0,
340 description: "Success".to_string(),
341 },
342 ExitCodeDef {
343 code: 1,
344 description: "Command failed".to_string(),
345 },
346 ],
347 },
348 CommandDef {
350 name: "conv history".to_string(),
351 description: "Get conversation history".to_string(),
352 usage: "slack-rs conv history <channel> [flags]".to_string(),
353 flags: vec![
354 FlagDef {
355 name: "--limit".to_string(),
356 flag_type: "integer".to_string(),
357 required: false,
358 description: "Maximum number of messages".to_string(),
359 default: None,
360 },
361 FlagDef {
362 name: "--profile".to_string(),
363 flag_type: "string".to_string(),
364 required: false,
365 description: "Profile name".to_string(),
366 default: Some("default".to_string()),
367 },
368 ],
369 examples: vec![ExampleDef {
370 description: "Get history".to_string(),
371 command: "slack-rs conv history C123456".to_string(),
372 }],
373 exit_codes: vec![
374 ExitCodeDef {
375 code: 0,
376 description: "Success".to_string(),
377 },
378 ExitCodeDef {
379 code: 1,
380 description: "Command failed".to_string(),
381 },
382 ],
383 },
384 CommandDef {
386 name: "thread get".to_string(),
387 description: "Get thread messages (conversation replies)".to_string(),
388 usage: "slack-rs thread get <channel> <thread_ts> [flags]".to_string(),
389 flags: vec![
390 FlagDef {
391 name: "--limit".to_string(),
392 flag_type: "integer".to_string(),
393 required: false,
394 description: "Number of messages per page".to_string(),
395 default: Some("100".to_string()),
396 },
397 FlagDef {
398 name: "--inclusive".to_string(),
399 flag_type: "boolean".to_string(),
400 required: false,
401 description: "Include parent message in results".to_string(),
402 default: None,
403 },
404 FlagDef {
405 name: "--raw".to_string(),
406 flag_type: "boolean".to_string(),
407 required: false,
408 description: "Output raw Slack API response".to_string(),
409 default: None,
410 },
411 FlagDef {
412 name: "--profile".to_string(),
413 flag_type: "string".to_string(),
414 required: false,
415 description: "Profile name".to_string(),
416 default: Some("default".to_string()),
417 },
418 FlagDef {
419 name: "--token-type".to_string(),
420 flag_type: "string".to_string(),
421 required: false,
422 description: "Token type (bot or user)".to_string(),
423 default: None,
424 },
425 ],
426 examples: vec![
427 ExampleDef {
428 description: "Get thread messages".to_string(),
429 command: "slack-rs thread get C123456 1234567890.123456".to_string(),
430 },
431 ExampleDef {
432 description: "Get thread with parent message".to_string(),
433 command: "slack-rs thread get C123456 1234567890.123456 --inclusive".to_string(),
434 },
435 ],
436 exit_codes: vec![
437 ExitCodeDef {
438 code: 0,
439 description: "Success".to_string(),
440 },
441 ExitCodeDef {
442 code: 1,
443 description: "Command failed".to_string(),
444 },
445 ],
446 },
447 CommandDef {
449 name: "msg post".to_string(),
450 description: "Post a message to a channel".to_string(),
451 usage: "slack-rs msg post <channel> <text> [flags]".to_string(),
452 flags: vec![
453 FlagDef {
454 name: "--thread-ts".to_string(),
455 flag_type: "string".to_string(),
456 required: false,
457 description: "Thread timestamp for reply".to_string(),
458 default: None,
459 },
460 FlagDef {
461 name: "--reply-broadcast".to_string(),
462 flag_type: "boolean".to_string(),
463 required: false,
464 description: "Broadcast reply to channel".to_string(),
465 default: None,
466 },
467 FlagDef {
468 name: "--profile".to_string(),
469 flag_type: "string".to_string(),
470 required: false,
471 description: "Profile name".to_string(),
472 default: Some("default".to_string()),
473 },
474 FlagDef {
475 name: "--idempotency-key".to_string(),
476 flag_type: "string".to_string(),
477 required: false,
478 description: "Idempotency key for preventing duplicate operations".to_string(),
479 default: None,
480 },
481 ],
482 examples: vec![ExampleDef {
483 description: "Post message".to_string(),
484 command: "slack-rs msg post C123 'Hello world'".to_string(),
485 }],
486 exit_codes: vec![
487 ExitCodeDef {
488 code: 0,
489 description: "Success".to_string(),
490 },
491 ExitCodeDef {
492 code: 1,
493 description: "Post failed".to_string(),
494 },
495 ],
496 },
497 CommandDef {
499 name: "msg update".to_string(),
500 description: "Update a message".to_string(),
501 usage: "slack-rs msg update <channel> <ts> <text> [flags]".to_string(),
502 flags: vec![
503 FlagDef {
504 name: "--profile".to_string(),
505 flag_type: "string".to_string(),
506 required: false,
507 description: "Profile name".to_string(),
508 default: Some("default".to_string()),
509 },
510 FlagDef {
511 name: "--idempotency-key".to_string(),
512 flag_type: "string".to_string(),
513 required: false,
514 description: "Idempotency key for preventing duplicate operations".to_string(),
515 default: None,
516 },
517 ],
518 examples: vec![ExampleDef {
519 description: "Update message".to_string(),
520 command: "slack-rs msg update C123 1234567890.123456 'Updated text'".to_string(),
521 }],
522 exit_codes: vec![
523 ExitCodeDef {
524 code: 0,
525 description: "Success".to_string(),
526 },
527 ExitCodeDef {
528 code: 1,
529 description: "Update failed".to_string(),
530 },
531 ],
532 },
533 CommandDef {
535 name: "msg delete".to_string(),
536 description: "Delete a message".to_string(),
537 usage: "slack-rs msg delete <channel> <ts> [flags]".to_string(),
538 flags: vec![
539 FlagDef {
540 name: "--profile".to_string(),
541 flag_type: "string".to_string(),
542 required: false,
543 description: "Profile name".to_string(),
544 default: Some("default".to_string()),
545 },
546 FlagDef {
547 name: "--idempotency-key".to_string(),
548 flag_type: "string".to_string(),
549 required: false,
550 description: "Idempotency key for preventing duplicate operations".to_string(),
551 default: None,
552 },
553 ],
554 examples: vec![ExampleDef {
555 description: "Delete message".to_string(),
556 command: "slack-rs msg delete C123 1234567890.123456".to_string(),
557 }],
558 exit_codes: vec![
559 ExitCodeDef {
560 code: 0,
561 description: "Success".to_string(),
562 },
563 ExitCodeDef {
564 code: 1,
565 description: "Delete failed".to_string(),
566 },
567 ],
568 },
569 CommandDef {
571 name: "users info".to_string(),
572 description: "Get user information".to_string(),
573 usage: "slack-rs users info <user_id> [flags]".to_string(),
574 flags: vec![FlagDef {
575 name: "--profile".to_string(),
576 flag_type: "string".to_string(),
577 required: false,
578 description: "Profile name".to_string(),
579 default: Some("default".to_string()),
580 }],
581 examples: vec![ExampleDef {
582 description: "Get user info".to_string(),
583 command: "slack-rs users info U123456".to_string(),
584 }],
585 exit_codes: vec![
586 ExitCodeDef {
587 code: 0,
588 description: "Success".to_string(),
589 },
590 ExitCodeDef {
591 code: 1,
592 description: "Command failed".to_string(),
593 },
594 ],
595 },
596 CommandDef {
598 name: "react add".to_string(),
599 description: "Add a reaction to a message".to_string(),
600 usage: "slack-rs react add <channel> <ts> <emoji> [flags]".to_string(),
601 flags: vec![
602 FlagDef {
603 name: "--profile".to_string(),
604 flag_type: "string".to_string(),
605 required: false,
606 description: "Profile name".to_string(),
607 default: Some("default".to_string()),
608 },
609 FlagDef {
610 name: "--idempotency-key".to_string(),
611 flag_type: "string".to_string(),
612 required: false,
613 description: "Idempotency key for preventing duplicate operations".to_string(),
614 default: None,
615 },
616 ],
617 examples: vec![ExampleDef {
618 description: "Add reaction".to_string(),
619 command: "slack-rs react add C123 1234567890.123456 thumbsup".to_string(),
620 }],
621 exit_codes: vec![
622 ExitCodeDef {
623 code: 0,
624 description: "Success".to_string(),
625 },
626 ExitCodeDef {
627 code: 1,
628 description: "Command failed".to_string(),
629 },
630 ],
631 },
632 CommandDef {
634 name: "react remove".to_string(),
635 description: "Remove a reaction from a message".to_string(),
636 usage: "slack-rs react remove <channel> <ts> <emoji> [flags]".to_string(),
637 flags: vec![
638 FlagDef {
639 name: "--profile".to_string(),
640 flag_type: "string".to_string(),
641 required: false,
642 description: "Profile name".to_string(),
643 default: Some("default".to_string()),
644 },
645 FlagDef {
646 name: "--idempotency-key".to_string(),
647 flag_type: "string".to_string(),
648 required: false,
649 description: "Idempotency key for preventing duplicate operations".to_string(),
650 default: None,
651 },
652 ],
653 examples: vec![ExampleDef {
654 description: "Remove reaction".to_string(),
655 command: "slack-rs react remove C123 1234567890.123456 thumbsup".to_string(),
656 }],
657 exit_codes: vec![
658 ExitCodeDef {
659 code: 0,
660 description: "Success".to_string(),
661 },
662 ExitCodeDef {
663 code: 1,
664 description: "Command failed".to_string(),
665 },
666 ],
667 },
668 CommandDef {
670 name: "file upload".to_string(),
671 description: "Upload a file".to_string(),
672 usage: "slack-rs file upload <path> [flags]".to_string(),
673 flags: vec![
674 FlagDef {
675 name: "--profile".to_string(),
676 flag_type: "string".to_string(),
677 required: false,
678 description: "Profile name".to_string(),
679 default: Some("default".to_string()),
680 },
681 FlagDef {
682 name: "--idempotency-key".to_string(),
683 flag_type: "string".to_string(),
684 required: false,
685 description: "Idempotency key for preventing duplicate operations".to_string(),
686 default: None,
687 },
688 ],
689 examples: vec![ExampleDef {
690 description: "Upload file".to_string(),
691 command: "slack-rs file upload document.pdf".to_string(),
692 }],
693 exit_codes: vec![
694 ExitCodeDef {
695 code: 0,
696 description: "Success".to_string(),
697 },
698 ExitCodeDef {
699 code: 1,
700 description: "Upload failed".to_string(),
701 },
702 ],
703 },
704 CommandDef {
706 name: "file download".to_string(),
707 description: "Download a file from Slack".to_string(),
708 usage: "slack-rs file download [<file_id>] [flags]".to_string(),
709 flags: vec![
710 FlagDef {
711 name: "--url".to_string(),
712 flag_type: "string".to_string(),
713 required: false,
714 description: "Direct download URL (alternative to file_id)".to_string(),
715 default: None,
716 },
717 FlagDef {
718 name: "--out".to_string(),
719 flag_type: "string".to_string(),
720 required: false,
721 description: "Output path (omit for current directory, '-' for stdout, directory for auto-naming)".to_string(),
722 default: None,
723 },
724 FlagDef {
725 name: "--profile".to_string(),
726 flag_type: "string".to_string(),
727 required: false,
728 description: "Profile name".to_string(),
729 default: Some("default".to_string()),
730 },
731 FlagDef {
732 name: "--token-type".to_string(),
733 flag_type: "string".to_string(),
734 required: false,
735 description: "Token type (bot or user)".to_string(),
736 default: None,
737 },
738 ],
739 examples: vec![
740 ExampleDef {
741 description: "Download by file ID".to_string(),
742 command: "slack-rs file download F123456".to_string(),
743 },
744 ExampleDef {
745 description: "Download to stdout".to_string(),
746 command: "slack-rs file download F123456 --out -".to_string(),
747 },
748 ExampleDef {
749 description: "Download by URL".to_string(),
750 command: "slack-rs file download --url https://files.slack.com/...".to_string(),
751 },
752 ],
753 exit_codes: vec![
754 ExitCodeDef {
755 code: 0,
756 description: "Success".to_string(),
757 },
758 ExitCodeDef {
759 code: 1,
760 description: "Download failed".to_string(),
761 },
762 ],
763 },
764 CommandDef {
766 name: "search".to_string(),
767 description: "Search messages".to_string(),
768 usage: "slack-rs search <query> [flags]".to_string(),
769 flags: vec![
770 FlagDef {
771 name: "--count".to_string(),
772 flag_type: "integer".to_string(),
773 required: false,
774 description: "Number of results".to_string(),
775 default: None,
776 },
777 FlagDef {
778 name: "--page".to_string(),
779 flag_type: "integer".to_string(),
780 required: false,
781 description: "Page number".to_string(),
782 default: None,
783 },
784 FlagDef {
785 name: "--profile".to_string(),
786 flag_type: "string".to_string(),
787 required: false,
788 description: "Profile name".to_string(),
789 default: Some("default".to_string()),
790 },
791 ],
792 examples: vec![ExampleDef {
793 description: "Search messages".to_string(),
794 command: "slack-rs search 'important announcement'".to_string(),
795 }],
796 exit_codes: vec![
797 ExitCodeDef {
798 code: 0,
799 description: "Success".to_string(),
800 },
801 ExitCodeDef {
802 code: 1,
803 description: "Search failed".to_string(),
804 },
805 ],
806 },
807 CommandDef {
809 name: "auth rename".to_string(),
810 description: "Rename a profile".to_string(),
811 usage: "slack-rs auth rename <old_name> <new_name>".to_string(),
812 flags: vec![],
813 examples: vec![ExampleDef {
814 description: "Rename profile".to_string(),
815 command: "slack-rs auth rename work personal".to_string(),
816 }],
817 exit_codes: vec![
818 ExitCodeDef {
819 code: 0,
820 description: "Success".to_string(),
821 },
822 ExitCodeDef {
823 code: 1,
824 description: "Rename failed".to_string(),
825 },
826 ],
827 },
828 CommandDef {
830 name: "auth export".to_string(),
831 description: "Export profiles to encrypted file".to_string(),
832 usage: "slack-rs auth export [flags]".to_string(),
833 flags: vec![
834 FlagDef {
835 name: "--profile".to_string(),
836 flag_type: "string".to_string(),
837 required: false,
838 description: "Export specific profile".to_string(),
839 default: Some("default".to_string()),
840 },
841 FlagDef {
842 name: "--all".to_string(),
843 flag_type: "boolean".to_string(),
844 required: false,
845 description: "Export all profiles".to_string(),
846 default: None,
847 },
848 FlagDef {
849 name: "--out".to_string(),
850 flag_type: "string".to_string(),
851 required: true,
852 description: "Output file path".to_string(),
853 default: None,
854 },
855 FlagDef {
856 name: "--passphrase-env".to_string(),
857 flag_type: "string".to_string(),
858 required: false,
859 description: "Environment variable containing passphrase".to_string(),
860 default: None,
861 },
862 FlagDef {
863 name: "--passphrase-prompt".to_string(),
864 flag_type: "boolean".to_string(),
865 required: false,
866 description: "Prompt for passphrase".to_string(),
867 default: None,
868 },
869 FlagDef {
870 name: "--yes".to_string(),
871 flag_type: "boolean".to_string(),
872 required: false,
873 description: "Confirm dangerous operation".to_string(),
874 default: None,
875 },
876 ],
877 examples: vec![ExampleDef {
878 description: "Export all profiles".to_string(),
879 command: "slack-rs auth export --all --out profiles.enc --yes".to_string(),
880 }],
881 exit_codes: vec![
882 ExitCodeDef {
883 code: 0,
884 description: "Success".to_string(),
885 },
886 ExitCodeDef {
887 code: 1,
888 description: "Export failed".to_string(),
889 },
890 ],
891 },
892 CommandDef {
894 name: "auth import".to_string(),
895 description: "Import profiles from encrypted file".to_string(),
896 usage: "slack-rs auth import [flags]".to_string(),
897 flags: vec![
898 FlagDef {
899 name: "--in".to_string(),
900 flag_type: "string".to_string(),
901 required: true,
902 description: "Input file path".to_string(),
903 default: None,
904 },
905 FlagDef {
906 name: "--passphrase-env".to_string(),
907 flag_type: "string".to_string(),
908 required: false,
909 description: "Environment variable containing passphrase".to_string(),
910 default: None,
911 },
912 FlagDef {
913 name: "--passphrase-prompt".to_string(),
914 flag_type: "boolean".to_string(),
915 required: false,
916 description: "Prompt for passphrase".to_string(),
917 default: None,
918 },
919 FlagDef {
920 name: "--yes".to_string(),
921 flag_type: "boolean".to_string(),
922 required: false,
923 description: "Automatically accept conflicts".to_string(),
924 default: None,
925 },
926 FlagDef {
927 name: "--force".to_string(),
928 flag_type: "boolean".to_string(),
929 required: false,
930 description: "Overwrite existing profiles".to_string(),
931 default: None,
932 },
933 FlagDef {
934 name: "--dry-run".to_string(),
935 flag_type: "boolean".to_string(),
936 required: false,
937 description: "Show what would be imported without making changes".to_string(),
938 default: None,
939 },
940 FlagDef {
941 name: "--json".to_string(),
942 flag_type: "boolean".to_string(),
943 required: false,
944 description: "Output results in JSON format".to_string(),
945 default: None,
946 },
947 ],
948 examples: vec![
949 ExampleDef {
950 description: "Import profiles".to_string(),
951 command: "slack-rs auth import --in profiles.enc".to_string(),
952 },
953 ExampleDef {
954 description: "Preview import without making changes".to_string(),
955 command: "slack-rs auth import --in profiles.enc --dry-run".to_string(),
956 },
957 ExampleDef {
958 description: "Preview import with JSON output".to_string(),
959 command: "slack-rs auth import --in profiles.enc --dry-run --json".to_string(),
960 },
961 ],
962 exit_codes: vec![
963 ExitCodeDef {
964 code: 0,
965 description: "Success".to_string(),
966 },
967 ExitCodeDef {
968 code: 1,
969 description: "Import failed".to_string(),
970 },
971 ],
972 },
973 CommandDef {
975 name: "config oauth set".to_string(),
976 description: "Set OAuth configuration for a profile".to_string(),
977 usage: "slack-rs config oauth set <profile> --client-id <id> --redirect-uri <uri> --scopes <scopes> [flags]".to_string(),
978 flags: vec![
979 FlagDef {
980 name: "--client-id".to_string(),
981 flag_type: "string".to_string(),
982 required: true,
983 description: "OAuth client ID".to_string(),
984 default: None,
985 },
986 FlagDef {
987 name: "--redirect-uri".to_string(),
988 flag_type: "string".to_string(),
989 required: true,
990 description: "OAuth redirect URI".to_string(),
991 default: None,
992 },
993 FlagDef {
994 name: "--scopes".to_string(),
995 flag_type: "string".to_string(),
996 required: true,
997 description: "Comma-separated list of scopes or 'all'".to_string(),
998 default: None,
999 },
1000 FlagDef {
1001 name: "--client-secret-env".to_string(),
1002 flag_type: "string".to_string(),
1003 required: false,
1004 description: "Read secret from environment variable".to_string(),
1005 default: None,
1006 },
1007 FlagDef {
1008 name: "--client-secret-file".to_string(),
1009 flag_type: "string".to_string(),
1010 required: false,
1011 description: "Read secret from file".to_string(),
1012 default: None,
1013 },
1014 FlagDef {
1015 name: "--client-secret".to_string(),
1016 flag_type: "string".to_string(),
1017 required: false,
1018 description: "Direct secret value (requires --yes, unsafe)".to_string(),
1019 default: None,
1020 },
1021 FlagDef {
1022 name: "--yes".to_string(),
1023 flag_type: "boolean".to_string(),
1024 required: false,
1025 description: "Confirm dangerous operation".to_string(),
1026 default: None,
1027 },
1028 ],
1029 examples: vec![ExampleDef {
1030 description: "Set OAuth config".to_string(),
1031 command: "slack-rs config oauth set work --client-id 123.456 --redirect-uri http://127.0.0.1:8765/callback --scopes all".to_string(),
1032 }],
1033 exit_codes: vec![
1034 ExitCodeDef {
1035 code: 0,
1036 description: "Success".to_string(),
1037 },
1038 ExitCodeDef {
1039 code: 1,
1040 description: "Config set failed".to_string(),
1041 },
1042 ],
1043 },
1044 CommandDef {
1046 name: "config oauth show".to_string(),
1047 description: "Show OAuth configuration for a profile".to_string(),
1048 usage: "slack-rs config oauth show <profile>".to_string(),
1049 flags: vec![],
1050 examples: vec![ExampleDef {
1051 description: "Show OAuth config".to_string(),
1052 command: "slack-rs config oauth show work".to_string(),
1053 }],
1054 exit_codes: vec![
1055 ExitCodeDef {
1056 code: 0,
1057 description: "Success".to_string(),
1058 },
1059 ExitCodeDef {
1060 code: 1,
1061 description: "Config show failed".to_string(),
1062 },
1063 ],
1064 },
1065 CommandDef {
1067 name: "config oauth delete".to_string(),
1068 description: "Delete OAuth configuration for a profile".to_string(),
1069 usage: "slack-rs config oauth delete <profile>".to_string(),
1070 flags: vec![],
1071 examples: vec![ExampleDef {
1072 description: "Delete OAuth config".to_string(),
1073 command: "slack-rs config oauth delete work".to_string(),
1074 }],
1075 exit_codes: vec![
1076 ExitCodeDef {
1077 code: 0,
1078 description: "Success".to_string(),
1079 },
1080 ExitCodeDef {
1081 code: 1,
1082 description: "Config delete failed".to_string(),
1083 },
1084 ],
1085 },
1086 CommandDef {
1088 name: "config set".to_string(),
1089 description: "Set default token type for a profile".to_string(),
1090 usage: "slack-rs config set <profile> --token-type <type>".to_string(),
1091 flags: vec![FlagDef {
1092 name: "--token-type".to_string(),
1093 flag_type: "string".to_string(),
1094 required: true,
1095 description: "Default token type (bot or user)".to_string(),
1096 default: None,
1097 }],
1098 examples: vec![ExampleDef {
1099 description: "Set token type".to_string(),
1100 command: "slack-rs config set work --token-type bot".to_string(),
1101 }],
1102 exit_codes: vec![
1103 ExitCodeDef {
1104 code: 0,
1105 description: "Success".to_string(),
1106 },
1107 ExitCodeDef {
1108 code: 1,
1109 description: "Config set failed".to_string(),
1110 },
1111 ],
1112 },
1113 CommandDef {
1115 name: "conv select".to_string(),
1116 description: "Interactively select a conversation".to_string(),
1117 usage: "slack-rs conv select [flags]".to_string(),
1118 flags: vec![FlagDef {
1119 name: "--profile".to_string(),
1120 flag_type: "string".to_string(),
1121 required: false,
1122 description: "Profile name".to_string(),
1123 default: Some("default".to_string()),
1124 }],
1125 examples: vec![ExampleDef {
1126 description: "Select conversation".to_string(),
1127 command: "slack-rs conv select".to_string(),
1128 }],
1129 exit_codes: vec![
1130 ExitCodeDef {
1131 code: 0,
1132 description: "Success".to_string(),
1133 },
1134 ExitCodeDef {
1135 code: 1,
1136 description: "Selection failed".to_string(),
1137 },
1138 ],
1139 },
1140 CommandDef {
1142 name: "users cache-update".to_string(),
1143 description: "Update user cache for mention resolution".to_string(),
1144 usage: "slack-rs users cache-update [flags]".to_string(),
1145 flags: vec![
1146 FlagDef {
1147 name: "--profile".to_string(),
1148 flag_type: "string".to_string(),
1149 required: false,
1150 description: "Profile name".to_string(),
1151 default: Some("default".to_string()),
1152 },
1153 FlagDef {
1154 name: "--force".to_string(),
1155 flag_type: "boolean".to_string(),
1156 required: false,
1157 description: "Force cache update".to_string(),
1158 default: None,
1159 },
1160 ],
1161 examples: vec![ExampleDef {
1162 description: "Update user cache".to_string(),
1163 command: "slack-rs users cache-update".to_string(),
1164 }],
1165 exit_codes: vec![
1166 ExitCodeDef {
1167 code: 0,
1168 description: "Success".to_string(),
1169 },
1170 ExitCodeDef {
1171 code: 1,
1172 description: "Cache update failed".to_string(),
1173 },
1174 ],
1175 },
1176 CommandDef {
1178 name: "users resolve-mentions".to_string(),
1179 description: "Resolve user mentions in text".to_string(),
1180 usage: "slack-rs users resolve-mentions <text> [flags]".to_string(),
1181 flags: vec![
1182 FlagDef {
1183 name: "--profile".to_string(),
1184 flag_type: "string".to_string(),
1185 required: false,
1186 description: "Profile name".to_string(),
1187 default: Some("default".to_string()),
1188 },
1189 FlagDef {
1190 name: "--format".to_string(),
1191 flag_type: "string".to_string(),
1192 required: false,
1193 description: "Output format".to_string(),
1194 default: None,
1195 },
1196 ],
1197 examples: vec![ExampleDef {
1198 description: "Resolve mentions".to_string(),
1199 command: "slack-rs users resolve-mentions '@john said hello'".to_string(),
1200 }],
1201 exit_codes: vec![
1202 ExitCodeDef {
1203 code: 0,
1204 description: "Success".to_string(),
1205 },
1206 ExitCodeDef {
1207 code: 1,
1208 description: "Resolution failed".to_string(),
1209 },
1210 ],
1211 },
1212 CommandDef {
1214 name: "commands".to_string(),
1215 description: "List all available commands in machine-readable format".to_string(),
1216 usage: "slack-rs commands --json".to_string(),
1217 flags: vec![FlagDef {
1218 name: "--json".to_string(),
1219 flag_type: "boolean".to_string(),
1220 required: true,
1221 description: "Output in JSON format".to_string(),
1222 default: None,
1223 }],
1224 examples: vec![ExampleDef {
1225 description: "List commands".to_string(),
1226 command: "slack-rs commands --json".to_string(),
1227 }],
1228 exit_codes: vec![
1229 ExitCodeDef {
1230 code: 0,
1231 description: "Success".to_string(),
1232 },
1233 ExitCodeDef {
1234 code: 1,
1235 description: "Command failed".to_string(),
1236 },
1237 ],
1238 },
1239 CommandDef {
1241 name: "schema".to_string(),
1242 description: "Show output schema for a command".to_string(),
1243 usage: "slack-rs schema --command <cmd> --output json-schema".to_string(),
1244 flags: vec![
1245 FlagDef {
1246 name: "--command".to_string(),
1247 flag_type: "string".to_string(),
1248 required: true,
1249 description: "Command name".to_string(),
1250 default: None,
1251 },
1252 FlagDef {
1253 name: "--output".to_string(),
1254 flag_type: "string".to_string(),
1255 required: true,
1256 description: "Output format (json-schema)".to_string(),
1257 default: None,
1258 },
1259 ],
1260 examples: vec![ExampleDef {
1261 description: "Show schema".to_string(),
1262 command: "slack-rs schema --command conv.list --output json-schema".to_string(),
1263 }],
1264 exit_codes: vec![
1265 ExitCodeDef {
1266 code: 0,
1267 description: "Success".to_string(),
1268 },
1269 ExitCodeDef {
1270 code: 1,
1271 description: "Schema generation failed".to_string(),
1272 },
1273 ],
1274 },
1275 CommandDef {
1277 name: "install-skills".to_string(),
1278 description: "Install agent skill from embedded or local source".to_string(),
1279 usage: "slack-rs install-skills [source] [--global]".to_string(),
1280 flags: vec![
1281 FlagDef {
1282 name: "source".to_string(),
1283 flag_type: "string".to_string(),
1284 required: false,
1285 description: "Source to install from: 'self' (embedded) or 'local:<path>'".to_string(),
1286 default: Some("self".to_string()),
1287 },
1288 FlagDef {
1289 name: "--global".to_string(),
1290 flag_type: "boolean".to_string(),
1291 required: false,
1292 description: "Install to ~/.agents instead of ./.agents".to_string(),
1293 default: Some("false".to_string()),
1294 },
1295 ],
1296 examples: vec![
1297 ExampleDef {
1298 description: "Install embedded skill (default)".to_string(),
1299 command: "slack-rs install-skills".to_string(),
1300 },
1301 ExampleDef {
1302 description: "Install from local path".to_string(),
1303 command: "slack-rs install-skills local:/path/to/skill".to_string(),
1304 },
1305 ExampleDef {
1306 description: "Install globally to ~/.agents".to_string(),
1307 command: "slack-rs install-skills --global".to_string(),
1308 },
1309 ],
1310 exit_codes: vec![
1311 ExitCodeDef {
1312 code: 0,
1313 description: "Success - skill installed".to_string(),
1314 },
1315 ExitCodeDef {
1316 code: 1,
1317 description: "Failure - installation error".to_string(),
1318 },
1319 ],
1320 },
1321 CommandDef {
1323 name: "demo".to_string(),
1324 description: "Run demonstration".to_string(),
1325 usage: "slack-rs demo".to_string(),
1326 flags: vec![],
1327 examples: vec![ExampleDef {
1328 description: "Run demo".to_string(),
1329 command: "slack-rs demo".to_string(),
1330 }],
1331 exit_codes: vec![
1332 ExitCodeDef {
1333 code: 0,
1334 description: "Success".to_string(),
1335 },
1336 ],
1337 },
1338 ]
1339}
1340
1341fn normalize_command_name(name: &str) -> String {
1343 name.replace('.', " ")
1345}
1346
1347pub fn get_command_definition(command_name: &str) -> Option<CommandDef> {
1350 let normalized = normalize_command_name(command_name);
1351 get_command_definitions()
1352 .into_iter()
1353 .find(|cmd| cmd.name == normalized)
1354}
1355
1356pub fn generate_commands_list() -> CommandsListResponse {
1358 CommandsListResponse {
1359 schema_version: 1,
1360 response_type: "commands.list".to_string(),
1361 ok: true,
1362 commands: get_command_definitions(),
1363 }
1364}
1365
1366pub fn generate_help(command_name: &str) -> Result<HelpResponse, String> {
1368 let cmd = get_command_definition(command_name)
1369 .ok_or_else(|| format!("Command '{}' not found", command_name))?;
1370
1371 Ok(HelpResponse {
1372 schema_version: 1,
1373 response_type: "help".to_string(),
1374 ok: true,
1375 command: cmd.name.clone(),
1376 usage: cmd.usage.clone(),
1377 flags: cmd.flags.clone(),
1378 examples: cmd.examples.clone(),
1379 exit_codes: cmd.exit_codes.clone(),
1380 })
1381}
1382
1383pub fn generate_schema(command_name: &str) -> Result<SchemaResponse, String> {
1385 let _cmd = get_command_definition(command_name)
1387 .ok_or_else(|| format!("Command '{}' not found", command_name))?;
1388
1389 let schema = if command_name == "install-skills" {
1391 serde_json::json!({
1392 "$schema": "http://json-schema.org/draft-07/schema#",
1393 "type": "object",
1394 "properties": {
1395 "schemaVersion": {
1396 "type": "string",
1397 "description": "Schema version number"
1398 },
1399 "type": {
1400 "type": "string",
1401 "description": "Response type identifier",
1402 "const": "skill-installation"
1403 },
1404 "ok": {
1405 "type": "boolean",
1406 "description": "Indicates if the operation was successful"
1407 },
1408 "skills": {
1409 "type": "array",
1410 "description": "List of installed skills",
1411 "items": {
1412 "type": "object",
1413 "properties": {
1414 "name": {
1415 "type": "string",
1416 "description": "Skill name"
1417 },
1418 "path": {
1419 "type": "string",
1420 "description": "Installation path"
1421 },
1422 "source_type": {
1423 "type": "string",
1424 "description": "Source type (self or local)"
1425 }
1426 },
1427 "required": ["name", "path", "source_type"]
1428 }
1429 }
1430 },
1431 "required": ["schemaVersion", "type", "ok", "skills"]
1432 })
1433 } else {
1434 serde_json::json!({
1436 "$schema": "http://json-schema.org/draft-07/schema#",
1437 "type": "object",
1438 "properties": {
1439 "schemaVersion": {
1440 "type": "integer",
1441 "description": "Schema version number"
1442 },
1443 "type": {
1444 "type": "string",
1445 "description": "Response type identifier"
1446 },
1447 "ok": {
1448 "type": "boolean",
1449 "description": "Indicates if the operation was successful"
1450 },
1451 "response": {
1452 "type": "object",
1453 "description": "Slack API response data"
1454 },
1455 "meta": {
1456 "type": "object",
1457 "description": "Metadata about the request and profile",
1458 "properties": {
1459 "profile": {"type": "string"},
1460 "team_id": {"type": "string"},
1461 "user_id": {"type": "string"},
1462 "method": {"type": "string"},
1463 "command": {"type": "string"}
1464 }
1465 }
1466 },
1467 "required": ["schemaVersion", "type", "ok"]
1468 })
1469 };
1470
1471 Ok(SchemaResponse {
1472 schema_version: 1,
1473 response_type: "schema".to_string(),
1474 ok: true,
1475 command: command_name.to_string(),
1476 schema,
1477 })
1478}
1479
1480#[cfg(test)]
1481mod tests {
1482 use super::*;
1483
1484 #[test]
1485 fn test_get_command_definitions() {
1486 let commands = get_command_definitions();
1487 assert!(!commands.is_empty());
1488 assert!(commands.iter().any(|c| c.name == "api call"));
1489 assert!(commands.iter().any(|c| c.name == "conv list"));
1490 }
1491
1492 #[test]
1493 fn test_get_command_definition() {
1494 let cmd = get_command_definition("conv list");
1495 assert!(cmd.is_some());
1496 let cmd = cmd.unwrap();
1497 assert_eq!(cmd.name, "conv list");
1498 assert!(!cmd.flags.is_empty());
1499 }
1500
1501 #[test]
1502 fn test_generate_commands_list() {
1503 let response = generate_commands_list();
1504 assert_eq!(response.schema_version, 1);
1505 assert_eq!(response.response_type, "commands.list");
1506 assert!(response.ok);
1507 assert!(!response.commands.is_empty());
1508 }
1509
1510 #[test]
1511 fn test_generate_help() {
1512 let help = generate_help("conv list");
1513 assert!(help.is_ok());
1514 let help = help.unwrap();
1515 assert_eq!(help.schema_version, 1);
1516 assert_eq!(help.response_type, "help");
1517 assert!(help.ok);
1518 assert_eq!(help.command, "conv list");
1519 }
1520
1521 #[test]
1522 fn test_generate_help_unknown_command() {
1523 let help = generate_help("unknown command");
1524 assert!(help.is_err());
1525 }
1526
1527 #[test]
1528 fn test_generate_schema() {
1529 let schema = generate_schema("conv list");
1530 assert!(schema.is_ok());
1531 let schema = schema.unwrap();
1532 assert_eq!(schema.schema_version, 1);
1533 assert_eq!(schema.response_type, "schema");
1534 assert!(schema.ok);
1535 assert_eq!(schema.command, "conv list");
1536 }
1537
1538 #[test]
1539 fn test_commands_list_json_serialization() {
1540 let response = generate_commands_list();
1541 let json = serde_json::to_string(&response);
1542 assert!(json.is_ok());
1543
1544 let parsed: Result<CommandsListResponse, _> = serde_json::from_str(&json.unwrap());
1546 assert!(parsed.is_ok());
1547 }
1548
1549 #[test]
1550 fn test_help_json_serialization() {
1551 let help = generate_help("conv list").unwrap();
1552 let json = serde_json::to_string(&help);
1553 assert!(json.is_ok());
1554
1555 let parsed: Result<HelpResponse, _> = serde_json::from_str(&json.unwrap());
1557 assert!(parsed.is_ok());
1558 }
1559
1560 #[test]
1561 fn test_schema_json_serialization() {
1562 let schema = generate_schema("conv list").unwrap();
1563 let json = serde_json::to_string(&schema);
1564 assert!(json.is_ok());
1565
1566 let parsed: Result<SchemaResponse, _> = serde_json::from_str(&json.unwrap());
1568 assert!(parsed.is_ok());
1569 }
1570}