1use crate::error::{CliError, Result};
4use colored::Colorize;
5use miyabi_agents::business::{
6 AIEntrepreneurAgent, AnalyticsAgent, CRMAgent, ContentCreationAgent, FunnelDesignAgent,
7 MarketResearchAgent, MarketingAgent, PersonaAgent, ProductConceptAgent, ProductDesignAgent,
8 SNSStrategyAgent, SalesAgent, SelfAnalysisAgent, YouTubeAgent,
9};
10use miyabi_agents::codegen::CodeGenAgent;
11use miyabi_agents::deployment::DeploymentAgent;
12use miyabi_agents::hooks::{AuditLogHook, EnvironmentCheckHook, HookedAgent, MetricsHook};
13use miyabi_agents::issue::IssueAgent;
14use miyabi_agents::pr::PRAgent;
15use miyabi_agents::review::ReviewAgent;
16use miyabi_agents::BaseAgent;
17use miyabi_agents::CoordinatorAgentWithLLM;
18use miyabi_core::git_utils::{find_git_root, get_current_branch};
19use miyabi_types::task::TaskType;
20use miyabi_types::{AgentConfig, AgentType, Task};
21use std::collections::HashMap;
22use std::path::PathBuf;
23
24pub struct AgentCommand {
25 pub agent_type: String,
26 pub issue: Option<u64>,
27}
28
29impl AgentCommand {
30 pub fn new(agent_type: String, issue: Option<u64>) -> Self {
31 Self { agent_type, issue }
32 }
33
34 pub async fn execute(&self) -> Result<()> {
35 println!(
36 "{}",
37 format!("🤖 Running {} agent...", self.agent_type)
38 .cyan()
39 .bold()
40 );
41
42 let agent_type = self.parse_agent_type()?;
44
45 let config = self.load_config()?;
47
48 match agent_type {
50 AgentType::CoordinatorAgent => {
52 self.run_coordinator_agent(config).await?;
53 }
54 AgentType::CodeGenAgent => {
55 self.run_codegen_agent(config).await?;
56 }
57 AgentType::ReviewAgent => {
58 self.run_review_agent(config).await?;
59 }
60 AgentType::IssueAgent => {
61 self.run_issue_agent(config).await?;
62 }
63 AgentType::PRAgent => {
64 self.run_pr_agent(config).await?;
65 }
66 AgentType::DeploymentAgent => {
67 self.run_deployment_agent(config).await?;
68 }
69
70 AgentType::AIEntrepreneurAgent => {
72 self.run_ai_entrepreneur_agent(config).await?;
73 }
74 AgentType::ProductConceptAgent => {
75 self.run_product_concept_agent(config).await?;
76 }
77 AgentType::ProductDesignAgent => {
78 self.run_product_design_agent(config).await?;
79 }
80 AgentType::FunnelDesignAgent => {
81 self.run_funnel_design_agent(config).await?;
82 }
83 AgentType::PersonaAgent => {
84 self.run_persona_agent(config).await?;
85 }
86 AgentType::SelfAnalysisAgent => {
87 self.run_self_analysis_agent(config).await?;
88 }
89
90 AgentType::MarketResearchAgent => {
92 self.run_market_research_agent(config).await?;
93 }
94 AgentType::MarketingAgent => {
95 self.run_marketing_agent(config).await?;
96 }
97 AgentType::ContentCreationAgent => {
98 self.run_content_creation_agent(config).await?;
99 }
100 AgentType::SNSStrategyAgent => {
101 self.run_sns_strategy_agent(config).await?;
102 }
103 AgentType::YouTubeAgent => {
104 self.run_youtube_agent(config).await?;
105 }
106
107 AgentType::SalesAgent => {
109 self.run_sales_agent(config).await?;
110 }
111 AgentType::CRMAgent => {
112 self.run_crm_agent(config).await?;
113 }
114 AgentType::AnalyticsAgent => {
115 self.run_analytics_agent(config).await?;
116 }
117
118 _ => {
119 println!(
120 "{}",
121 format!("Agent type {:?} not yet implemented", agent_type).yellow()
122 );
123 }
124 }
125
126 println!();
127 println!("{}", "✅ Agent completed successfully!".green().bold());
128
129 Ok(())
130 }
131
132 pub fn parse_agent_type(&self) -> Result<AgentType> {
133 match self.agent_type.to_lowercase().as_str() {
134 "coordinator" => Ok(AgentType::CoordinatorAgent),
136 "codegen" | "code-gen" => Ok(AgentType::CodeGenAgent),
137 "review" => Ok(AgentType::ReviewAgent),
138 "issue" => Ok(AgentType::IssueAgent),
139 "pr" => Ok(AgentType::PRAgent),
140 "deployment" | "deploy" => Ok(AgentType::DeploymentAgent),
141
142 "ai-entrepreneur" | "entrepreneur" => Ok(AgentType::AIEntrepreneurAgent),
144 "product-concept" | "concept" => Ok(AgentType::ProductConceptAgent),
145 "product-design" | "design" => Ok(AgentType::ProductDesignAgent),
146 "funnel-design" | "funnel" => Ok(AgentType::FunnelDesignAgent),
147 "persona" => Ok(AgentType::PersonaAgent),
148 "self-analysis" | "analysis" => Ok(AgentType::SelfAnalysisAgent),
149
150 "market-research" | "research" => Ok(AgentType::MarketResearchAgent),
152 "marketing" => Ok(AgentType::MarketingAgent),
153 "content-creation" | "content" => Ok(AgentType::ContentCreationAgent),
154 "sns-strategy" | "sns" => Ok(AgentType::SNSStrategyAgent),
155 "youtube" => Ok(AgentType::YouTubeAgent),
156
157 "sales" => Ok(AgentType::SalesAgent),
159 "crm" => Ok(AgentType::CRMAgent),
160 "analytics" => Ok(AgentType::AnalyticsAgent),
161
162 _ => Err(CliError::InvalidAgentType(self.agent_type.clone())),
163 }
164 }
165
166 fn load_config(&self) -> Result<AgentConfig> {
167 let github_token = self.get_github_token()?;
169
170 let device_identifier = std::env::var("DEVICE_IDENTIFIER")
172 .unwrap_or_else(|_| hostname::get().unwrap().to_string_lossy().to_string());
173
174 let (repo_owner, repo_name) = self.parse_git_remote()?;
176
177 Ok(AgentConfig {
179 device_identifier,
180 github_token,
181 repo_owner: Some(repo_owner),
182 repo_name: Some(repo_name),
183 use_task_tool: false,
184 use_worktree: true,
185 worktree_base_path: Some(".worktrees".to_string()),
186 log_directory: "./logs".to_string(),
187 report_directory: "./reports".to_string(),
188 tech_lead_github_username: None,
189 ciso_github_username: None,
190 po_github_username: None,
191 firebase_production_project: None,
192 firebase_staging_project: None,
193 production_url: None,
194 staging_url: None,
195 })
196 }
197
198 fn git_root(&self) -> Result<PathBuf> {
199 find_git_root(None).map_err(CliError::GitConfig)
200 }
201
202 fn get_github_token(&self) -> Result<String> {
213 if let Ok(token) = std::env::var("GITHUB_TOKEN") {
215 if !token.trim().is_empty() {
216 return Ok(token.trim().to_string());
217 }
218 }
219
220 if let Ok(output) = std::process::Command::new("gh")
222 .args(["auth", "token"])
223 .output()
224 {
225 if output.status.success() {
226 let token = String::from_utf8_lossy(&output.stdout).trim().to_string();
227 if !token.is_empty()
228 && (token.starts_with("ghp_")
229 || token.starts_with("gho_")
230 || token.starts_with("ghu_")
231 || token.starts_with("ghs_")
232 || token.starts_with("ghr_"))
233 {
234 return Ok(token);
235 }
236 }
237 }
238
239 Err(CliError::GitConfig(
241 "GitHub token not found. Please set up authentication:\n\n\
242 Option 1: Set environment variable\n\
243 export GITHUB_TOKEN=ghp_xxx\n\n\
244 Option 2: Authenticate with gh CLI\n\
245 gh auth login\n\n\
246 Option 3: Add to .env file (uncomment the GITHUB_TOKEN line)\n\
247 GITHUB_TOKEN=ghp_xxx"
248 .to_string(),
249 ))
250 }
251
252 fn parse_git_remote(&self) -> Result<(String, String)> {
259 let output = std::process::Command::new("git")
261 .args(["remote", "get-url", "origin"])
262 .output()
263 .map_err(|e| CliError::GitConfig(format!("Failed to run git command: {}", e)))?;
264
265 if !output.status.success() {
266 return Err(CliError::GitConfig(
267 "Failed to get git remote URL. Not a git repository?".to_string(),
268 ));
269 }
270
271 let remote_url = String::from_utf8_lossy(&output.stdout).trim().to_string();
272
273 if remote_url.starts_with("http") && remote_url.contains("github.com/") {
275 let parts: Vec<&str> = remote_url
276 .split("github.com/")
277 .nth(1)
278 .ok_or_else(|| CliError::GitConfig("Invalid GitHub URL".to_string()))?
279 .trim_end_matches(".git")
280 .split('/')
281 .collect();
282
283 if parts.len() >= 2 {
284 return Ok((parts[0].to_string(), parts[1].to_string()));
285 }
286 }
287
288 if remote_url.starts_with("git@github.com:") {
290 let repo_part = remote_url
291 .strip_prefix("git@github.com:")
292 .ok_or_else(|| CliError::GitConfig("Invalid SSH URL".to_string()))?
293 .trim_end_matches(".git");
294
295 let parts: Vec<&str> = repo_part.split('/').collect();
296 if parts.len() >= 2 {
297 return Ok((parts[0].to_string(), parts[1].to_string()));
298 }
299 }
300
301 Err(CliError::GitConfig(format!(
302 "Could not parse GitHub owner/repo from remote URL: {}",
303 remote_url
304 )))
305 }
306
307 fn register_standard_hooks<A: BaseAgent>(&self, agent: &mut HookedAgent<A>, config: &AgentConfig) {
320 agent.register_hook(MetricsHook::new());
321 agent.register_hook(EnvironmentCheckHook::new(["GITHUB_TOKEN"]));
322 agent.register_hook(AuditLogHook::new(config.log_directory.clone()));
323 }
324
325 async fn run_coordinator_agent(&self, config: AgentConfig) -> Result<()> {
326 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
327
328 println!(" Issue: #{}", issue_number);
329 println!(" Type: CoordinatorAgent with LLM (Task decomposition & DAG)");
330 println!();
331
332 let mut agent = HookedAgent::new(CoordinatorAgentWithLLM::new(config.clone()));
334 self.register_standard_hooks(&mut agent, &config);
335
336 let task = Task {
338 id: format!("coordinator-issue-{}", issue_number),
339 title: format!("Coordinate Issue #{}", issue_number),
340 description: format!("Decompose Issue #{} into executable tasks", issue_number),
341 task_type: miyabi_types::task::TaskType::Feature,
342 priority: 1,
343 severity: None,
344 impact: None,
345 assigned_agent: Some(AgentType::CoordinatorAgent),
346 dependencies: vec![],
347 estimated_duration: Some(5),
348 status: None,
349 start_time: None,
350 end_time: None,
351 metadata: Some(HashMap::from([(
352 "issue_number".to_string(),
353 serde_json::json!(issue_number),
354 )])),
355 };
356
357 println!("{}", " Executing...".dimmed());
359 let result = agent.execute(&task).await?;
360
361 println!();
363 println!(" Results:");
364 println!(" Status: {:?}", result.status);
365
366 if let Some(metrics) = result.metrics {
367 println!(" Duration: {}ms", metrics.duration_ms);
368 }
369
370 if let Some(data) = result.data {
371 println!(" Data: {}", serde_json::to_string_pretty(&data)?);
372 }
373
374 Ok(())
375 }
376
377 async fn run_codegen_agent(&self, config: AgentConfig) -> Result<()> {
378 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
379
380 println!(" Issue: #{}", issue_number);
381 println!(" Type: CodeGenAgent (Code generation)");
382 println!();
383
384 let mut agent = HookedAgent::new(CodeGenAgent::new(config.clone()));
386 self.register_standard_hooks(&mut agent, &config);
387
388 let task = Task {
390 id: format!("codegen-issue-{}", issue_number),
391 title: format!("Generate code for Issue #{}", issue_number),
392 description: format!("Implement solution for Issue #{}", issue_number),
393 task_type: miyabi_types::task::TaskType::Feature,
394 priority: 1,
395 severity: None,
396 impact: None,
397 assigned_agent: Some(AgentType::CodeGenAgent),
398 dependencies: vec![],
399 estimated_duration: Some(30),
400 status: None,
401 start_time: None,
402 end_time: None,
403 metadata: Some(HashMap::from([(
404 "issue_number".to_string(),
405 serde_json::json!(issue_number),
406 )])),
407 };
408
409 println!("{}", " Executing...".dimmed());
411 let result = agent.execute(&task).await?;
412
413 println!();
415 println!(" Results:");
416 println!(" Status: {:?}", result.status);
417
418 if let Some(metrics) = result.metrics {
419 println!(" Duration: {}ms", metrics.duration_ms);
420 if let Some(lines_changed) = metrics.lines_changed {
421 println!(" Lines changed: {}", lines_changed);
422 }
423 if let Some(tests_added) = metrics.tests_added {
424 println!(" Tests added: {}", tests_added);
425 }
426 }
427
428 Ok(())
429 }
430
431 async fn run_review_agent(&self, config: AgentConfig) -> Result<()> {
432 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
433
434 println!(" Issue: #{}", issue_number);
435 println!(" Type: ReviewAgent (Code quality review)");
436 println!();
437
438 let mut agent = HookedAgent::new(ReviewAgent::new(config.clone()));
439 self.register_standard_hooks(&mut agent, &config);
440
441 let task = Task {
442 id: format!("review-issue-{}", issue_number),
443 title: format!("Review implementation for Issue #{}", issue_number),
444 description: format!(
445 "Run lint, tests, security checks for Issue #{}",
446 issue_number
447 ),
448 task_type: TaskType::Refactor,
449 priority: 1,
450 severity: None,
451 impact: None,
452 assigned_agent: Some(AgentType::ReviewAgent),
453 dependencies: vec![],
454 estimated_duration: Some(15),
455 status: None,
456 start_time: None,
457 end_time: None,
458 metadata: Some(HashMap::from([(
459 "issue_number".to_string(),
460 serde_json::json!(issue_number),
461 )])),
462 };
463
464 println!("{}", " Executing...".dimmed());
465 let result = agent.execute(&task).await?;
466
467 println!();
468 println!(" Results:");
469 println!(" Status: {:?}", result.status);
470
471 if let Some(ref metrics) = result.metrics {
472 println!(" Duration: {}ms", metrics.duration_ms);
473 if let Some(score) = metrics.quality_score {
474 println!(" Quality Score: {}", score);
475 }
476 }
477
478 if let Some(ref data) = result.data {
479 println!(" Data: {}", serde_json::to_string_pretty(data)?);
480 }
481
482 Ok(())
483 }
484
485 async fn run_issue_agent(&self, config: AgentConfig) -> Result<()> {
486 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
487
488 println!(" Issue: #{}", issue_number);
489 println!(" Type: IssueAgent (Issue analysis & labeling)");
490 println!();
491
492 let mut agent = HookedAgent::new(IssueAgent::new(config.clone()));
494 self.register_standard_hooks(&mut agent, &config);
495
496 let task = Task {
498 id: format!("issue-analysis-{}", issue_number),
499 title: format!("Analyze Issue #{}", issue_number),
500 description: format!("Classify Issue #{} and apply labels", issue_number),
501 task_type: miyabi_types::task::TaskType::Feature,
502 priority: 1,
503 severity: None,
504 impact: None,
505 assigned_agent: Some(AgentType::IssueAgent),
506 dependencies: vec![],
507 estimated_duration: Some(5),
508 status: None,
509 start_time: None,
510 end_time: None,
511 metadata: Some(HashMap::from([(
512 "issue_number".to_string(),
513 serde_json::json!(issue_number),
514 )])),
515 };
516
517 println!("{}", " Executing...".dimmed());
519 let result = agent.execute(&task).await?;
520
521 println!();
523 println!(" Results:");
524 println!(" Status: {:?}", result.status);
525
526 if let Some(metrics) = result.metrics {
527 println!(" Duration: {}ms", metrics.duration_ms);
528 }
529
530 if let Some(data) = result.data {
531 if let Ok(analysis) = serde_json::from_value::<miyabi_types::IssueAnalysis>(data) {
533 println!(" Analysis:");
534 println!(" Issue Type: {:?}", analysis.issue_type);
535 println!(" Severity: {:?}", analysis.severity);
536 println!(" Impact: {:?}", analysis.impact);
537 println!(" Assigned Agent: {:?}", analysis.assigned_agent);
538 println!(
539 " Estimated Duration: {} minutes",
540 analysis.estimated_duration
541 );
542 println!(" Dependencies: {}", analysis.dependencies.join(", "));
543 println!(" Applied Labels: {}", analysis.labels.join(", "));
544 }
545 }
546
547 Ok(())
548 }
549
550 async fn run_pr_agent(&self, config: AgentConfig) -> Result<()> {
551 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
552
553 println!(" Issue: #{}", issue_number);
554 println!(" Type: PRAgent (Pull Request creation)");
555 println!();
556
557 let repo_root = self.git_root()?;
558 let branch =
559 get_current_branch(&repo_root).map_err(|e| CliError::GitConfig(e.to_string()))?;
560 let base_branch =
561 std::env::var("MIYABI_BASE_BRANCH").unwrap_or_else(|_| "main".to_string());
562
563 let mut metadata = HashMap::new();
564 metadata.insert("issueNumber".to_string(), serde_json::json!(issue_number));
565 metadata.insert("branch".to_string(), serde_json::json!(branch.clone()));
566 metadata.insert(
567 "baseBranch".to_string(),
568 serde_json::json!(base_branch.clone()),
569 );
570
571 let mut agent = HookedAgent::new(PRAgent::new(config.clone()));
572 self.register_standard_hooks(&mut agent, &config);
573
574 let task = Task {
575 id: format!("pr-issue-{}", issue_number),
576 title: format!("Create PR for Issue #{}", issue_number),
577 description: format!("Generate pull request for Issue #{}", issue_number),
578 task_type: TaskType::Feature,
579 priority: 1,
580 severity: None,
581 impact: None,
582 assigned_agent: Some(AgentType::PRAgent),
583 dependencies: vec![],
584 estimated_duration: Some(5),
585 status: None,
586 start_time: None,
587 end_time: None,
588 metadata: Some(metadata),
589 };
590
591 println!("{}", " Executing...".dimmed());
592 let result = agent.execute(&task).await?;
593
594 println!();
595 println!(" Results:");
596 println!(" Status: {:?}", result.status);
597
598 if let Some(ref data) = result.data {
599 println!(" Data: {}", serde_json::to_string_pretty(data)?);
600 }
601
602 println!(" Branch: {}", branch);
603 println!(" Base Branch: {}", base_branch);
604
605 Ok(())
606 }
607
608 async fn run_deployment_agent(&self, config: AgentConfig) -> Result<()> {
609 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
610
611 println!(" Issue: #{}", issue_number);
612 println!(" Type: DeploymentAgent (Build/Test/Deploy)");
613 println!();
614
615 let environment =
616 std::env::var("MIYABI_DEPLOY_ENV").unwrap_or_else(|_| "staging".to_string());
617
618 let health_url = match environment.as_str() {
619 "production" => config
620 .production_url
621 .clone()
622 .or_else(|| std::env::var("MIYABI_PRODUCTION_HEALTH_URL").ok()),
623 _ => config
624 .staging_url
625 .clone()
626 .or_else(|| std::env::var("MIYABI_STAGING_HEALTH_URL").ok()),
627 };
628
629 let mut metadata = HashMap::new();
630 metadata.insert("issue_number".to_string(), serde_json::json!(issue_number));
631 metadata.insert(
632 "environment".to_string(),
633 serde_json::json!(environment.clone()),
634 );
635 if let Some(url) = health_url {
636 metadata.insert("health_url".to_string(), serde_json::json!(url));
637 }
638
639 let mut agent = HookedAgent::new(DeploymentAgent::new(config.clone()));
640 self.register_standard_hooks(&mut agent, &config);
641
642 let task = Task {
643 id: format!("deployment-issue-{}", issue_number),
644 title: format!("Deploy changes for Issue #{}", issue_number),
645 description: format!(
646 "Build, test, and deploy code associated with Issue #{}",
647 issue_number
648 ),
649 task_type: TaskType::Deployment,
650 priority: 1,
651 severity: None,
652 impact: None,
653 assigned_agent: Some(AgentType::DeploymentAgent),
654 dependencies: vec![],
655 estimated_duration: Some(30),
656 status: None,
657 start_time: None,
658 end_time: None,
659 metadata: Some(metadata),
660 };
661
662 println!("{}", " Executing...".dimmed());
663 let result = agent.execute(&task).await?;
664
665 println!();
666 println!(" Results:");
667 println!(" Status: {:?}", result.status);
668
669 if let Some(ref metrics) = result.metrics {
670 println!(" Duration: {}ms", metrics.duration_ms);
671 }
672
673 if let Some(ref data) = result.data {
674 println!(" Data: {}", serde_json::to_string_pretty(data)?);
675 }
676
677 if let Some(ref escalation) = result.escalation {
678 println!(
679 " Escalation: {:?} ({})",
680 escalation.target, escalation.reason
681 );
682 }
683
684 Ok(())
685 }
686
687 async fn run_ai_entrepreneur_agent(&self, config: AgentConfig) -> Result<()> {
689 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
690
691 println!(" Issue: #{}", issue_number);
692 println!(" Type: AIEntrepreneurAgent (8-phase business plan generation)");
693 println!();
694
695 let agent = AIEntrepreneurAgent::new(config);
696 let task = self.create_business_task(
697 issue_number,
698 "AI Entrepreneur Business Plan",
699 "Generate comprehensive 8-phase business plan",
700 );
701
702 println!("{}", " Executing...".dimmed());
703 let result = agent.execute(&task).await?;
704 self.display_business_result(result)?;
705
706 Ok(())
707 }
708
709 async fn run_product_concept_agent(&self, config: AgentConfig) -> Result<()> {
710 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
711
712 println!(" Issue: #{}", issue_number);
713 println!(" Type: ProductConceptAgent (MVP design & product strategy)");
714 println!();
715
716 let agent = ProductConceptAgent::new(config);
717 let task = self.create_business_task(
718 issue_number,
719 "Product Concept Design",
720 "Design MVP and product strategy",
721 );
722
723 println!("{}", " Executing...".dimmed());
724 let result = agent.execute(&task).await?;
725 self.display_business_result(result)?;
726
727 Ok(())
728 }
729
730 async fn run_product_design_agent(&self, config: AgentConfig) -> Result<()> {
731 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
732
733 println!(" Issue: #{}", issue_number);
734 println!(" Type: ProductDesignAgent (Comprehensive product design)");
735 println!();
736
737 let agent = ProductDesignAgent::new(config);
738 let task = self.create_business_task(
739 issue_number,
740 "Product Design Specification",
741 "Create comprehensive product design and technical specification",
742 );
743
744 println!("{}", " Executing...".dimmed());
745 let result = agent.execute(&task).await?;
746 self.display_business_result(result)?;
747
748 Ok(())
749 }
750
751 async fn run_funnel_design_agent(&self, config: AgentConfig) -> Result<()> {
752 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
753
754 println!(" Issue: #{}", issue_number);
755 println!(" Type: FunnelDesignAgent (AARRR metrics & conversion optimization)");
756 println!();
757
758 let agent = FunnelDesignAgent::new(config);
759 let task = self.create_business_task(
760 issue_number,
761 "Funnel Design Strategy",
762 "Design AARRR metrics and conversion optimization",
763 );
764
765 println!("{}", " Executing...".dimmed());
766 let result = agent.execute(&task).await?;
767 self.display_business_result(result)?;
768
769 Ok(())
770 }
771
772 async fn run_persona_agent(&self, config: AgentConfig) -> Result<()> {
773 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
774
775 println!(" Issue: #{}", issue_number);
776 println!(" Type: PersonaAgent (Customer persona & segment analysis)");
777 println!();
778
779 let agent = PersonaAgent::new(config);
780 let task = self.create_business_task(
781 issue_number,
782 "Customer Persona Analysis",
783 "Analyze customer personas and segments",
784 );
785
786 println!("{}", " Executing...".dimmed());
787 let result = agent.execute(&task).await?;
788 self.display_business_result(result)?;
789
790 Ok(())
791 }
792
793 async fn run_self_analysis_agent(&self, config: AgentConfig) -> Result<()> {
794 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
795
796 println!(" Issue: #{}", issue_number);
797 println!(" Type: SelfAnalysisAgent (Self-assessment & business strategy)");
798 println!();
799
800 let agent = SelfAnalysisAgent::new(config);
801 let task = self.create_business_task(
802 issue_number,
803 "Self Analysis Strategy",
804 "Perform self-assessment and business strategy formulation",
805 );
806
807 println!("{}", " Executing...".dimmed());
808 let result = agent.execute(&task).await?;
809 self.display_business_result(result)?;
810
811 Ok(())
812 }
813
814 async fn run_market_research_agent(&self, config: AgentConfig) -> Result<()> {
815 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
816
817 println!(" Issue: #{}", issue_number);
818 println!(" Type: MarketResearchAgent (Market analysis & competitive landscape)");
819 println!();
820
821 let agent = MarketResearchAgent::new(config);
822 let task = self.create_business_task(
823 issue_number,
824 "Market Research Analysis",
825 "Conduct market research and competitive analysis",
826 );
827
828 println!("{}", " Executing...".dimmed());
829 let result = agent.execute(&task).await?;
830 self.display_business_result(result)?;
831
832 Ok(())
833 }
834
835 async fn run_marketing_agent(&self, config: AgentConfig) -> Result<()> {
836 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
837
838 println!(" Issue: #{}", issue_number);
839 println!(" Type: MarketingAgent (Marketing strategy & campaign planning)");
840 println!();
841
842 let agent = MarketingAgent::new(config);
843 let task = self.create_business_task(
844 issue_number,
845 "Marketing Strategy Plan",
846 "Develop comprehensive marketing strategy and campaigns",
847 );
848
849 println!("{}", " Executing...".dimmed());
850 let result = agent.execute(&task).await?;
851 self.display_business_result(result)?;
852
853 Ok(())
854 }
855
856 async fn run_content_creation_agent(&self, config: AgentConfig) -> Result<()> {
857 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
858
859 println!(" Issue: #{}", issue_number);
860 println!(" Type: ContentCreationAgent (Content creation & blog article generation)");
861 println!();
862
863 let agent = ContentCreationAgent::new(config);
864 let task = self.create_business_task(
865 issue_number,
866 "Content Creation Strategy",
867 "Create content strategy and blog articles",
868 );
869
870 println!("{}", " Executing...".dimmed());
871 let result = agent.execute(&task).await?;
872 self.display_business_result(result)?;
873
874 Ok(())
875 }
876
877 async fn run_sns_strategy_agent(&self, config: AgentConfig) -> Result<()> {
878 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
879
880 println!(" Issue: #{}", issue_number);
881 println!(" Type: SNSStrategyAgent (Social media strategy & community management)");
882 println!();
883
884 let agent = SNSStrategyAgent::new(config);
885 let task = self.create_business_task(
886 issue_number,
887 "SNS Strategy Plan",
888 "Develop social media strategy and community management",
889 );
890
891 println!("{}", " Executing...".dimmed());
892 let result = agent.execute(&task).await?;
893 self.display_business_result(result)?;
894
895 Ok(())
896 }
897
898 async fn run_youtube_agent(&self, config: AgentConfig) -> Result<()> {
899 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
900
901 println!(" Issue: #{}", issue_number);
902 println!(" Type: YouTubeAgent (YouTube strategy & video content planning)");
903 println!();
904
905 let agent = YouTubeAgent::new(config);
906 let task = self.create_business_task(
907 issue_number,
908 "YouTube Strategy Plan",
909 "Develop YouTube strategy and video content planning",
910 );
911
912 println!("{}", " Executing...".dimmed());
913 let result = agent.execute(&task).await?;
914 self.display_business_result(result)?;
915
916 Ok(())
917 }
918
919 async fn run_sales_agent(&self, config: AgentConfig) -> Result<()> {
920 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
921
922 println!(" Issue: #{}", issue_number);
923 println!(" Type: SalesAgent (Sales strategy & process optimization)");
924 println!();
925
926 let agent = SalesAgent::new(config);
927 let task = self.create_business_task(
928 issue_number,
929 "Sales Strategy Plan",
930 "Develop sales strategy and process optimization",
931 );
932
933 println!("{}", " Executing...".dimmed());
934 let result = agent.execute(&task).await?;
935 self.display_business_result(result)?;
936
937 Ok(())
938 }
939
940 async fn run_crm_agent(&self, config: AgentConfig) -> Result<()> {
941 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
942
943 println!(" Issue: #{}", issue_number);
944 println!(" Type: CRMAgent (CRM strategy & customer relationship management)");
945 println!();
946
947 let agent = CRMAgent::new(config);
948 let task = self.create_business_task(
949 issue_number,
950 "CRM Strategy Plan",
951 "Develop CRM strategy and customer relationship management",
952 );
953
954 println!("{}", " Executing...".dimmed());
955 let result = agent.execute(&task).await?;
956 self.display_business_result(result)?;
957
958 Ok(())
959 }
960
961 async fn run_analytics_agent(&self, config: AgentConfig) -> Result<()> {
962 let issue_number = self.issue.ok_or(CliError::MissingIssueNumber)?;
963
964 println!(" Issue: #{}", issue_number);
965 println!(" Type: AnalyticsAgent (Data analytics & business intelligence)");
966 println!();
967
968 let agent = AnalyticsAgent::new(config);
969 let task = self.create_business_task(
970 issue_number,
971 "Analytics Strategy Plan",
972 "Develop analytics strategy and business intelligence",
973 );
974
975 println!("{}", " Executing...".dimmed());
976 let result = agent.execute(&task).await?;
977 self.display_business_result(result)?;
978
979 Ok(())
980 }
981
982 fn create_business_task(&self, issue_number: u64, title: &str, description: &str) -> Task {
984 Task {
985 id: format!("business-issue-{}", issue_number),
986 title: title.to_string(),
987 description: description.to_string(),
988 task_type: miyabi_types::task::TaskType::Feature,
989 priority: 1,
990 severity: None,
991 impact: None,
992 assigned_agent: None,
993 dependencies: vec![],
994 estimated_duration: Some(60), status: None,
996 start_time: None,
997 end_time: None,
998 metadata: Some(HashMap::from([(
999 "issue_number".to_string(),
1000 serde_json::json!(issue_number),
1001 )])),
1002 }
1003 }
1004
1005 fn display_business_result(&self, result: miyabi_types::AgentResult) -> Result<()> {
1006 println!();
1007 println!(" Results:");
1008 println!(" Status: {:?}", result.status);
1009
1010 if let Some(metrics) = result.metrics {
1011 println!(" Duration: {}ms", metrics.duration_ms);
1012 if let Some(quality_score) = metrics.quality_score {
1013 println!(" Quality Score: {}/100", quality_score);
1014 }
1015 }
1016
1017 if let Some(data) = result.data {
1018 println!(
1019 " Summary: {}",
1020 data.get("summary").unwrap_or(&serde_json::Value::String(
1021 "No summary available".to_string()
1022 ))
1023 );
1024 }
1025
1026 Ok(())
1027 }
1028}
1029
1030#[cfg(test)]
1031mod tests {
1032 use super::*;
1033
1034 #[test]
1035 fn test_parse_agent_type() {
1036 let cmd = AgentCommand::new("coordinator".to_string(), None);
1037 assert!(matches!(
1038 cmd.parse_agent_type().unwrap(),
1039 AgentType::CoordinatorAgent
1040 ));
1041
1042 let cmd = AgentCommand::new("codegen".to_string(), None);
1043 assert!(matches!(
1044 cmd.parse_agent_type().unwrap(),
1045 AgentType::CodeGenAgent
1046 ));
1047
1048 let cmd = AgentCommand::new("code-gen".to_string(), None);
1049 assert!(matches!(
1050 cmd.parse_agent_type().unwrap(),
1051 AgentType::CodeGenAgent
1052 ));
1053
1054 let cmd = AgentCommand::new("invalid".to_string(), None);
1055 assert!(cmd.parse_agent_type().is_err());
1056
1057 let cmd = AgentCommand::new("ai-entrepreneur".to_string(), None);
1059 assert!(matches!(
1060 cmd.parse_agent_type().unwrap(),
1061 AgentType::AIEntrepreneurAgent
1062 ));
1063
1064 let cmd = AgentCommand::new("entrepreneur".to_string(), None);
1065 assert!(matches!(
1066 cmd.parse_agent_type().unwrap(),
1067 AgentType::AIEntrepreneurAgent
1068 ));
1069
1070 let cmd = AgentCommand::new("marketing".to_string(), None);
1071 assert!(matches!(
1072 cmd.parse_agent_type().unwrap(),
1073 AgentType::MarketingAgent
1074 ));
1075
1076 let cmd = AgentCommand::new("analytics".to_string(), None);
1077 assert!(matches!(
1078 cmd.parse_agent_type().unwrap(),
1079 AgentType::AnalyticsAgent
1080 ));
1081 }
1082
1083 #[test]
1084 fn test_agent_command_creation() {
1085 let cmd = AgentCommand::new("coordinator".to_string(), Some(123));
1086 assert_eq!(cmd.agent_type, "coordinator");
1087 assert_eq!(cmd.issue, Some(123));
1088
1089 let cmd = AgentCommand::new("codegen".to_string(), None);
1090 assert_eq!(cmd.agent_type, "codegen");
1091 assert_eq!(cmd.issue, None);
1092 }
1093}