miyabi_cli/commands/
init.rs1use crate::error::{CliError, Result};
4use colored::Colorize;
5use std::fs;
6use std::path::{Path, PathBuf};
7
8pub struct InitCommand {
9 pub name: String,
10 #[allow(dead_code)] pub private: bool,
12}
13
14impl InitCommand {
15 pub fn new(name: String, private: bool) -> Self {
16 Self { name, private }
17 }
18
19 pub async fn execute(&self) -> Result<()> {
20 println!("{}", "🚀 Initializing new Miyabi project...".cyan().bold());
21
22 self.validate_project_name()?;
24
25 let project_dir = self.create_project_directory()?;
27
28 self.init_git_repository(&project_dir)?;
30
31 self.create_project_structure(&project_dir)?;
33
34 self.create_config_files(&project_dir)?;
36
37 println!();
38 println!("{}", "✅ Project initialized successfully!".green().bold());
39 println!();
40 println!("Next steps:");
41 println!(" cd {}", self.name);
42 println!(" export GITHUB_TOKEN=ghp_xxx");
43 println!(" miyabi status");
44
45 Ok(())
46 }
47
48 fn validate_project_name(&self) -> Result<()> {
49 if self.name.is_empty() {
51 return Err(CliError::InvalidProjectName(
52 "Project name cannot be empty".to_string(),
53 ));
54 }
55
56 if !self
58 .name
59 .chars()
60 .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
61 {
62 return Err(CliError::InvalidProjectName(
63 "Project name can only contain alphanumeric characters, hyphens, and underscores"
64 .to_string(),
65 ));
66 }
67
68 Ok(())
69 }
70
71 fn create_project_directory(&self) -> Result<PathBuf> {
72 let project_dir = PathBuf::from(&self.name);
73
74 if project_dir.exists() {
76 return Err(CliError::ProjectExists(self.name.clone()));
77 }
78
79 fs::create_dir(&project_dir)?;
81 println!(" Created directory: {}", project_dir.display());
82
83 Ok(project_dir)
84 }
85
86 fn init_git_repository(&self, project_dir: &Path) -> Result<()> {
87 use std::process::Command;
88
89 let output = Command::new("git")
91 .args(["init"])
92 .current_dir(project_dir)
93 .output()?;
94
95 if !output.status.success() {
96 return Err(CliError::Io(std::io::Error::other(
97 "Failed to initialize git repository",
98 )));
99 }
100
101 println!(" Initialized git repository");
102 Ok(())
103 }
104
105 fn create_project_structure(&self, project_dir: &Path) -> Result<()> {
106 let dirs = vec![
108 ".github/workflows",
109 ".claude/agents/specs/coding",
110 ".claude/agents/specs/business",
111 ".claude/agents/prompts/coding",
112 ".claude/agents/prompts/business",
113 ".claude/commands",
114 ".claude/prompts",
115 ".claude/templates",
116 "docs",
117 "scripts",
118 "logs",
119 "reports",
120 ];
121
122 for dir in dirs {
123 let dir_path = project_dir.join(dir);
124 fs::create_dir_all(&dir_path)?;
125 }
126
127 self.create_claude_md(project_dir)?;
129
130 self.create_claude_files(project_dir)?;
132
133 println!(" Created project structure");
134 Ok(())
135 }
136
137 fn create_claude_md(&self, project_dir: &Path) -> Result<()> {
138 let claude_md = format!(
139 r#"# Claude Code プロジェクト設定
140
141このファイルは、Claude Codeが自動的に参照するプロジェクトコンテキストファイルです。
142
143## プロジェクト概要
144
145**{}** - Miyabi自律型開発プロジェクト
146
147## アーキテクチャ
148
149### コアコンポーネント
150
1511. **Agent System** - 自律実行Agent(Miyabi Framework)
1522. **GitHub OS Integration** - GitHubをOSとして活用
1533. **Label System** - 53ラベル体系による状態管理
154
155### ディレクトリ構造
156
157```
158{}/
159├── .claude/ # Claude Code設定
160│ ├── agents/ # Agent仕様・プロンプト
161│ ├── commands/ # カスタムコマンド
162│ └── prompts/ # 実行プロンプト
163├── .github/ # GitHub設定
164│ └── workflows/ # GitHub Actions
165├── docs/ # ドキュメント
166├── scripts/ # 自動化スクリプト
167├── logs/ # ログファイル
168└── reports/ # レポート出力
169```
170
171## 開発ガイドライン
172
173### コミット規約
174- Conventional Commits準拠
175- `feat:`, `fix:`, `chore:`, `docs:`, etc.
176
177### セキュリティ
178- トークンは環境変数
179- `.miyabi.yml`は`.gitignore`に追加済み
180
181## 環境変数
182
183```bash
184GITHUB_TOKEN=ghp_xxx # GitHubアクセストークン
185ANTHROPIC_API_KEY=sk-xxx # Anthropic APIキー(Agent実行時)
186```
187
188## 実行例
189
190```bash
191# ステータス確認
192miyabi status
193
194# Agent実行
195miyabi agent coordinator --issue 1
196
197# テスト実行
198cargo test --all
199
200# Linter実行
201cargo clippy --all-targets
202```
203
204---
205
206**このファイルはClaude Codeが自動参照します。プロジェクトのコンテキストとして常に最新に保ってください。**
207"#,
208 self.name, self.name
209 );
210
211 fs::write(project_dir.join("CLAUDE.md"), claude_md)?;
212 Ok(())
213 }
214
215 fn create_claude_files(&self, project_dir: &Path) -> Result<()> {
216 let claude_readme = r#"# .claude Directory
218
219Claude Code設定ディレクトリ - プロジェクト固有の設定とプロンプト
220
221## 構造
222
223- `agents/` - Agent仕様とプロンプト
224 - `specs/coding/` - コーディング系Agent仕様
225 - `specs/business/` - ビジネス系Agent仕様
226 - `prompts/coding/` - 実行プロンプト
227- `commands/` - カスタムスラッシュコマンド
228- `prompts/` - 汎用プロンプト
229- `templates/` - テンプレートファイル
230
231## カスタムコマンド
232
233`.claude/commands/` 配下に `*.md` ファイルを作成することで、
234カスタムスラッシュコマンドを定義できます。
235
236例: `.claude/commands/test.md` → `/test` コマンド
237
238## Agent仕様
239
240Agent仕様ファイル(`.claude/agents/specs/`)で、各Agentの役割・権限・エスカレーション条件を定義します。
241"#;
242 fs::write(project_dir.join(".claude/README.md"), claude_readme)?;
243
244 let quick_start = format!(
246 r#"# {} - Quick Start Guide
247
248## 🚀 3分で始めるMiyabi
249
250### 1. 環境変数設定
251
252```bash
253export GITHUB_TOKEN=ghp_xxx
254export ANTHROPIC_API_KEY=sk-xxx
255```
256
257### 2. ステータス確認
258
259```bash
260miyabi status
261```
262
263### 3. Issue作成
264
265GitHubでIssueを作成し、以下のラベルを付与:
266- `type:feature` または `type:bug`
267- `priority:P1-High`
268
269### 4. Agent実行
270
271```bash
272miyabi agent coordinator --issue 1
273```
274
275## 📚 詳細ドキュメント
276
277- [CLAUDE.md](../CLAUDE.md) - プロジェクトコンテキスト
278- [.claude/README.md](./README.md) - .claudeディレクトリ説明
279
280---
281
282**Miyabi** - Beauty in Autonomous Development 🌸
283"#,
284 self.name
285 );
286 fs::write(project_dir.join(".claude/QUICK_START.md"), quick_start)?;
287
288 Ok(())
289 }
290
291 fn create_config_files(&self, project_dir: &Path) -> Result<()> {
292 let miyabi_config = format!(
294 r#"# Miyabi Configuration
295project_name: {}
296version: "0.1.0"
297
298# GitHub settings (use environment variables for sensitive data)
299# github_token: ${{{{ GITHUB_TOKEN }}}}
300
301# Agent settings
302agents:
303 enabled: true
304 use_worktree: true
305 worktree_base_path: ".worktrees"
306
307# Logging
308logging:
309 level: info
310 directory: "./logs"
311
312# Reporting
313reporting:
314 directory: "./reports"
315"#,
316 self.name
317 );
318
319 fs::write(project_dir.join(".miyabi.yml"), miyabi_config)?;
320
321 let gitignore = r#"# Miyabi
323.miyabi.yml
324.worktrees/
325logs/
326reports/
327*.log
328
329# Environment
330.env
331.env.local
332
333# Dependencies
334node_modules/
335target/
336
337# IDE
338.vscode/
339.idea/
340*.swp
341*.swo
342"#;
343
344 fs::write(project_dir.join(".gitignore"), gitignore)?;
345
346 let readme = format!(
348 r#"# {}
349
350Miyabi autonomous development project.
351
352## Setup
353
3541. Set GitHub token:
355 ```bash
356 export GITHUB_TOKEN=ghp_xxx
357 ```
358
3592. Check status:
360 ```bash
361 miyabi status
362 ```
363
3643. Run agent:
365 ```bash
366 miyabi agent coordinator --issue 1
367 ```
368
369## Documentation
370
371- See `docs/` directory for detailed documentation
372- See `.claude/agents/specs/` for agent specifications
373"#,
374 self.name
375 );
376
377 fs::write(project_dir.join("README.md"), readme)?;
378
379 println!(" Created configuration files");
380 Ok(())
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use super::*;
387
388 #[test]
389 fn test_validate_project_name() {
390 let valid_cmd = InitCommand::new("my-project".to_string(), false);
391 assert!(valid_cmd.validate_project_name().is_ok());
392
393 let valid_cmd = InitCommand::new("my_project_123".to_string(), false);
394 assert!(valid_cmd.validate_project_name().is_ok());
395
396 let invalid_cmd = InitCommand::new("".to_string(), false);
397 assert!(invalid_cmd.validate_project_name().is_err());
398
399 let invalid_cmd = InitCommand::new("my project".to_string(), false);
400 assert!(invalid_cmd.validate_project_name().is_err());
401
402 let invalid_cmd = InitCommand::new("my@project".to_string(), false);
403 assert!(invalid_cmd.validate_project_name().is_err());
404 }
405}