agent_cli.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. #!/usr/bin/env python3
  2. """
  3. AceFlow v2.0 CLI工具 - Agent集成版本
  4. 为Cursor、Cline、Copilot等AI工具提供智能工作流建议
  5. """
  6. import sys
  7. import json
  8. import yaml
  9. import argparse
  10. from pathlib import Path
  11. from typing import Dict, Any, List, Optional
  12. from datetime import datetime
  13. # 导入决策引擎
  14. sys.path.append(str(Path(__file__).parent.parent))
  15. from engines.rule_based_engine import get_decision_engine, DecisionResult
  16. class AceFlowCLI:
  17. """AceFlow CLI工具"""
  18. def __init__(self):
  19. self.project_root = Path.cwd()
  20. self.aceflow_dir = self.project_root / ".aceflow"
  21. self.version = "2.0.0"
  22. self.engine = get_decision_engine(self.project_root)
  23. def describe(self, output_format: str = "json") -> str:
  24. """描述工具能力,供Agent发现和理解"""
  25. description = {
  26. "name": "AceFlow",
  27. "version": self.version,
  28. "description": "AI驱动的软件开发工作流管理工具",
  29. "capabilities": [
  30. "智能工作流程推荐",
  31. "任务分类和优先级建议",
  32. "开发阶段管理",
  33. "进度跟踪和预测",
  34. "项目复杂度分析",
  35. "团队协作优化"
  36. ],
  37. "use_cases": [
  38. "项目规划和流程选择",
  39. "任务管理和跟踪",
  40. "开发效率优化",
  41. "质量保证流程",
  42. "敏捷开发支持"
  43. ],
  44. "trigger_patterns": [
  45. "工作流",
  46. "流程",
  47. "项目规划",
  48. "任务管理",
  49. "开发流程",
  50. "敏捷开发",
  51. "项目管理"
  52. ],
  53. "integration": {
  54. "cli_commands": {
  55. "suggest": {
  56. "description": "智能工作流推荐",
  57. "usage": "aceflow suggest --task '任务描述' [选项]",
  58. "example": "aceflow suggest --task '修复登录bug' --format json"
  59. },
  60. "plan": {
  61. "description": "项目规划建议",
  62. "usage": "aceflow plan --project-type web --team-size 5 [选项]",
  63. "example": "aceflow plan --project-type web --team-size 5 --format json"
  64. },
  65. "track": {
  66. "description": "进度跟踪",
  67. "usage": "aceflow track --stage current [选项]",
  68. "example": "aceflow track --stage current --format json"
  69. },
  70. "status": {
  71. "description": "项目状态查询",
  72. "usage": "aceflow status [选项]",
  73. "example": "aceflow status --format json"
  74. }
  75. },
  76. "output_formats": ["json", "yaml", "text"],
  77. "response_schema": {
  78. "type": "object",
  79. "properties": {
  80. "recommended_flow": {"type": "string"},
  81. "confidence": {"type": "number"},
  82. "reasoning": {"type": "string"},
  83. "steps": {"type": "array"},
  84. "estimated_hours": {"type": "integer"},
  85. "alternatives": {"type": "array"}
  86. }
  87. }
  88. },
  89. "when_to_use": {
  90. "scenarios": [
  91. "用户询问如何组织开发流程",
  92. "需要制定项目计划",
  93. "想要标准化开发流程",
  94. "需要跟踪项目进度",
  95. "选择合适的开发模式"
  96. ],
  97. "keywords": [
  98. "workflow", "process", "planning", "management",
  99. "工作流", "流程", "规划", "管理"
  100. ]
  101. }
  102. }
  103. if output_format == "yaml":
  104. return yaml.dump(description, default_flow_style=False, allow_unicode=True)
  105. elif output_format == "text":
  106. return self._format_description_text(description)
  107. else:
  108. return json.dumps(description, indent=2, ensure_ascii=False)
  109. def suggest(self, task: str, **kwargs) -> Dict[str, Any]:
  110. """智能工作流推荐"""
  111. if not task:
  112. raise ValueError("任务描述不能为空")
  113. # 构建上下文
  114. context = {}
  115. if kwargs.get("team_size"):
  116. context["team_size"] = int(kwargs["team_size"])
  117. if kwargs.get("project_type"):
  118. context["project_type"] = kwargs["project_type"]
  119. if kwargs.get("complexity"):
  120. context["complexity"] = kwargs["complexity"]
  121. if kwargs.get("urgency"):
  122. context["urgency"] = kwargs["urgency"]
  123. # 获取决策结果
  124. result = self.engine.make_decision(task, context)
  125. # 格式化输出
  126. return self._format_decision_result(result)
  127. def plan(self, **kwargs) -> Dict[str, Any]:
  128. """项目规划建议"""
  129. # 构建虚拟任务描述
  130. project_type = kwargs.get("project_type", "web")
  131. team_size = kwargs.get("team_size", 5)
  132. task_description = f"为{project_type}项目制定开发计划"
  133. context = {
  134. "project_type": project_type,
  135. "team_size": int(team_size),
  136. "urgency": kwargs.get("urgency", "medium")
  137. }
  138. if kwargs.get("complexity"):
  139. context["complexity"] = kwargs["complexity"]
  140. # 获取决策结果
  141. result = self.engine.make_decision(task_description, context)
  142. # 增加项目规划特定信息
  143. planning_result = self._format_decision_result(result)
  144. planning_result["project_recommendations"] = self._generate_project_recommendations(context)
  145. return planning_result
  146. def track(self, **kwargs) -> Dict[str, Any]:
  147. """进度跟踪"""
  148. stage = kwargs.get("stage", "current")
  149. # 读取项目状态
  150. project_state = self._load_project_state()
  151. if not project_state:
  152. return {
  153. "error": "未找到项目状态,请先初始化项目",
  154. "suggestion": "运行 'aceflow init' 初始化项目"
  155. }
  156. # 构建跟踪结果
  157. tracking_result = {
  158. "project_id": project_state.get("project_id", "unknown"),
  159. "flow_mode": project_state.get("flow_mode", "unknown"),
  160. "current_stage": project_state.get("current_stage", "unknown"),
  161. "overall_progress": project_state.get("progress", {}).get("overall", 0),
  162. "stage_states": project_state.get("stage_states", {}),
  163. "last_updated": project_state.get("last_updated", "unknown"),
  164. "recommendations": []
  165. }
  166. # 添加进度建议
  167. if tracking_result["overall_progress"] < 30:
  168. tracking_result["recommendations"].append("项目处于早期阶段,建议专注于需求分析和设计")
  169. elif tracking_result["overall_progress"] < 70:
  170. tracking_result["recommendations"].append("项目进展良好,建议保持当前开发节奏")
  171. else:
  172. tracking_result["recommendations"].append("项目接近完成,建议重点关注测试和部署准备")
  173. return tracking_result
  174. def status(self, **kwargs) -> Dict[str, Any]:
  175. """项目状态查询"""
  176. # 读取项目状态和配置
  177. project_state = self._load_project_state()
  178. project_config = self._load_project_config()
  179. # 分析项目特征
  180. project_profile = self.engine.project_analyzer.analyze_project()
  181. status_result = {
  182. "project_info": {
  183. "name": project_config.get("project", {}).get("name", "unknown"),
  184. "type": project_profile.project_type,
  185. "team_size": project_profile.team_size,
  186. "complexity": project_profile.complexity.value,
  187. "tech_stack": project_profile.tech_stack
  188. },
  189. "current_state": project_state or {},
  190. "project_health": {
  191. "has_tests": project_profile.has_tests,
  192. "has_ci_cd": project_profile.has_ci_cd,
  193. "has_documentation": project_profile.has_documentation,
  194. "file_count": project_profile.file_count,
  195. "git_activity": project_profile.git_activity
  196. },
  197. "recommendations": []
  198. }
  199. # 生成健康建议
  200. if not project_profile.has_tests:
  201. status_result["recommendations"].append("建议添加测试用例以提高代码质量")
  202. if not project_profile.has_ci_cd:
  203. status_result["recommendations"].append("建议配置CI/CD流程以自动化部署")
  204. if not project_profile.has_documentation:
  205. status_result["recommendations"].append("建议完善项目文档以提高可维护性")
  206. return status_result
  207. def memory(self, action: str, **kwargs) -> Dict[str, Any]:
  208. """记忆管理"""
  209. memory_dir = self.aceflow_dir / "memory"
  210. if action == "list":
  211. return self._list_memories(memory_dir)
  212. elif action == "search":
  213. query = kwargs.get("query", "")
  214. return self._search_memories(memory_dir, query)
  215. elif action == "clean":
  216. return self._clean_memories(memory_dir)
  217. else:
  218. return {"error": f"不支持的记忆操作: {action}"}
  219. def _format_decision_result(self, result: DecisionResult) -> Dict[str, Any]:
  220. """格式化决策结果"""
  221. return {
  222. "recommended_flow": result.recommended_flow,
  223. "confidence": result.confidence,
  224. "reasoning": result.reasoning,
  225. "steps": result.steps,
  226. "estimated_hours": result.estimated_hours,
  227. "alternatives": result.alternatives,
  228. "timestamp": datetime.now().isoformat(),
  229. "task_type": result.metadata.get("task_type", "unknown")
  230. }
  231. def _generate_project_recommendations(self, context: Dict[str, Any]) -> List[str]:
  232. """生成项目规划建议"""
  233. recommendations = []
  234. team_size = context.get("team_size", 1)
  235. project_type = context.get("project_type", "web")
  236. # 团队规模建议
  237. if team_size <= 3:
  238. recommendations.append("小团队建议使用敏捷开发模式,快速迭代")
  239. elif team_size > 8:
  240. recommendations.append("大团队建议制定详细的协作流程和代码规范")
  241. # 项目类型建议
  242. if project_type == "web":
  243. recommendations.append("Web项目建议重点关注性能优化和用户体验")
  244. elif project_type == "mobile":
  245. recommendations.append("移动应用建议关注多平台适配和离线功能")
  246. elif project_type == "api":
  247. recommendations.append("API项目建议重点关注接口设计和文档完善")
  248. return recommendations
  249. def _load_project_state(self) -> Optional[Dict[str, Any]]:
  250. """加载项目状态"""
  251. state_file = self.aceflow_dir / "state" / "project_state.json"
  252. if state_file.exists():
  253. try:
  254. with open(state_file, 'r', encoding='utf-8') as f:
  255. return json.load(f)
  256. except Exception:
  257. return None
  258. return None
  259. def _load_project_config(self) -> Optional[Dict[str, Any]]:
  260. """加载项目配置"""
  261. config_file = self.aceflow_dir / "config.yaml"
  262. if config_file.exists():
  263. try:
  264. with open(config_file, 'r', encoding='utf-8') as f:
  265. return yaml.safe_load(f)
  266. except Exception:
  267. return None
  268. return None
  269. def _list_memories(self, memory_dir: Path) -> Dict[str, Any]:
  270. """列出记忆"""
  271. if not memory_dir.exists():
  272. return {"memories": [], "count": 0}
  273. memories = []
  274. for memory_file in memory_dir.glob("*.json"):
  275. try:
  276. with open(memory_file, 'r', encoding='utf-8') as f:
  277. memory_data = json.load(f)
  278. memories.append({
  279. "id": memory_file.stem,
  280. "timestamp": memory_data.get("timestamp", "unknown"),
  281. "content_preview": memory_data.get("content", "")[:100] + "..." if len(memory_data.get("content", "")) > 100 else memory_data.get("content", ""),
  282. "keywords": memory_data.get("keywords", [])
  283. })
  284. except Exception:
  285. continue
  286. return {"memories": memories, "count": len(memories)}
  287. def _search_memories(self, memory_dir: Path, query: str) -> Dict[str, Any]:
  288. """搜索记忆"""
  289. if not memory_dir.exists():
  290. return {"results": [], "count": 0}
  291. results = []
  292. query_lower = query.lower()
  293. for memory_file in memory_dir.glob("*.json"):
  294. try:
  295. with open(memory_file, 'r', encoding='utf-8') as f:
  296. memory_data = json.load(f)
  297. # 简单的关键词匹配
  298. content = memory_data.get("content", "").lower()
  299. keywords = [k.lower() for k in memory_data.get("keywords", [])]
  300. if query_lower in content or any(query_lower in keyword for keyword in keywords):
  301. results.append({
  302. "id": memory_file.stem,
  303. "timestamp": memory_data.get("timestamp", "unknown"),
  304. "content": memory_data.get("content", ""),
  305. "relevance": self._calculate_relevance(query_lower, content, keywords)
  306. })
  307. except Exception:
  308. continue
  309. # 按相关性排序
  310. results.sort(key=lambda x: x["relevance"], reverse=True)
  311. return {"results": results, "count": len(results)}
  312. def _calculate_relevance(self, query: str, content: str, keywords: List[str]) -> float:
  313. """计算相关性分数"""
  314. score = 0.0
  315. # 内容匹配
  316. if query in content:
  317. score += 1.0
  318. # 关键词匹配
  319. for keyword in keywords:
  320. if query in keyword:
  321. score += 0.5
  322. return score
  323. def _clean_memories(self, memory_dir: Path) -> Dict[str, Any]:
  324. """清理记忆"""
  325. if not memory_dir.exists():
  326. return {"message": "记忆目录不存在", "cleaned": 0}
  327. cleaned_count = 0
  328. # 清理空文件或损坏文件
  329. for memory_file in memory_dir.glob("*.json"):
  330. try:
  331. with open(memory_file, 'r', encoding='utf-8') as f:
  332. memory_data = json.load(f)
  333. # 检查是否为空或无效记忆
  334. if not memory_data.get("content") or len(memory_data.get("content", "")) < 10:
  335. memory_file.unlink()
  336. cleaned_count += 1
  337. except Exception:
  338. # 删除损坏的文件
  339. memory_file.unlink()
  340. cleaned_count += 1
  341. return {"message": f"清理完成,删除了{cleaned_count}个无效记忆", "cleaned": cleaned_count}
  342. def _format_description_text(self, description: Dict[str, Any]) -> str:
  343. """格式化文本描述"""
  344. text = f"{description['name']} v{description['version']}\n"
  345. text += f"{description['description']}\n\n"
  346. text += "核心能力:\n"
  347. for capability in description['capabilities']:
  348. text += f" - {capability}\n"
  349. text += "\n使用场景:\n"
  350. for use_case in description['use_cases']:
  351. text += f" - {use_case}\n"
  352. text += "\n触发关键词:\n"
  353. text += f" {', '.join(description['trigger_patterns'])}\n"
  354. return text
  355. def main():
  356. """主函数"""
  357. parser = argparse.ArgumentParser(
  358. description="AceFlow v2.0 - AI驱动的软件开发工作流管理工具",
  359. formatter_class=argparse.RawDescriptionHelpFormatter
  360. )
  361. parser.add_argument("--version", action="version", version="AceFlow v2.0.0")
  362. parser.add_argument("--format", choices=["json", "yaml", "text"], default="json",
  363. help="输出格式")
  364. parser.add_argument("--verbose", action="store_true", help="详细输出")
  365. parser.add_argument("--quiet", action="store_true", help="静默模式")
  366. subparsers = parser.add_subparsers(dest="command", help="可用命令")
  367. # describe命令
  368. describe_parser = subparsers.add_parser("describe", help="描述工具能力")
  369. describe_parser.add_argument("--format", choices=["json", "yaml", "text"], default="json")
  370. # suggest命令
  371. suggest_parser = subparsers.add_parser("suggest", help="智能工作流推荐")
  372. suggest_parser.add_argument("--task", required=True, help="任务描述")
  373. suggest_parser.add_argument("--team-size", type=int, help="团队规模")
  374. suggest_parser.add_argument("--project-type", help="项目类型")
  375. suggest_parser.add_argument("--complexity", choices=["simple", "moderate", "complex", "enterprise"], help="项目复杂度")
  376. suggest_parser.add_argument("--urgency", choices=["low", "medium", "high"], default="medium", help="紧急程度")
  377. # plan命令
  378. plan_parser = subparsers.add_parser("plan", help="项目规划建议")
  379. plan_parser.add_argument("--project-type", default="web", help="项目类型")
  380. plan_parser.add_argument("--team-size", type=int, default=5, help="团队规模")
  381. plan_parser.add_argument("--complexity", choices=["simple", "moderate", "complex", "enterprise"], help="项目复杂度")
  382. plan_parser.add_argument("--urgency", choices=["low", "medium", "high"], default="medium", help="紧急程度")
  383. # track命令
  384. track_parser = subparsers.add_parser("track", help="进度跟踪")
  385. track_parser.add_argument("--stage", default="current", help="阶段")
  386. # status命令
  387. status_parser = subparsers.add_parser("status", help="项目状态查询")
  388. # memory命令
  389. memory_parser = subparsers.add_parser("memory", help="记忆管理")
  390. memory_parser.add_argument("action", choices=["list", "search", "clean"], help="操作类型")
  391. memory_parser.add_argument("--query", help="搜索查询")
  392. args = parser.parse_args()
  393. if not args.command:
  394. parser.print_help()
  395. return
  396. try:
  397. cli = AceFlowCLI()
  398. if args.command == "describe":
  399. result = cli.describe(args.format)
  400. print(result)
  401. elif args.command == "suggest":
  402. result = cli.suggest(
  403. task=args.task,
  404. team_size=args.team_size,
  405. project_type=args.project_type,
  406. complexity=args.complexity,
  407. urgency=args.urgency
  408. )
  409. print(json.dumps(result, indent=2, ensure_ascii=False))
  410. elif args.command == "plan":
  411. result = cli.plan(
  412. project_type=args.project_type,
  413. team_size=args.team_size,
  414. complexity=args.complexity,
  415. urgency=args.urgency
  416. )
  417. print(json.dumps(result, indent=2, ensure_ascii=False))
  418. elif args.command == "track":
  419. result = cli.track(stage=args.stage)
  420. print(json.dumps(result, indent=2, ensure_ascii=False))
  421. elif args.command == "status":
  422. result = cli.status()
  423. print(json.dumps(result, indent=2, ensure_ascii=False))
  424. elif args.command == "memory":
  425. result = cli.memory(args.action, query=args.query)
  426. print(json.dumps(result, indent=2, ensure_ascii=False))
  427. except Exception as e:
  428. if args.verbose:
  429. import traceback
  430. traceback.print_exc()
  431. else:
  432. print(json.dumps({"error": str(e)}, indent=2, ensure_ascii=False))
  433. sys.exit(1)
  434. if __name__ == "__main__":
  435. main()