#!/usr/bin/env python3 """ AI 夜間開發系統 - 專案配置載入器 從 YAML 檔案讀取專案配置並輸出為 shell 環境變數 """ import sys import yaml from pathlib import Path from typing import Dict, Any, Optional class ProjectConfigLoader: """專案配置載入器""" def __init__(self, config_file: str = "projects.yml"): """初始化配置載入器 Args: config_file: YAML 配置檔案路徑 """ self.config_file = Path(config_file) self.projects: Dict[str, Any] = {} if not self.config_file.exists(): raise FileNotFoundError(f"配置檔案不存在: {self.config_file}") self._load_config() def _load_config(self) -> None: """載入 YAML 配置檔案""" try: with open(self.config_file, 'r', encoding='utf-8') as f: data = yaml.safe_load(f) self.projects = data.get('projects', {}) except yaml.YAMLError as e: print(f"錯誤: YAML 格式錯誤 - {e}", file=sys.stderr) sys.exit(1) except Exception as e: print(f"錯誤: 無法讀取配置檔案 - {e}", file=sys.stderr) sys.exit(1) def get_project(self, project_id: str) -> Optional[Dict[str, Any]]: """取得指定專案的配置 Args: project_id: 專案 ID Returns: 專案配置字典,若不存在則返回 None """ return self.projects.get(project_id) def list_projects(self) -> list: """列出所有專案 ID""" return list(self.projects.keys()) def export_shell_env(self, project_id: str) -> str: """將專案配置匯出為 shell 環境變數設定 Args: project_id: 專案 ID Returns: Shell export 命令字串 """ project = self.get_project(project_id) if not project: return f"echo '錯誤: 專案 {project_id} 不存在'" # 構建 export 命令 exports = [] exports.append(f"export AI_DEV_PROJECT=\"{project_id}\"") exports.append(f"export AI_DEV_PROJECT_DIR=\"{project.get('project_dir', '')}\"") exports.append(f"export AI_DEV_AI_PROVIDER=\"{project.get('ai_provider', 'claude')}\"") exports.append(f"export AI_DEV_ENABLE=\"{str(project.get('enable', True)).lower()}\"") exports.append(f"export AI_DEV_LOG_DIR=\"{project.get('log_dir', '$HOME/.ai-dev-logs')}\"") exports.append(f"export AI_DEV_MAX_ITERATIONS=\"{project.get('max_iterations', 30)}\"") # 添加成功訊息 project_name = project.get('name', project_id) exports.append(f"echo '✅ {project_name} 專案配置已載入'") return "\n".join(exports) def show_project_info(self, project_id: str) -> None: """顯示專案資訊 Args: project_id: 專案 ID """ project = self.get_project(project_id) if not project: print(f"錯誤: 專案 {project_id} 不存在", file=sys.stderr) sys.exit(1) print(f"\n專案: {project.get('name', project_id)}") print(f"ID: {project_id}") print(f"描述: {project.get('description', 'N/A')}") print(f"路徑: {project.get('project_dir', 'N/A')}") print(f"AI Provider: {project.get('ai_provider', 'claude')}") print(f"啟用: {project.get('enable', True)}") print(f"最大迭代次數: {project.get('max_iterations', 30)}") def add_project(self, project_id: str, config: Dict[str, Any]) -> None: """新增專案配置 Args: project_id: 專案 ID config: 專案配置字典 """ if project_id in self.projects: print(f"警告: 專案 {project_id} 已存在,將被覆蓋", file=sys.stderr) self.projects[project_id] = config self._save_config() print(f"✅ 專案 {project_id} 已新增") def update_project(self, project_id: str, config: Dict[str, Any]) -> None: """更新專案配置 Args: project_id: 專案 ID config: 專案配置字典 """ if project_id not in self.projects: print(f"錯誤: 專案 {project_id} 不存在", file=sys.stderr) sys.exit(1) self.projects[project_id].update(config) self._save_config() print(f"✅ 專案 {project_id} 已更新") def remove_project(self, project_id: str) -> None: """刪除專案配置 Args: project_id: 專案 ID """ if project_id not in self.projects: print(f"錯誤: 專案 {project_id} 不存在", file=sys.stderr) sys.exit(1) del self.projects[project_id] self._save_config() print(f"✅ 專案 {project_id} 已刪除") def _save_config(self) -> None: """儲存配置到 YAML 檔案""" try: data = {'projects': self.projects} with open(self.config_file, 'w', encoding='utf-8') as f: yaml.safe_dump(data, f, allow_unicode=True, default_flow_style=False, sort_keys=False) except Exception as e: print(f"錯誤: 無法儲存配置檔案 - {e}", file=sys.stderr) sys.exit(1) def main(): """主程式""" # 取得腳本所在目錄 script_dir = Path(__file__).parent config_file = script_dir / "projects.yml" # 解析命令列參數 if len(sys.argv) < 2: print("使用方式:") print(f" {sys.argv[0]} list # 列出所有專案") print(f" {sys.argv[0]} export # 輸出專案的 shell 環境變數") print(f" {sys.argv[0]} info # 顯示專案資訊") sys.exit(1) command = sys.argv[1] try: loader = ProjectConfigLoader(config_file) if command == "list": projects = loader.list_projects() print("\n可用專案:") for project_id in projects: project = loader.get_project(project_id) print(f" - {project_id}: {project.get('name', 'N/A')}") print() elif command == "export": if len(sys.argv) < 3: print("錯誤: 請指定專案 ID", file=sys.stderr) sys.exit(1) project_id = sys.argv[2] print(loader.export_shell_env(project_id)) elif command == "info": if len(sys.argv) < 3: print("錯誤: 請指定專案 ID", file=sys.stderr) sys.exit(1) project_id = sys.argv[2] loader.show_project_info(project_id) else: print(f"錯誤: 未知的命令 '{command}'", file=sys.stderr) sys.exit(1) except Exception as e: print(f"錯誤: {e}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()