modify gemini template

This commit is contained in:
NINI
2026-04-28 01:51:20 +09:00
parent 949e0ab13c
commit 38291723f0
32 changed files with 523 additions and 320 deletions
+37 -36
View File
@@ -1,15 +1,14 @@
#!/usr/bin/env python3
"""
Codex Harness Step Executor — phase 내 step을 순차 실행하고 자가 교정한다.
Gemini Harness Step Executor — phase 내 step을 순차 실행하고 자가 교정한다.
Usage:
python3 scripts/execute.py <phase-dir> [--push]
python scripts/execute.py <phase-dir> [--push]
"""
import argparse
import contextlib
import json
import os
import subprocess
import sys
import threading
@@ -51,7 +50,7 @@ def progress_indicator(label: str):
class StepExecutor:
"""Phase 디렉토리 안의 step들을 Codex로 순차 실행하는 하네스."""
"""Phase 디렉토리 안의 step들을 Gemini CLI로 순차 실행하는 하네스."""
MAX_RETRIES = 3
FEAT_MSG = "feat({phase}): step {num}{name}"
@@ -115,7 +114,7 @@ class StepExecutor:
r = self._run_git("rev-parse", "--abbrev-ref", "HEAD")
if r.returncode != 0:
print(f" ERROR: git을 사용할 수 없거나 git repo가 아닙니다.")
print(" ERROR: git을 사용할 수 없거나 git repo가 아닙니다.")
print(f" {r.stderr.strip()}")
sys.exit(1)
@@ -128,7 +127,7 @@ class StepExecutor:
if r.returncode != 0:
print(f" ERROR: 브랜치 '{branch}' checkout 실패.")
print(f" {r.stderr.strip()}")
print(f" Hint: 변경사항을 stash하거나 commit한 후 다시 시도하세요.")
print(" Hint: 변경사항을 stash하거나 commit한 후 다시 시도하세요.")
sys.exit(1)
print(f" Branch: {branch}")
@@ -176,9 +175,9 @@ class StepExecutor:
def _load_guardrails(self) -> str:
sections = []
agents_md = ROOT / "AGENTS.md"
if agents_md.exists():
sections.append(f"## 프로젝트 규칙 (AGENTS.md)\n\n{agents_md.read_text(encoding='utf-8')}")
gemini_md = ROOT / "GEMINI.md"
if gemini_md.exists():
sections.append(f"## 프로젝트 규칙 (GEMINI.md)\n\n{gemini_md.read_text(encoding='utf-8')}")
docs_dir = ROOT / "docs"
if docs_dir.is_dir():
for doc in sorted(docs_dir.glob("*.md")):
@@ -201,29 +200,29 @@ class StepExecutor:
retry_section = ""
if prev_error:
retry_section = (
f"\n## 이전 시도 실패 — 아래 에러를 반드시 참고하여 수정하라\n\n"
"\n## 이전 시도 실패 — 아래 에러를 반드시 참고하여 수정하라\n\n"
f"{prev_error}\n\n---\n\n"
)
return (
f"당신은 {self._project} 프로젝트의 Codex 문서 작성 에이전트입니다. 아래 step을 수행하세요.\n\n"
f"당신은 {self._project} 프로젝트의 Gemini CLI 문서 작성 에이전트입니다. 아래 step을 수행하세요.\n\n"
f"{guardrails}\n\n---\n\n"
f"{step_context}{retry_section}"
f"## 작업 규칙\n\n"
f"1. 이전 step에서 작성된 문서와 메모를 확인하고 일관성을 유지하라.\n"
f"2. 이 step에 명시된 작업만 수행하라. 추가 산출물이나 임의 요구사항을 만들지 마라.\n"
f"3. 기존 문서 구조와 피드백 기록을 깨뜨리지 마라.\n"
f"4. AC(Acceptance Criteria) 검증을 직접 실행하라.\n"
"## 작업 규칙\n\n"
"1. 이전 step에서 작성된 문서와 메모를 확인하고 일관성을 유지하라.\n"
"2. 이 step에 명시된 작업만 수행하라. 추가 산출물이나 임의 요구사항을 만들지 마라.\n"
"3. 기존 문서 구조와 피드백 기록을 깨뜨리지 마라.\n"
"4. AC(Acceptance Criteria) 검증을 직접 실행하라.\n"
f"5. /phases/{self._phase_dir_name}/index.json의 해당 step status를 업데이트하라:\n"
f" - AC 통과 \"completed\" + \"summary\" 필드에 이 step의 산출물을 한 줄로 요약\n"
f" - {self.MAX_RETRIES}회 수정 시도 후에도 실패 \"error\" + \"error_message\" 기록\n"
f" - 사용자 개입이 필요한 경우 (API 키, 인증, 수동 설정 등) → \"blocked\" + \"blocked_reason\" 기록 후 즉시 중단\n"
f"6. 직접 git commit하지 마라. commit은 scripts/execute.py가 step 완료 후 수행한다.\n"
f"7. 병렬 조사나 독립 리뷰가 필요하고 step에서 허용했다면 .codex/agents의 custom agent 역할을 활용하라.\n\n---\n\n"
" - AC 통과 -> \"completed\" + \"summary\" 필드에 이 step의 산출물을 한 줄로 요약\n"
f" - {self.MAX_RETRIES}회 수정 시도 후에도 실패 -> \"error\" + \"error_message\" 기록\n"
" - 사용자 개입이 필요한 경우 -> \"blocked\" + \"blocked_reason\" 기록 후 즉시 중단\n"
"6. 직접 git commit하지 마라. commit은 scripts/execute.py가 step 완료 후 수행한다.\n"
"7. 병렬 조사나 독립 리뷰가 필요하고 step에서 허용했다면 .gemini/agents의 subagent 역할을 활용하라.\n\n---\n\n"
)
# --- Codex 호출 ---
# --- Gemini CLI invocation ---
def _invoke_codex(self, step: dict, preamble: str) -> dict:
def _invoke_gemini(self, step: dict, preamble: str) -> dict:
step_num, step_name = step["step"], step["name"]
step_file = self._phase_dir / f"step{step_num}.md"
@@ -232,7 +231,7 @@ class StepExecutor:
sys.exit(1)
prompt = preamble + step_file.read_text(encoding="utf-8")
cmd = ["codex", "exec", "--skip-git-repo-check", "--full-auto", "--json", "-"]
cmd = ["gemini", "--output-format", "json", "--approval-mode", "yolo"]
try:
result = subprocess.run(
@@ -246,18 +245,20 @@ class StepExecutor:
timeout=1800,
)
except FileNotFoundError:
print("\n ERROR: Codex CLI를 찾을 수 없습니다. `codex --version`이 실행되는지 확인하세요.")
print("\n ERROR: Gemini CLI를 찾을 수 없습니다. `gemini --version`이 실행되는지 확인하세요.")
sys.exit(1)
if result.returncode != 0:
print(f"\n WARN: Codex가 비정상 종료됨 (code {result.returncode})")
print(f"\n WARN: Gemini CLI가 비정상 종료됨 (code {result.returncode})")
if result.stderr:
print(f" stderr: {result.stderr[:500]}")
output = {
"step": step_num, "name": step_name,
"step": step_num,
"name": step_name,
"exitCode": result.returncode,
"stdout": result.stdout, "stderr": result.stderr,
"stdout": result.stdout,
"stderr": result.stderr,
}
out_path = self._phase_dir / f"step{step_num}-output.json"
with open(out_path, "w", encoding="utf-8") as f:
@@ -269,10 +270,10 @@ class StepExecutor:
def _print_header(self):
print(f"\n{'='*60}")
print(f" Codex Harness Step Executor")
print(" Gemini Harness Step Executor")
print(f" Phase: {self._phase_name} | Steps: {self._total}")
if self._auto_push:
print(f" Auto-push: enabled")
print(" Auto-push: enabled")
print(f"{'='*60}")
def _check_blockers(self):
@@ -281,12 +282,12 @@ class StepExecutor:
if s["status"] == "error":
print(f"\n ✗ Step {s['step']} ({s['name']}) failed.")
print(f" Error: {s.get('error_message', 'unknown')}")
print(f" Fix and reset status to 'pending' to retry.")
print(" Fix and reset status to 'pending' to retry.")
sys.exit(1)
if s["status"] == "blocked":
print(f"\n ⏸ Step {s['step']} ({s['name']}) blocked.")
print(f" Reason: {s.get('blocked_reason', 'unknown')}")
print(f" Resolve and reset status to 'pending' to retry.")
print(" Resolve and reset status to 'pending' to retry.")
sys.exit(2)
if s["status"] != "pending":
break
@@ -315,7 +316,7 @@ class StepExecutor:
tag += f" [retry {attempt}/{self.MAX_RETRIES}]"
with progress_indicator(tag) as pi:
self._invoke_codex(step, preamble)
self._invoke_gemini(step, preamble)
elapsed = int(pi.elapsed)
index = self._read_json(self._index_file)
@@ -368,7 +369,7 @@ class StepExecutor:
self._update_top_index("error")
sys.exit(1)
return False # unreachable
return False
def _execute_all_steps(self, guardrails: str):
while True:
@@ -414,8 +415,8 @@ class StepExecutor:
def main():
parser = argparse.ArgumentParser(description="Codex Harness Step Executor")
parser.add_argument("phase_dir", help="Phase directory name (e.g. 0-mvp)")
parser = argparse.ArgumentParser(description="Gemini Harness Step Executor")
parser.add_argument("phase_dir", help="Phase directory name (e.g. 0-document)")
parser.add_argument("--push", action="store_true", help="Push branch after completion")
args = parser.parse_args()