Coverage for src / moai_adk / cli / spec_status.py: 0.00%
100 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
1#!/usr/bin/env python3
2"""Spec Status Manager CLI Tool
4Provides command-line interface for managing SPEC status tracking.
6Usage:
7 python3 -m moai_adk.cli.spec_status <command> <spec_id> [options]
8 moai-spec <command> <spec_id> [options]
10Commands:
11 - status_update: Update SPEC status
12 - validate_completion: Validate if SPEC is ready for completion
13 - batch_update: Update all completed SPECs
14"""
16import argparse
17import json
18import sys
19from datetime import datetime
20from pathlib import Path
21from typing import Any, Dict
23try:
24 from moai_adk.core.spec_status_manager import SpecStatusManager
25except ImportError:
26 # Fallback: look in current directory
27 import sys
28 from pathlib import Path
29 sys.path.insert(0, str(Path(__file__).parent.parent))
30 try:
31 from core.spec_status_manager import SpecStatusManager
32 except ImportError:
33 raise ImportError("SpecStatusManager not found")
36def update_spec_status(spec_id: str, new_status: str, reason: str = "") -> Dict[str, Any]:
37 """Update SPEC status with validation and logging
39 Args:
40 spec_id: The SPEC identifier
41 new_status: New status value
42 reason: Reason for status change
44 Returns:
45 Update result dictionary
46 """
47 try:
48 # Initialize manager
49 project_root = Path.cwd()
50 manager = SpecStatusManager(project_root)
52 # Validate new status
53 valid_statuses = ['draft', 'in-progress', 'completed', 'archived']
54 if new_status not in valid_statuses:
55 return {
56 "success": False,
57 "error": f"Invalid status: {new_status}. Valid statuses: {valid_statuses}"
58 }
60 # Check if SPEC exists
61 spec_file = project_root / ".moai" / "specs" / spec_id / "spec.md"
62 if not spec_file.exists():
63 return {
64 "success": False,
65 "error": f"SPEC file not found: {spec_file}"
66 }
68 # Update status
69 success = manager.update_spec_status(spec_id, new_status)
71 if success:
72 # Log the status change
73 log_entry = {
74 "timestamp": datetime.now().isoformat(),
75 "spec_id": spec_id,
76 "old_status": "unknown", # Could be extracted from file before update
77 "new_status": new_status,
78 "reason": reason
79 }
81 # Create status change log
82 log_dir = project_root / ".moai" / "logs"
83 log_dir.mkdir(parents=True, exist_ok=True)
84 log_file = log_dir / "spec_status_changes.jsonl"
86 with open(log_file, 'a', encoding='utf-8') as f:
87 f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
89 return {
90 "success": True,
91 "spec_id": spec_id,
92 "new_status": new_status,
93 "reason": reason,
94 "timestamp": log_entry["timestamp"]
95 }
96 else:
97 return {
98 "success": False,
99 "error": f"Failed to update SPEC {spec_id} status"
100 }
102 except Exception as e:
103 return {
104 "success": False,
105 "error": f"Error updating SPEC status: {str(e)}"
106 }
109def validate_spec_completion(spec_id: str) -> Dict[str, Any]:
110 """Validate if SPEC is ready for completion
112 Args:
113 spec_id: The SPEC identifier
115 Returns:
116 Validation result dictionary
117 """
118 try:
119 project_root = Path.cwd()
120 manager = SpecStatusManager(project_root)
122 # Run validation
123 validation = manager.validate_spec_for_completion(spec_id)
125 return {
126 "success": True,
127 "spec_id": spec_id,
128 "validation": validation
129 }
131 except Exception as e:
132 return {
133 "success": False,
134 "error": f"Error validating SPEC completion: {str(e)}"
135 }
138def batch_update_completed_specs() -> Dict[str, Any]:
139 """Batch update all draft SPECs that have completed implementations
141 Returns:
142 Batch update result dictionary
143 """
144 try:
145 project_root = Path.cwd()
146 manager = SpecStatusManager(project_root)
148 # Run batch update
149 results = manager.batch_update_completed_specs()
151 # Log the batch update
152 log_entry = {
153 "timestamp": datetime.now().isoformat(),
154 "operation": "batch_update_completed",
155 "results": results
156 }
158 log_dir = project_root / ".moai" / "logs"
159 log_dir.mkdir(parents=True, exist_ok=True)
160 log_file = log_dir / "spec_status_changes.jsonl"
162 with open(log_file, 'a', encoding='utf-8') as f:
163 f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
165 return {
166 "success": True,
167 "results": results,
168 "timestamp": log_entry["timestamp"]
169 }
171 except Exception as e:
172 return {
173 "success": False,
174 "error": f"Error in batch update: {str(e)}"
175 }
178def detect_draft_specs() -> Dict[str, Any]:
179 """Detect all draft SPECs in the project
181 Returns:
182 List of draft SPEC IDs
183 """
184 try:
185 project_root = Path.cwd()
186 manager = SpecStatusManager(project_root)
188 draft_specs = manager.detect_draft_specs()
190 return {
191 "success": True,
192 "draft_specs": list(draft_specs),
193 "count": len(draft_specs)
194 }
196 except Exception as e:
197 return {
198 "success": False,
199 "error": f"Error detecting draft SPECs: {str(e)}"
200 }
203def main():
204 """Main function"""
205 parser = argparse.ArgumentParser(description='Spec Status Manager Hooks')
206 parser.add_argument('command', choices=[
207 'status_update', 'validate_completion', 'batch_update', 'detect_drafts'
208 ], help='Command to execute')
209 parser.add_argument('spec_id', nargs='?', help='SPEC ID (for specific commands)')
210 parser.add_argument('--status', help='New status value')
211 parser.add_argument('--reason', default='', help='Reason for status change')
213 args = parser.parse_args()
215 try:
216 result = {}
218 if args.command == 'status_update':
219 if not args.spec_id or not args.status:
220 print(json.dumps({
221 "success": False,
222 "error": "status_update requires spec_id and --status"
223 }))
224 sys.exit(1)
226 result = update_spec_status(args.spec_id, args.status, args.reason)
228 elif args.command == 'validate_completion':
229 if not args.spec_id:
230 print(json.dumps({
231 "success": False,
232 "error": "validate_completion requires spec_id"
233 }))
234 sys.exit(1)
236 result = validate_spec_completion(args.spec_id)
238 elif args.command == 'batch_update':
239 result = batch_update_completed_specs()
241 elif args.command == 'detect_drafts':
242 result = detect_draft_specs()
244 # Add command info
245 result["command"] = args.command
246 result["execution_time"] = datetime.now().isoformat()
248 print(json.dumps(result, ensure_ascii=False, indent=2))
250 except Exception as e:
251 error_result = {
252 "success": False,
253 "command": args.command if 'args' in locals() else 'unknown',
254 "error": str(e),
255 "execution_time": datetime.now().isoformat()
256 }
258 print(json.dumps(error_result, ensure_ascii=False, indent=2))
259 sys.exit(1)
262if __name__ == "__main__":
263 main()