diff --git a/core/task_pipeline.py b/core/task_pipeline.py index 0e5ea23..ad03714 100644 --- a/core/task_pipeline.py +++ b/core/task_pipeline.py @@ -117,31 +117,33 @@ class TaskPipeline: # 프로젝트 파일 읽기 (공용) # ────────────────────────────────────────── - def _read_recent_files(self, cutoff_seconds: int = 600) -> list[str]: - """최근 변경된 프로젝트 파일 읽기.""" + def _read_project_files(self) -> list[str]: + """프로젝트의 모든 텍스트 파일 읽기.""" import os - import time as _time - recent_files = [] - cutoff = _time.time() - cutoff_seconds + project_files = [] project_root = Path(self.project_path) - skip_dirs = {".git", "__pycache__", "node_modules", ".venv", "venv"} + skip_dirs = {".git", "__pycache__", "node_modules", ".venv", "venv", "dist", "build"} + binary_exts = {".png", ".jpg", ".jpeg", ".gif", ".ico", ".woff", ".woff2", ".ttf", + ".eot", ".mp3", ".mp4", ".zip", ".tar", ".gz", ".exe", ".dll", + ".so", ".pyc", ".pyo", ".db", ".sqlite"} for root, dirs, files in os.walk(self.project_path): dirs[:] = [d for d in dirs if d not in skip_dirs] for fname in files: fpath = Path(root) / fname + if fpath.suffix.lower() in binary_exts: + continue try: - if fpath.stat().st_mtime > cutoff: - rel = fpath.relative_to(project_root) - content = fpath.read_text(encoding="utf-8", errors="replace") - recent_files.append( - f"### {rel}\n```\n{content}\n```" - ) + rel = fpath.relative_to(project_root) + content = fpath.read_text(encoding="utf-8", errors="replace") + project_files.append( + f"### {rel}\n```\n{content}\n```" + ) except (OSError, UnicodeDecodeError): continue - return recent_files + return project_files # ────────────────────────────────────────── # Planner 자가 검증 (오케스트레이션) @@ -151,21 +153,10 @@ class TaskPipeline: self, user_request: str, plan: dict, code_outputs: list[str], ) -> dict: - """Planner가 자기 계획의 달성 여부를 검증. + """Planner가 자기 계획의 달성 여부를 에이전트 모드로 검증. - 실제 파일을 읽어서 계획이 충족됐는지 판단합니다. - 미달이면 추가 태스크를 생성합니다. - - Returns: - { - "satisfied": bool, - "feedback": "미충족 사유", - "additional_tasks": [...] (satisfied=false일 때) - } + 프로젝트 디렉토리에서 직접 파일을 읽어서 계획 충족 여부를 판단합니다. """ - recent_files = self._read_recent_files() - files_section = "\n\n".join(recent_files) if recent_files else "(파일 없음)" - agent_reports = "\n".join( f"--- Agent {i+1} ---\n{output}" for i, output in enumerate(code_outputs) @@ -175,12 +166,12 @@ class TaskPipeline: f"## 원래 사용자 요청\n{user_request}\n\n" f"## 내가 세운 계획\n{json.dumps(plan, ensure_ascii=False, indent=2)}\n\n" f"## 에이전트 보고\n{agent_reports}\n\n" - f"## 현재 프로젝트 파일\n{files_section}\n\n" f"## 판단 요청\n" - f"위 계획이 충족되었는지 판단하세요.\n" + f"현재 디렉토리의 프로젝트 파일을 직접 읽어서 계획이 충족되었는지 확인하세요.\n" + f"필요한 파일만 선택적으로 읽으세요.\n\n" f"충족되었으면 satisfied=true.\n" f"미충족이면 satisfied=false + 부족한 부분을 해결할 추가 태스크를 생성하세요.\n\n" - f"JSON 형식:\n" + f"반드시 아래 JSON만 출력하세요:\n" f"```json\n" f'{{\n' f' "satisfied": true|false,\n' @@ -192,7 +183,9 @@ class TaskPipeline: f"```" ) - response = await self.gemini.call("planner", prompt, timeout=180) + response = await self.gemini.call_agent( + "planner", prompt, cwd=self.project_path, timeout=180, + ) self._log("planner_verify", user_request, response) result = self._extract_json(response) @@ -203,7 +196,7 @@ class TaskPipeline: # ────────────────────────────────────────── async def batch_review(self, tasks: list[dict], code_outputs: list[str]) -> dict: - """에이전트가 생성/수정한 실제 파일을 리뷰.""" + """에이전트 모드로 프로젝트 파일을 직접 읽어 리뷰.""" task_summaries = [] for i, task in enumerate(tasks): title = task.get("title", task.get("description", f"Task {i+1}")) @@ -213,26 +206,17 @@ class TaskPipeline: for i, output in enumerate(code_outputs): agent_reports.append(f"--- Agent {i+1} 보고 ---\n{output}") - recent_files = self._read_recent_files() - - if not recent_files: - return { - "passed": True, - "summary": "파일 변경 없음 또는 삭제 작업 - 자동 통과", - "issues": [], - } - - files_section = "\n\n".join(recent_files) - prompt = ( f"## 요청된 태스크\n{chr(10).join(task_summaries)}\n\n" f"## 에이전트 보고\n{chr(10).join(agent_reports)}\n\n" - f"## 실제 생성/수정된 파일\n{files_section}\n\n" - f"위 파일들이 태스크 요구사항을 충족하는지 리뷰하세요." + f"현재 디렉토리의 프로젝트 파일을 직접 읽어서 리뷰하세요.\n" + f"필요한 파일만 선택적으로 확인하세요." ) - response = await self.gemini.call("reviewer", prompt, timeout=180) - self._log("batch_review", f"{len(tasks)} tasks, {len(recent_files)} files", response) + response = await self.gemini.call_agent( + "reviewer", prompt, cwd=self.project_path, timeout=180, + ) + self._log("batch_review", f"{len(tasks)} tasks", response) review = self._extract_json(response) return review or {"passed": True, "summary": response, "raw": response}