-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprogress.py
More file actions
95 lines (75 loc) · 3.03 KB
/
Copy pathprogress.py
File metadata and controls
95 lines (75 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"""
Geração de progress.txt a partir do history.json.
O progress.txt serve como memória de longo prazo para o LLM local:
cada task concluída contribui com uma linha de lição aprendida,
permitindo que iterações futuras saltem a fase de descoberta.
Inspirado no padrão Ralph Loop (Geoffrey Huntley).
"""
from __future__ import annotations
from pathlib import Path
import checkpoint as ckpt
import config
from models import EventType, TaskStatus
def generate_progress(project_id: str) -> None:
"""
Lê history.json e tasks.json do projeto e gera/atualiza progress.txt
no diretório de estado do projeto.
Formato do progress.txt:
[task-001] Título da task
Tentativas: 3 | Último erro antes de passar: AssertionError: ...
Lição: passou após corrigir importação circular em models.py
Chamado pelo squire após cada task concluída.
"""
history = ckpt.load_history(project_id)
task_list = ckpt.load_tasks(project_id)
if not history or not task_list:
return
tasks_by_id = {t.id: t for t in task_list.tasks}
lines: list[str] = [
"# progress.txt — memória acumulada de iterações",
"# Gerado automaticamente pelo squire. Não editar manualmente.",
"",
]
# Agrupar eventos por task_id
events_by_task: dict[str, list] = {}
for event in history.events:
if event.task_id:
events_by_task.setdefault(event.task_id, []).append(event)
for task_id, events in events_by_task.items():
task = tasks_by_id.get(task_id)
if not task or task.status != TaskStatus.completed:
continue
# Contar tentativas de implementação
impl_attempts = sum(
1 for e in events
if e.type == EventType.implementation_cycle
)
# Último erro antes do teste passar
last_error: str | None = None
for e in reversed(events):
if e.type == EventType.tests_failed and e.summary:
last_error = e.summary[:200]
break
# Número de rejeições de homologação
rejections = sum(
1 for e in events
if e.type == EventType.homologation_failed
)
lines.append(f"[{task_id}] {task.title}")
lines.append(f" Tentativas: {impl_attempts} | Rejeições: {rejections}")
if last_error:
lines.append(f" Último erro: {last_error}")
if task.rejection_summaries:
lines.append(f" Feedbacks de homologação: {'; '.join(task.rejection_summaries[-2:])}")
lines.append("")
progress_path = config.project_dir(project_id) / "progress.txt"
progress_path.write_text("\n".join(lines), encoding="utf-8")
def load_progress(project_id: str) -> str:
"""
Lê progress.txt do projeto e retorna como string.
Retorna string vazia se não existir.
"""
progress_path = config.project_dir(project_id) / "progress.txt"
if not progress_path.exists():
return ""
return progress_path.read_text(encoding="utf-8")