feat: improve generated agent prompts

This commit is contained in:
2026-06-02 12:49:55 +01:00
parent 318b9471ae
commit 7a1d36c03c
4 changed files with 281 additions and 18 deletions
+92 -2
View File
@@ -10,6 +10,7 @@ STATUS_RE = re.compile(r"^Status:\s*(.+)$", re.MULTILINE)
GOAL_RE = re.compile(r"^Goal:\s*(.+)", re.MULTILINE)
AC_RE = re.compile(r"^- (.+)$", re.MULTILINE)
GAP_RE = re.compile(r"^Implementation Gap:\s*(.+)", re.MULTILINE)
@dataclass
@@ -28,6 +29,12 @@ class Task:
def ac_lines(self) -> list[str]:
return AC_RE.findall(self.body)
def implementation_gap(self, root: Path) -> str | None:
match = GAP_RE.search(self.body)
if match and match.group(1).strip():
return match.group(1).strip()
return None
def read_tasks(root: Path) -> list[Task]:
tasks_path = root / "TASKS.md"
@@ -92,6 +99,12 @@ CONTEXT_FILES_TO_READ = [
"context/agent-guidelines.md",
]
TEST_CMD_SECTION_RE = re.compile(
r"^##\s*Test Commands\s*\n([\s\S]*?)(?=^##|\Z)",
re.MULTILINE,
)
BASH_BLOCK_RE = re.compile(r"```bash\n(.*?)```", re.DOTALL)
def _read_file_safe(root: Path, relative: str) -> str:
path = root / relative
@@ -100,6 +113,55 @@ def _read_file_safe(root: Path, relative: str) -> str:
return f"# {relative} — not found"
def _extract_test_commands_from_claude(root: Path) -> list[str]:
"""Extract bash test commands from CLAUDE.md Test Commands section."""
def _looks_like_command(line: str) -> bool:
"""Heuristic: keep lines that look like shell commands."""
if not line.strip():
return False
lower = line.lower()
# Skip markdown, prose, headings
if any(lower.startswith(p) for p in ("# ", "- ", "if ", "do not", "for ", "use ")) or lower in (
"validation commands",
):
return False
# Skip lines that look like sentences (contain spaces followed by lowercase words)
# but keep things like `source .venv/bin/activate` and `pip install ...`
parts = line.split()
if len(parts) <= 1:
return True
# If the first word is a known shell builtin / command prefix, accept it
known_prefixes = ("source", "cd", "ls", "cp", "mv", "rm", "mkdir", "echo", "grep",
"git", "pip", "python", "pytest", "rdb", "cat", "head", "tail",
"sed", "awk", "find", "install")
return parts[0].lower() in known_prefixes
claude_path = root / "CLAUDE.md"
if not claude_path.exists():
return []
text = claude_path.read_text(encoding="utf-8")
section_match = TEST_CMD_SECTION_RE.search(text)
if not section_match:
return []
section_text = section_match.group(1)
blocks = BASH_BLOCK_RE.findall(section_text)
seen: set[str] = set()
commands: list[str] = []
for block in blocks:
for line in block.strip().splitlines():
stripped = line.strip()
if not stripped:
continue
if _looks_like_command(stripped):
if stripped not in seen:
seen.add(stripped)
commands.append(stripped)
return commands
def generate_agent_prompt(root: Path) -> str:
task = get_next_task(root)
if not task:
@@ -128,9 +190,20 @@ def generate_agent_prompt(root: Path) -> str:
f"Status: {task.status}",
"",
f"Goal:\n{task.goal(root)}",
]
gap = task.implementation_gap(root)
if gap:
sections.extend([
"",
"Implementation Gap:",
gap,
])
sections.extend([
"",
"Acceptance Criteria:",
]
])
for line in task.ac_lines():
sections.append(f"- {line}")
@@ -141,8 +214,25 @@ def generate_agent_prompt(root: Path) -> str:
"",
"# Constraints",
"",
"- Implement ONE task only. Do not combine with other tasks.",
"- Inspect the codebase first, then edit.",
"- Do not repeatedly reread unchanged files.",
"- Limit your work to ONE small implementation step only. Do not combine tasks or features.",
"- Make the smallest useful change possible.",
])
test_cmds = _extract_test_commands_from_claude(root)
if test_cmds:
sections.extend([
"",
"---",
"",
"# Test Commands (from CLAUDE.md)",
"",
"Use these existing test commands:",
("```bash\n" + "\n".join(test_cmds) + "\n```\n"),
])
sections.extend([
"",
"---",
"",