cursor_task_agent.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #!/usr/bin/env python3
  2. """
  3. Cursor任务执行Agent
  4. 这个脚本会定期检查task-manager MCP中的pending任务,
  5. 并自动通过Cursor CLI或API触发任务执行。
  6. 使用方法:
  7. 1. 直接运行:python scripts/cursor_task_agent.py
  8. 2. 作为后台服务运行:python scripts/cursor_task_agent.py --daemon
  9. 3. 单次检查:python scripts/cursor_task_agent.py --once
  10. """
  11. import json
  12. import time
  13. import argparse
  14. import logging
  15. import sys
  16. from pathlib import Path
  17. from datetime import datetime
  18. # 配置日志
  19. logging.basicConfig(
  20. level=logging.INFO,
  21. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  22. handlers=[
  23. logging.FileHandler('logs/cursor_task_agent.log'),
  24. logging.StreamHandler(sys.stdout)
  25. ]
  26. )
  27. logger = logging.getLogger('CursorTaskAgent')
  28. class CursorTaskAgent:
  29. """
  30. Cursor任务执行Agent
  31. 负责:
  32. 1. 定期从MCP获取pending任务
  33. 2. 为每个任务创建Cursor执行指令
  34. 3. 触发Cursor执行任务(通过创建临时提示文件)
  35. """
  36. def __init__(self, check_interval=300, workspace_path=None):
  37. """
  38. 初始化Agent
  39. Args:
  40. check_interval: 检查间隔(秒),默认300秒(5分钟)
  41. workspace_path: 工作区路径
  42. """
  43. self.check_interval = check_interval
  44. self.workspace_path = workspace_path or Path(__file__).parent.parent
  45. self.prompt_dir = self.workspace_path / '.cursor' / 'task_prompts'
  46. self.prompt_dir.mkdir(parents=True, exist_ok=True)
  47. logger.info(f"Cursor Task Agent initialized")
  48. logger.info(f"Workspace: {self.workspace_path}")
  49. logger.info(f"Prompt directory: {self.prompt_dir}")
  50. logger.info(f"Check interval: {self.check_interval}s")
  51. def get_pending_tasks_from_db(self):
  52. """
  53. 从数据库直接获取pending任务
  54. 注意:这里我们绕过MCP,直接连接数据库
  55. 因为MCP的轮询机制有限制
  56. """
  57. try:
  58. import psycopg2
  59. from psycopg2.extras import RealDictCursor
  60. # 从配置文件读取数据库连接信息
  61. config_file = self.workspace_path / 'mcp-servers' / 'task-manager' / 'config.json'
  62. with open(config_file, 'r', encoding='utf-8') as f:
  63. config = json.load(f)
  64. db_uri = config['database']['uri']
  65. # 连接数据库
  66. conn = psycopg2.connect(db_uri)
  67. cursor = conn.cursor(cursor_factory=RealDictCursor)
  68. # 查询pending任务
  69. cursor.execute("""
  70. SELECT task_id, task_name, task_description, status,
  71. code_name, code_path, create_time, create_by
  72. FROM task_list
  73. WHERE status = 'pending'
  74. ORDER BY create_time ASC
  75. """)
  76. tasks = cursor.fetchall()
  77. cursor.close()
  78. conn.close()
  79. logger.info(f"Found {len(tasks)} pending tasks")
  80. return [dict(task) for task in tasks]
  81. except Exception as e:
  82. logger.error(f"Failed to get pending tasks from database: {e}")
  83. return []
  84. def create_task_prompt(self, task):
  85. """
  86. 为任务创建Cursor提示文件
  87. 这个文件会告诉用户有新任务需要执行
  88. """
  89. prompt_file = self.prompt_dir / f"task_{task['task_id']}.md"
  90. prompt_content = f"""# 🔔 新任务通知
  91. **任务ID**: {task['task_id']}
  92. **任务名称**: {task['task_name']}
  93. **创建时间**: {task['create_time']}
  94. **创建者**: {task['create_by']}
  95. ---
  96. ## 📋 任务描述
  97. {task['task_description']}
  98. ---
  99. ## 🚀 执行指令
  100. 请在Cursor中执行以下操作来完成此任务:
  101. ### 方式1:使用MCP工具(推荐)
  102. 在Cursor Chat中输入:
  103. ```
  104. @task-manager 请执行task_id={task['task_id']}的任务
  105. ```
  106. 或者直接使用工具:
  107. ```
  108. 调用工具: execute_task
  109. 参数: {{
  110. "task_id": {task['task_id']},
  111. "auto_complete": true
  112. }}
  113. ```
  114. ### 方式2:手动执行
  115. 1. 阅读上述任务描述
  116. 2. 根据描述开发相应的Python代码
  117. 3. 完成后调用update_task_status工具更新状态
  118. ---
  119. **⚠️ 注意**:任务完成后,此提示文件将自动删除。
  120. """
  121. # 写入提示文件
  122. with open(prompt_file, 'w', encoding='utf-8') as f:
  123. f.write(prompt_content)
  124. logger.info(f"Created task prompt: {prompt_file}")
  125. return prompt_file
  126. def check_and_notify_tasks(self):
  127. """
  128. 检查pending任务并创建通知
  129. """
  130. logger.info("Checking for pending tasks...")
  131. tasks = self.get_pending_tasks_from_db()
  132. if not tasks:
  133. logger.info("No pending tasks found")
  134. return 0
  135. # 为每个任务创建提示文件
  136. for task in tasks:
  137. try:
  138. prompt_file = self.create_task_prompt(task)
  139. logger.info(f"Task {task['task_id']} ({task['task_name']}) - prompt created at {prompt_file}")
  140. except Exception as e:
  141. logger.error(f"Failed to create prompt for task {task['task_id']}: {e}")
  142. return len(tasks)
  143. def run_once(self):
  144. """
  145. 执行一次检查
  146. """
  147. logger.info("=" * 60)
  148. logger.info("Running single check...")
  149. count = self.check_and_notify_tasks()
  150. logger.info(f"Check completed. Found {count} pending tasks.")
  151. logger.info("=" * 60)
  152. return count
  153. def run_daemon(self):
  154. """
  155. 作为守护进程运行,定期检查任务
  156. """
  157. logger.info("=" * 60)
  158. logger.info("Starting Cursor Task Agent in daemon mode...")
  159. logger.info(f"Will check for new tasks every {self.check_interval} seconds")
  160. logger.info("Press Ctrl+C to stop")
  161. logger.info("=" * 60)
  162. try:
  163. while True:
  164. try:
  165. count = self.check_and_notify_tasks()
  166. logger.info(f"Next check in {self.check_interval} seconds...")
  167. time.sleep(self.check_interval)
  168. except KeyboardInterrupt:
  169. raise
  170. except Exception as e:
  171. logger.error(f"Error during check: {e}")
  172. logger.info(f"Retrying in {self.check_interval} seconds...")
  173. time.sleep(self.check_interval)
  174. except KeyboardInterrupt:
  175. logger.info("\n" + "=" * 60)
  176. logger.info("Cursor Task Agent stopped by user")
  177. logger.info("=" * 60)
  178. def main():
  179. """
  180. 主函数
  181. """
  182. parser = argparse.ArgumentParser(
  183. description='Cursor任务执行Agent - 自动检查并通知pending任务'
  184. )
  185. parser.add_argument(
  186. '--once',
  187. action='store_true',
  188. help='只执行一次检查,不持续运行'
  189. )
  190. parser.add_argument(
  191. '--daemon',
  192. action='store_true',
  193. help='作为守护进程运行(与--once互斥)'
  194. )
  195. parser.add_argument(
  196. '--interval',
  197. type=int,
  198. default=300,
  199. help='检查间隔(秒),默认300秒(5分钟)'
  200. )
  201. args = parser.parse_args()
  202. # 创建logs目录
  203. logs_dir = Path(__file__).parent.parent / 'logs'
  204. logs_dir.mkdir(exist_ok=True)
  205. # 创建Agent实例
  206. agent = CursorTaskAgent(check_interval=args.interval)
  207. # 根据参数运行
  208. if args.once:
  209. agent.run_once()
  210. else:
  211. # 默认或明确指定daemon模式
  212. agent.run_daemon()
  213. if __name__ == '__main__':
  214. main()