miyabi_cli/commands/
status.rs

1//! Status command - Check project status
2
3use crate::error::Result;
4use colored::Colorize;
5use std::path::Path;
6
7pub struct StatusCommand {
8    pub watch: bool,
9}
10
11impl StatusCommand {
12    pub fn new(watch: bool) -> Self {
13        Self { watch }
14    }
15
16    pub async fn execute(&self) -> Result<()> {
17        println!("{}", "📊 Project Status".cyan().bold());
18        println!();
19
20        // Check Miyabi installation
21        self.check_miyabi_installation();
22
23        // Check environment variables
24        self.check_environment();
25
26        // Check git repository status
27        self.check_git_status()?;
28
29        // Check worktrees
30        self.check_worktrees();
31
32        // Check recent activity
33        self.check_recent_activity();
34
35        if self.watch {
36            println!();
37            println!("{}", "  (Watch mode: Press Ctrl+C to exit)".dimmed());
38            // TODO: Implement watch mode with auto-refresh
39        }
40
41        Ok(())
42    }
43
44    fn check_miyabi_installation(&self) {
45        println!("{}", "Miyabi Installation:".bold());
46
47        if Path::new(".miyabi.yml").exists() {
48            println!("  ✅ Miyabi is installed");
49
50            // Check directory structure
51            let required_dirs = vec![".claude/agents", ".github/workflows", "logs", "reports"];
52
53            for dir in required_dirs {
54                if Path::new(dir).exists() {
55                    println!("    ✓ {}", dir);
56                } else {
57                    println!("    {} {} (missing)", "⚠".yellow(), dir);
58                }
59            }
60        } else {
61            println!("  {} Miyabi is not installed", "❌".red());
62            println!("    Run: miyabi install");
63        }
64
65        println!();
66    }
67
68    fn check_environment(&self) {
69        println!("{}", "Environment:".bold());
70
71        // Check GITHUB_TOKEN
72        if std::env::var("GITHUB_TOKEN").is_ok() {
73            println!("  ✅ GITHUB_TOKEN is set");
74        } else {
75            println!("  {} GITHUB_TOKEN is not set", "❌".red());
76            println!("    Run: export GITHUB_TOKEN=ghp_xxx");
77        }
78
79        // Check DEVICE_IDENTIFIER (optional)
80        if let Ok(device_id) = std::env::var("DEVICE_IDENTIFIER") {
81            println!("  ✅ DEVICE_IDENTIFIER: {}", device_id);
82        } else {
83            println!("  {} DEVICE_IDENTIFIER not set (optional)", "ℹ".blue());
84        }
85
86        println!();
87    }
88
89    fn check_git_status(&self) -> Result<()> {
90        use std::process::Command;
91
92        println!("{}", "Git Repository:".bold());
93
94        // Check if we're in a git repository
95        let output = Command::new("git")
96            .args(["rev-parse", "--git-dir"])
97            .output()?;
98
99        if !output.status.success() {
100            println!("  {} Not a git repository", "❌".red());
101            println!();
102            return Ok(());
103        }
104
105        println!("  ✅ Git repository detected");
106
107        // Get current branch
108        let output = Command::new("git")
109            .args(["branch", "--show-current"])
110            .output()?;
111
112        if output.status.success() {
113            let branch = String::from_utf8_lossy(&output.stdout);
114            println!("    Branch: {}", branch.trim());
115        }
116
117        // Get remote URL
118        let output = Command::new("git")
119            .args(["remote", "get-url", "origin"])
120            .output()?;
121
122        if output.status.success() {
123            let remote = String::from_utf8_lossy(&output.stdout);
124            println!("    Remote: {}", remote.trim());
125        }
126
127        // Get uncommitted changes
128        let output = Command::new("git").args(["status", "--short"]).output()?;
129
130        if output.status.success() {
131            let changes = String::from_utf8_lossy(&output.stdout);
132            let change_count = changes.lines().count();
133
134            if change_count > 0 {
135                println!(
136                    "    {} {} uncommitted change(s)",
137                    "⚠".yellow(),
138                    change_count
139                );
140            } else {
141                println!("    ✓ Working directory clean");
142            }
143        }
144
145        println!();
146        Ok(())
147    }
148
149    fn check_worktrees(&self) {
150        use std::process::Command;
151
152        println!("{}", "Worktrees:".bold());
153
154        let output = Command::new("git").args(["worktree", "list"]).output();
155
156        if let Ok(output) = output {
157            if output.status.success() {
158                let worktrees = String::from_utf8_lossy(&output.stdout);
159                let worktree_lines: Vec<&str> = worktrees.lines().collect();
160
161                if worktree_lines.len() > 1 {
162                    println!("  {} active worktree(s)", worktree_lines.len() - 1);
163                    for (i, line) in worktree_lines.iter().skip(1).enumerate() {
164                        println!("    {}. {}", i + 1, line);
165                    }
166                } else {
167                    println!("  No active worktrees");
168                }
169            } else {
170                println!("  Unable to check worktrees");
171            }
172        }
173
174        println!();
175    }
176
177    fn check_recent_activity(&self) {
178        println!("{}", "Recent Activity:".bold());
179
180        // Check log files
181        if Path::new("logs").exists() {
182            if let Ok(entries) = std::fs::read_dir("logs") {
183                let log_count = entries.count();
184                println!("  {} log file(s) in logs/", log_count);
185            }
186        }
187
188        // Check reports
189        if Path::new("reports").exists() {
190            if let Ok(entries) = std::fs::read_dir("reports") {
191                let report_count = entries.count();
192                println!("  {} report file(s) in reports/", report_count);
193            }
194        }
195
196        // TODO: Check GitHub Issues/PRs via API
197
198        println!();
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn test_status_command_creation() {
208        let cmd = StatusCommand::new(false);
209        assert!(!cmd.watch);
210
211        let cmd = StatusCommand::new(true);
212        assert!(cmd.watch);
213    }
214}