miyabi_cli/commands/
status.rs1use 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 self.check_miyabi_installation();
22
23 self.check_environment();
25
26 self.check_git_status()?;
28
29 self.check_worktrees();
31
32 self.check_recent_activity();
34
35 if self.watch {
36 println!();
37 println!("{}", " (Watch mode: Press Ctrl+C to exit)".dimmed());
38 }
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 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 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 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 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 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 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 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 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 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 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}