feat(ralph): add autonomous project execution system
- router.py: add !approve, !status, !stop, !propose commands for project lifecycle management - approved-tasks.json: coordination schema for evening→night→morning pipeline - tools/ralph/: ralph.sh loop, prompt.md, prd-template.json - cron/jobs.json: enable morning-report, evening-report, night-execute (23:00 opus) Evening-report proposes features to approved-tasks.json as 'pending'; Marius approves via !approve; night-execute launches ralph.sh per project. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
4
approved-tasks.json
Normal file
4
approved-tasks.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"projects": [],
|
||||
"last_updated": null
|
||||
}
|
||||
@@ -179,12 +179,12 @@
|
||||
},
|
||||
{
|
||||
"name": "morning-report",
|
||||
"cron": "30 9 * * *",
|
||||
"cron": "30 8 * * *",
|
||||
"channel": "echo-work",
|
||||
"model": "sonnet",
|
||||
"prompt": "RAPORT DIMINEAȚĂ - trimite pe EMAIL (Gmail: mmarius28@gmail.com)\n\n## CALENDAR\nVerifică calendarul:\n```bash\ncd ~/echo-core && source venv/bin/activate && python3 tools/calendar_check.py today\npython3 tools/calendar_check.py travel\npython3 tools/calendar_check.py week\n```\n\n## CITEȘTE CONTEXT\n- USER.md pentru programul lui Marius (luni-joi 15-16 liber)\n- memory/kb/insights/ pentru propuneri (ultimele 3 zile)\n- memory/approved-tasks.md pentru status proiecte/features\n\n## FORMAT EMAIL HTML\n- Font: 16px text, 18px titluri\n- Culori: albastru (#dbeafe) DONE, gri (#f3f4f6) PROGRAMAT, verde (#d1fae5) PROJECTS\n- Link-uri vizibile\n\n## STRUCTURA RAPORT\n\n### 1. CALENDAR\n- 📅 **AZI:** [evenimente]\n- 📅 **MÂINE:** [evenimente]\n- 📅 **PESTE 2 ZILE:** [dacă e GRUP, NLP, meeting mare]\n- 🚂 **TRAVEL:** Reminders bilete+cazare\n\n### 2. PROIECTE/FEATURES NOAPTEA 💻\n\nCitesc approved-tasks.md și raportez ce s-a realizat:\n\n**Format pentru fiecare proiect/feature [x]:**\n\n```html\n<div style=\"background: #d1fae5; padding: 15px; margin: 10px 0; border-radius: 8px;\">\n <h3>✅ P1 - Nume Proiect</h3>\n \n <p><strong>Status:</strong> X/Y stories complete</p>\n \n <p><strong>Stories realizate:</strong></p>\n <ul>\n <li>✅ US-001: Titlu story - implementat cu succes</li>\n <li>✅ US-002: Titlu story - quality checks pass</li>\n <li>🔄 US-003: Titlu story - în progres (blocat pe dependency)</li>\n </ul>\n \n <p><strong>Link:</strong> <a href=\"https://gitea.romfast.ro/romfast/PROJECT-NAME\">gitea.romfast.ro/romfast/PROJECT-NAME</a></p>\n \n <p><strong>Learnings:</strong> [din progress.txt - ce patterns am descoperit]</p>\n \n <p><strong>Next steps:</strong> [ce rămâne de făcut]</p>\n</div>\n```\n\n**Dacă NU s-au executat proiecte/features:**\n- Sari peste această secțiune\n\n### 3. STATUS GENERAL\n- Ce s-a făcut ieri (joburi, taskuri)\n- Git status ~/clawd\n- Joburi executate (YouTube, insights, etc.)\n\n### 4. PROPUNERI CU ZI ȘI ORĂ!\n\n**OBLIGATORIU:** Fiecare propunere TU+EU sau FAC TU trebuie să aibă ZI și ORĂ concrete!\n\nCategorii:\n- 🤖 **FAC EU** (0 efort) - execut singur\n- 🤝 **TU+EU** (eu pregătesc) - cu zi/oră!\n- 👤 **FAC TU** (template gata) - cu zi/oră!\n\nExemplu:\n- **A1 - Sesiune Dizolvare Vină** 🤝 TU+EU\n 📅 **Marți 3 feb, 15:00-15:30**\n Context + link sursă\n\nReguli programare:\n- Luni-Joi 15:00-16:00 = slot liber\n- Vineri-Duminică = NLP, evită\n- Verifică calendar să nu fie ocupat\n\n### 5. INSIGHTS DISPONIBILE\n\nListează insights-uri [ ] nepropuse încă (format scurt).\n\n### 6. CUM RĂSPUNZI\n- DA = aprob toate (cu zilele/orele propuse)\n- 1 pentru A1,A2 = execut ACUM\n- 2 pentru A3 = programez noapte\n- 3 pentru A5 = skip\n- Alt orar = \"A1 miercuri nu marți\"\n\n## TRIMITERE\npython3 /home/moltbot/echo-core/tools/email_send.py \"mmarius28@gmail.com\" \"Raport Dimineata DATA\" \"HTML_CONTENT\"\n\nNU trimite pe Discord - doar email.",
|
||||
"enabled": true,
|
||||
"prompt": "RAPORT DIMINEAȚĂ - trimite pe EMAIL (Gmail: mmarius28@gmail.com)\n\n## CALENDAR\nVerifică calendarul:\n```bash\ncd ~/echo-core && source venv/bin/activate && python3 tools/calendar_check.py today\npython3 tools/calendar_check.py travel\npython3 tools/calendar_check.py week\n```\n\n## CITEȘTE CONTEXT\n- USER.md pentru programul lui Marius (luni-joi 15-16 liber)\n- memory/kb/insights/ pentru propuneri (ultimele 3 zile)\n- /home/moltbot/echo-core/approved-tasks.json pentru status proiecte/features (câmpurile: name, status, started_at, pid)\n\n## FORMAT EMAIL HTML\n- Font: 16px text, 18px titluri\n- Culori: albastru (#dbeafe) DONE, gri (#f3f4f6) PROGRAMAT, verde (#d1fae5) PROJECTS\n- Link-uri vizibile\n\n## STRUCTURA RAPORT\n\n### 1. CALENDAR\n- 📅 **AZI:** [evenimente]\n- 📅 **MÂINE:** [evenimente]\n- 📅 **PESTE 2 ZILE:** [dacă e GRUP, NLP, meeting mare]\n- 🚂 **TRAVEL:** Reminders bilete+cazare\n\n### 2. PROIECTE/FEATURES NOAPTEA 💻\n\nCitesc /home/moltbot/echo-core/approved-tasks.json și raportez ce s-a realizat:\n(statusuri: pending, approved, running, complete, failed, stopped)\nPentru stories done/total: citesc /home/moltbot/workspace/{name}/scripts/ralph/prd.json\n\n**Format pentru fiecare proiect/feature [x]:**\n\n```html\n<div style=\"background: #d1fae5; padding: 15px; margin: 10px 0; border-radius: 8px;\">\n <h3>✅ P1 - Nume Proiect</h3>\n \n <p><strong>Status:</strong> X/Y stories complete</p>\n \n <p><strong>Stories realizate:</strong></p>\n <ul>\n <li>✅ US-001: Titlu story - implementat cu succes</li>\n <li>✅ US-002: Titlu story - quality checks pass</li>\n <li>🔄 US-003: Titlu story - în progres (blocat pe dependency)</li>\n </ul>\n \n <p><strong>Link:</strong> <a href=\"https://gitea.romfast.ro/romfast/PROJECT-NAME\">gitea.romfast.ro/romfast/PROJECT-NAME</a></p>\n \n <p><strong>Learnings:</strong> [din progress.txt - ce patterns am descoperit]</p>\n \n <p><strong>Next steps:</strong> [ce rămâne de făcut]</p>\n</div>\n```\n\n**Dacă NU s-au executat proiecte/features:**\n- Sari peste această secțiune\n\n### 3. STATUS GENERAL\n- Ce s-a făcut ieri (joburi, taskuri)\n- Git status ~/clawd\n- Joburi executate (YouTube, insights, etc.)\n\n### 4. PROPUNERI CU ZI ȘI ORĂ!\n\n**OBLIGATORIU:** Fiecare propunere TU+EU sau FAC TU trebuie să aibă ZI și ORĂ concrete!\n\nCategorii:\n- 🤖 **FAC EU** (0 efort) - execut singur\n- 🤝 **TU+EU** (eu pregătesc) - cu zi/oră!\n- 👤 **FAC TU** (template gata) - cu zi/oră!\n\nExemplu:\n- **A1 - Sesiune Dizolvare Vină** 🤝 TU+EU\n 📅 **Marți 3 feb, 15:00-15:30**\n Context + link sursă\n\nReguli programare:\n- Luni-Joi 15:00-16:00 = slot liber\n- Vineri-Duminică = NLP, evită\n- Verifică calendar să nu fie ocupat\n\n### 5. INSIGHTS DISPONIBILE\n\nListează insights-uri [ ] nepropuse încă (format scurt).\n\n### 6. CUM RĂSPUNZI\n- DA = aprob toate (cu zilele/orele propuse)\n- 1 pentru A1,A2 = execut ACUM\n- 2 pentru A3 = programez noapte\n- 3 pentru A5 = skip\n- Alt orar = \"A1 miercuri nu marți\"\n\n## TRIMITERE\npython3 /home/moltbot/echo-core/tools/email_send.py \"mmarius28@gmail.com\" \"Raport Dimineata DATA\" \"HTML_CONTENT\"\n\nNU trimite pe Discord - doar email.",
|
||||
"allowed_tools": [],
|
||||
"enabled": false,
|
||||
"last_run": null,
|
||||
"last_status": null,
|
||||
"next_run": null
|
||||
@@ -194,9 +194,9 @@
|
||||
"cron": "0 21 * * *",
|
||||
"channel": "echo-work",
|
||||
"model": "sonnet",
|
||||
"prompt": "RAPORT SEARĂ - trimite pe EMAIL (Gmail: mmarius28@gmail.com)\n\n## CALENDAR\nVerifică ce ai mâine și săptămâna:\n```bash\ncd ~/echo-core && source venv/bin/activate && python3 tools/calendar_check.py today\npython3 tools/calendar_check.py week\n```\n\n## CITEȘTE CONTEXT\n- USER.md pentru programul lui Marius (luni-joi 15-16 liber, vineri-dum NLP)\n- memory/kb/insights/YYYY-MM-DD.md pentru propuneri insights\n- memory/kb/youtube/ și memory/kb/articole/ pentru inspirație proiecte\n- memory/approved-tasks.md pentru status proiecte existente\n\n## FORMAT EMAIL HTML\n- Font: 16px text, 18px titluri\n- Culori: albastru (#dbeafe) DONE, gri (#f3f4f6) PROGRAMAT, verde (#d1fae5) PROJECTS\n- Link-uri vizibile\n\n## STRUCTURA RAPORT\n\n### 1. MÂINE\n- 📅 Evenimente calendar\n- 🚂 Travel reminders\n\n### 2. STATUS\n- Ce s-a făcut azi\n- Git status\n\n### 3. PROPUNERI CU ZI ȘI ORĂ!\n\n**OBLIGATORIU:** Fiecare propunere TU+EU sau FAC TU trebuie să aibă ZI și ORĂ concrete!\n\nReguli programare:\n- Luni-Joi 15:00-16:00 = slot liber\n- Vineri-Duminică = NLP, evită\n- Verifică calendar să nu fie ocupat\n- Sesiuni scurte: 15-30 min\n\n### 4. PROGRAME/PROIECTE PRACTICE 💻\n\n**CONTEXT OBLIGATORIU - citește înainte de a propune:**\n\n**Proiecte existente (PRIORITARE pentru features):**\n- **roa2web** (gitea.romfast.ro/romfast/roa2web) - FastAPI+Vue.js+Telegram bot\n - Are deja: balanță, facturi, trezorerie\n - Lipsesc: validări declarații ANAF, facturare valută/taxare inversă, notificări\n - Rapoarte ROA noi → FEATURE în roa2web, NU proiect separat!\n- **Chatbot Maria** (Flowise pe LXC 104, ngrok → romfast.ro/chatbot_maria.html)\n - Document store: XML, MD | Groq gratuit + Ollama embeddings + FAISS\n - Problema: răspunsuri nu sunt suficient de bune\n - Angajatul nou poate menține documentația (scrie TXT, trebuie converter)\n - Clientii îl accesează din programele ROA direct\n\n**Întrebări frecvente clienți (surse de proiecte):**\n- Erori validare declarații ANAF (D406, D394, D100 etc.)\n- Cum facturez în valută cu taxare inversă?\n- Probleme la instalări, inițializări firme noi, configurări\n\n**Reguli propuneri (80/20 STRICT):**\n- Impact mare pentru Marius → apoi pentru clienți ERP ROA\n- Inspirat din discovery (YouTube, articole, insights procesate)\n- Features roa2web > proiecte noi (integrare în existent)\n- Proiecte independente doar dacă NU se potrivesc în roa2web/Flowise\n\n**A. FEATURES PROIECTE EXISTENTE (2-3, PRIORITAR):**\n\nFormat:\n```\n### ⚡ F1 - Feature pentru [roa2web/chatbot]\n**Ce face:** Descriere scurtă\n**De ce:** Ce problemă rezolvă (ex: \"clienții întreabă X de 5 ori/săptămână\")\n**Complexitate:** S/M/L\n**Proiect:** roa2web / chatbot-maria\n```\n\n**B. PROIECTE NOI (max 1, doar dacă nu se integrează în existente):**\n\nFormat:\n```\n### 💻 P1 - Nume Proiect\n**De ce:** Cum se leagă de nevoile lui Marius/clienți\n**Impact:** Pentru Marius + pentru clienți\n**Efort:** Ore/zile realist\n**Stack:** Simplu (80/20)\n**Sursă:** [Link nota KB]\n```\n\n**NU propune:**\n- Proiecte complexe fără beneficiu clar\n- Proiecte duplicat cu ce există deja\n- Rapoarte ROA ca proiect separat (→ feature roa2web)\n\n### 5. INSIGHTS DISPONIBILE\nListează insights-uri [ ] nepropuse încă (format scurt).\n\n### 6. CUM RĂSPUNZI\n- DA = aprob toate (cu zilele/orele propuse)\n- 1 pentru A1,A2 = execut ACUM\n- 2 pentru A3 = programez noapte\n- 3 pentru A5 = skip\n- **F pentru F1,F3** = implementează features (joburi noapte)\n- **P pentru P1** = creează proiect nou (job noapte)\n- Alt orar = \"A1 miercuri nu marți\"\n\n## IMPLEMENTARE PROIECTE APROBATE\n\nCând Marius aprobă cu F sau P:\n1. Adaugă în memory/approved-tasks.md secțiunea \"Noaptea asta\"\n2. Night-execute (23:00) va:\n - Pentru proiecte noi: genera PRD cu ralph_prd_generator.py → ralph.sh\n - Pentru features roa2web: adaugă stories în prd.json existent → ralph.sh\n - Pentru chatbot: generează documentație nouă și uploadează în Flowise\n\n## TRIMITERE\npython3 /home/moltbot/echo-core/tools/email_send.py \"mmarius28@gmail.com\" \"Raport Seara DATA\" \"HTML_CONTENT\"\n\nNU trimite pe Discord - doar email.",
|
||||
"enabled": true,
|
||||
"prompt": "RAPORT SEARĂ - trimite pe EMAIL (Gmail: mmarius28@gmail.com)\n\n## CALENDAR\nVerifică ce ai mâine și săptămâna:\n```bash\ncd ~/echo-core && source venv/bin/activate && python3 tools/calendar_check.py today\npython3 tools/calendar_check.py week\n```\n\n## CITEȘTE CONTEXT\n- USER.md pentru programul lui Marius (luni-joi 15-16 liber, vineri-dum NLP)\n- memory/kb/insights/YYYY-MM-DD.md pentru propuneri insights\n- memory/kb/youtube/ și memory/kb/articole/ pentru inspirație proiecte\n- /home/moltbot/echo-core/approved-tasks.json pentru status proiecte existente (câmpurile: name, status, proposed_at)\n\n## FORMAT EMAIL HTML\n- Font: 16px text, 18px titluri\n- Culori: albastru (#dbeafe) DONE, gri (#f3f4f6) PROGRAMAT, verde (#d1fae5) PROJECTS\n- Link-uri vizibile\n\n## STRUCTURA RAPORT\n\n### 1. MÂINE\n- 📅 Evenimente calendar\n- 🚂 Travel reminders\n\n### 2. STATUS\n- Ce s-a făcut azi\n- Git status\n\n### 3. PROPUNERI CU ZI ȘI ORĂ!\n\n**OBLIGATORIU:** Fiecare propunere TU+EU sau FAC TU trebuie să aibă ZI și ORĂ concrete!\n\nReguli programare:\n- Luni-Joi 15:00-16:00 = slot liber\n- Vineri-Duminică = NLP, evită\n- Verifică calendar să nu fie ocupat\n- Sesiuni scurte: 15-30 min\n\n### 4. PROGRAME/PROIECTE PRACTICE 💻\n\n**CONTEXT OBLIGATORIU - citește înainte de a propune:**\n\n**Proiecte existente (PRIORITARE pentru features):**\n- **roa2web** (gitea.romfast.ro/romfast/roa2web) - FastAPI+Vue.js+Telegram bot\n - Are deja: balanță, facturi, trezorerie\n - Lipsesc: validări declarații ANAF, facturare valută/taxare inversă, notificări\n - Rapoarte ROA noi → FEATURE în roa2web, NU proiect separat!\n- **Chatbot Maria** (Flowise pe LXC 104, ngrok → romfast.ro/chatbot_maria.html)\n - Document store: XML, MD | Groq gratuit + Ollama embeddings + FAISS\n - Problema: răspunsuri nu sunt suficient de bune\n - Angajatul nou poate menține documentația (scrie TXT, trebuie converter)\n - Clientii îl accesează din programele ROA direct\n\n**Întrebări frecvente clienți (surse de proiecte):**\n- Erori validare declarații ANAF (D406, D394, D100 etc.)\n- Cum facturez în valută cu taxare inversă?\n- Probleme la instalări, inițializări firme noi, configurări\n\n**Reguli propuneri (80/20 STRICT):**\n- Impact mare pentru Marius → apoi pentru clienți ERP ROA\n- Inspirat din discovery (YouTube, articole, insights procesate)\n- Features roa2web > proiecte noi (integrare în existent)\n- Proiecte independente doar dacă NU se potrivesc în roa2web/Flowise\n\n**A. FEATURES PROIECTE EXISTENTE (2-3, PRIORITAR):**\n\nFormat:\n```\n### ⚡ F1 - Feature pentru [roa2web/chatbot]\n**Ce face:** Descriere scurtă\n**De ce:** Ce problemă rezolvă (ex: \"clienții întreabă X de 5 ori/săptămână\")\n**Complexitate:** S/M/L\n**Proiect:** roa2web / chatbot-maria\n```\n\n**B. PROIECTE NOI (max 1, doar dacă nu se integrează în existente):**\n\nFormat:\n```\n### 💻 P1 - Nume Proiect\n**De ce:** Cum se leagă de nevoile lui Marius/clienți\n**Impact:** Pentru Marius + pentru clienți\n**Efort:** Ore/zile realist\n**Stack:** Simplu (80/20)\n**Sursă:** [Link nota KB]\n```\n\n**NU propune:**\n- Proiecte complexe fără beneficiu clar\n- Proiecte duplicat cu ce există deja\n- Rapoarte ROA ca proiect separat (→ feature roa2web)\n\n### 5. INSIGHTS DISPONIBILE\nListează insights-uri [ ] nepropuse încă (format scurt).\n\n### 6. CUM RĂSPUNZI\n- DA = aprob toate (cu zilele/orele propuse)\n- 1 pentru A1,A2 = execut ACUM\n- 2 pentru A3 = programez noapte\n- 3 pentru A5 = skip\n- **F pentru F1,F3** = implementează features (joburi noapte)\n- **P pentru P1** = creează proiect nou (job noapte)\n- Alt orar = \"A1 miercuri nu marți\"\n\n## IMPLEMENTARE PROIECTE APROBATE\n\nCând propui features (F) sau proiecte (P), adaugă-le automat în /home/moltbot/echo-core/approved-tasks.json cu status 'pending':\n```bash\npython3 -c \"\nimport json, datetime\nf = open('/home/moltbot/echo-core/approved-tasks.json')\ndata = json.load(f); f.close()\ndata['projects'].append({'name': 'SLUG-PROIECT', 'description': 'DESCRIERE', 'status': 'pending', 'proposed_at': datetime.datetime.utcnow().isoformat(), 'approved_at': None, 'started_at': None, 'pid': None})\ndata['last_updated'] = datetime.datetime.utcnow().isoformat()\nopen('/home/moltbot/echo-core/approved-tasks.json', 'w').write(json.dumps(data, indent=2))\n\"\n```\n\nÎn email, arată lui Marius comanda de aprobare:\n`!approve SLUG-PROIECT` (trimite pe Discord/Telegram la Echo)\n\nNight-execute (23:00) va:\n - genera PRD cu ralph_prd_generator.py dacă nu există prd.json\n - lansa ralph.sh 15 iterații pentru fiecare proiect aprobat\n\n## TRIMITERE\npython3 /home/moltbot/echo-core/tools/email_send.py \"mmarius28@gmail.com\" \"Raport Seara DATA\" \"HTML_CONTENT\"\n\nNU trimite pe Discord - doar email.",
|
||||
"allowed_tools": [],
|
||||
"enabled": false,
|
||||
"last_run": null,
|
||||
"last_status": null,
|
||||
"next_run": null
|
||||
@@ -269,8 +269,24 @@
|
||||
"prompt": "Heartbeat check. Rulează src/heartbeat.py printr-un scurt raport de status.\nDacă nu e nimic de raportat (email=0, calendar nu are evenimente <2h, kb ok), răspunde doar cu HEARTBEAT_OK și oprește-te — nu trimite mesaj.\nDacă e ceva: raport scurt pe Discord #echo-work.",
|
||||
"allowed_tools": [],
|
||||
"enabled": true,
|
||||
"last_run": "2026-04-26T08:00:00.003149+00:00",
|
||||
"last_run": "2026-04-26T14:00:00.003075+00:00",
|
||||
"last_status": "ok",
|
||||
"next_run": "2026-04-26T10:00:00+00:00"
|
||||
"next_run": "2026-04-26T16:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "night-execute",
|
||||
"cron": "0 23 * * *",
|
||||
"channel": "echo-work",
|
||||
"model": "opus",
|
||||
"enabled": true,
|
||||
"prompt": "NIGHT-EXECUTE - Implementare autonoma proiecte aprobate\n\n## PASUL 1: Citeste proiectele aprobate\n\nCiteste /home/moltbot/echo-core/approved-tasks.json\nSelecteaza proiectele cu status='approved'\nDaca nu sunt proiecte aprobate: raporteaza pe Discord si opreste-te.\n\n## PASUL 2: Pentru fiecare proiect aprobat\n\n1. Verifica daca workspace-ul exista: /home/moltbot/workspace/{name}\n - Daca nu: git clone https://gitea.romfast.ro/romfast/{name}.git /home/moltbot/workspace/{name}\n\n2. Verifica daca prd.json exista: /home/moltbot/workspace/{name}/scripts/ralph/prd.json\n - Daca nu: ruleaza generatorul PRD:\n source .venv/bin/activate\n python3 tools/ralph_prd_generator.py \"{name}\" \"{description}\" /home/moltbot/workspace\n\n3. Lanseaza Ralph loop:\n cd /home/moltbot/workspace/{name}\n chmod +x scripts/ralph/ralph.sh\n mkdir -p scripts/ralph/logs\n nohup ./scripts/ralph/ralph.sh 15 > scripts/ralph/logs/ralph-$(date +%Y%m%d).log 2>&1 &\n echo $! > scripts/ralph/.ralph.pid\n\n4. Actualizeaza approved-tasks.json:\n - status: 'running'\n - started_at: timestamp curent\n - pid: PID din .ralph.pid\n\n## PASUL 3: Raport Discord\n\nTrimite pe echo-work:\n- Cate proiecte au pornit\n- PID-urile lor\n- 'morning-report va raporta progresul la 08:30'\n\n## REGULI IMPORTANTE\n\n- Nu modifica niciodata src/router.py, src/claude_session.py sau alte fisiere core echo-core prin Ralph\n- echo-core self-improvement NUMAI pe branch ralph/echo-improve, nu pe master\n- Daca ralph.sh esueaza: log in approved-tasks.json (status: failed, error: mesaj)\n- Delay 5 secunde intre proiecte pentru a evita rate limiting\n",
|
||||
"allowed_tools": [
|
||||
"Bash",
|
||||
"Read",
|
||||
"Write"
|
||||
],
|
||||
"last_run": null,
|
||||
"last_status": null,
|
||||
"next_run": null
|
||||
}
|
||||
]
|
||||
182
src/router.py
182
src/router.py
@@ -1,6 +1,11 @@
|
||||
"""Echo Core message router — routes messages to Claude or commands."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
from src.config import Config
|
||||
@@ -16,6 +21,8 @@ from src.claude_session import (
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
APPROVED_TASKS_FILE = Path(__file__).parent.parent / "approved-tasks.json"
|
||||
|
||||
# Module-level config instance (lazy singleton)
|
||||
_config: Config | None = None
|
||||
|
||||
@@ -45,6 +52,19 @@ def route_message(
|
||||
"""
|
||||
text = text.strip()
|
||||
|
||||
# Ralph commands (!approve, !status, !stop, !propose)
|
||||
if text.lower().startswith("!approve ") or text.lower() == "!approve":
|
||||
return _ralph_approve(text), True
|
||||
|
||||
if text.lower() == "!status" or text.lower().startswith("!status "):
|
||||
return _ralph_status(text), True
|
||||
|
||||
if text.lower().startswith("!stop "):
|
||||
return _ralph_stop(text), True
|
||||
|
||||
if text.lower().startswith("!propose "):
|
||||
return _ralph_propose(text), True
|
||||
|
||||
# Text-based commands (not slash commands — these work in any adapter)
|
||||
if text.lower() == "/clear":
|
||||
default_model = _get_config().get("bot.default_model", "sonnet")
|
||||
@@ -136,6 +156,168 @@ def _model_command(channel_id: str, text: str) -> str:
|
||||
return f"Model changed to {choice}."
|
||||
|
||||
|
||||
def _load_approved_tasks() -> dict:
|
||||
"""Load approved-tasks.json, return empty structure if missing."""
|
||||
if APPROVED_TASKS_FILE.exists():
|
||||
return json.loads(APPROVED_TASKS_FILE.read_text())
|
||||
return {"projects": [], "last_updated": None}
|
||||
|
||||
|
||||
def _save_approved_tasks(data: dict) -> None:
|
||||
data["last_updated"] = datetime.now(timezone.utc).isoformat()
|
||||
APPROVED_TASKS_FILE.write_text(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
|
||||
|
||||
def _ralph_approve(text: str) -> str:
|
||||
"""!approve P1,P2 sau !approve roa2web — aprobă proiecte pentru night-execute."""
|
||||
parts = text.split(None, 1)
|
||||
if len(parts) < 2:
|
||||
data = _load_approved_tasks()
|
||||
pending = [p for p in data["projects"] if p.get("status") == "pending"]
|
||||
if not pending:
|
||||
return "Niciun proiect pending. Folosește !propose <nume> <descriere> pentru a adăuga."
|
||||
lines = [f"Proiecte pending (aprobă cu !approve <nume>):"]
|
||||
for p in pending:
|
||||
lines.append(f" - {p['name']}: {p['description'][:60]}")
|
||||
return "\n".join(lines)
|
||||
|
||||
names_raw = parts[1].strip()
|
||||
names = [n.strip() for n in names_raw.replace(",", " ").split() if n.strip()]
|
||||
|
||||
data = _load_approved_tasks()
|
||||
approved = []
|
||||
not_found = []
|
||||
|
||||
for name in names:
|
||||
found = False
|
||||
for p in data["projects"]:
|
||||
if p["name"].lower() == name.lower():
|
||||
p["status"] = "approved"
|
||||
p["approved_at"] = datetime.now(timezone.utc).isoformat()
|
||||
approved.append(p["name"])
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
not_found.append(name)
|
||||
|
||||
if not_found:
|
||||
return f"Nu am găsit proiectele: {', '.join(not_found)}. Verifică !status pentru lista completă."
|
||||
|
||||
_save_approved_tasks(data)
|
||||
names_str = ", ".join(approved)
|
||||
return f"✅ Aprobat pentru tonight: {names_str}\nNight-execute rulează la 23:00 și va implementa stories autonom."
|
||||
|
||||
|
||||
def _ralph_status(text: str) -> str:
|
||||
"""!status sau !status <proiect> — status Ralph pentru proiecte."""
|
||||
parts = text.split(None, 1)
|
||||
filter_name = parts[1].strip().lower() if len(parts) > 1 else None
|
||||
|
||||
data = _load_approved_tasks()
|
||||
projects = data.get("projects", [])
|
||||
|
||||
if filter_name:
|
||||
projects = [p for p in projects if filter_name in p["name"].lower()]
|
||||
|
||||
if not projects:
|
||||
return "Niciun proiect în approved-tasks.json. Adaugă cu !propose."
|
||||
|
||||
lines = ["📊 Status proiecte Ralph:"]
|
||||
for p in projects:
|
||||
status = p.get("status", "unknown")
|
||||
name = p["name"]
|
||||
pid = p.get("pid")
|
||||
started = p.get("started_at", "")[:16] if p.get("started_at") else "-"
|
||||
|
||||
# Verifică dacă procesul mai rulează
|
||||
if pid and status == "running":
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
running_indicator = f"🟢 PID {pid}"
|
||||
except (ProcessLookupError, PermissionError):
|
||||
running_indicator = "🔴 PID mort"
|
||||
p["status"] = "stopped"
|
||||
_save_approved_tasks(data)
|
||||
else:
|
||||
running_indicator = {"approved": "⏳ aștept 23:00", "pending": "📋 pending",
|
||||
"complete": "✅ complet", "failed": "❌ eșuat", "stopped": "⏹ oprit"}.get(status, status)
|
||||
|
||||
# Stories complete din prd.json
|
||||
prd_path = Path(f"/home/moltbot/workspace/{name}/scripts/ralph/prd.json")
|
||||
stories_info = ""
|
||||
if prd_path.exists():
|
||||
try:
|
||||
prd = json.loads(prd_path.read_text())
|
||||
total = len(prd.get("userStories", []))
|
||||
done = sum(1 for s in prd.get("userStories", []) if s.get("passes"))
|
||||
stories_info = f" | Stories: {done}/{total}"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
lines.append(f" {name}: {running_indicator}{stories_info} | Start: {started}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _ralph_stop(text: str) -> str:
|
||||
"""!stop <proiect> — oprește Ralph loop pentru un proiect."""
|
||||
parts = text.split(None, 1)
|
||||
if len(parts) < 2:
|
||||
return "Folosire: !stop <nume-proiect>"
|
||||
|
||||
name = parts[1].strip()
|
||||
data = _load_approved_tasks()
|
||||
|
||||
for p in data["projects"]:
|
||||
if p["name"].lower() == name.lower():
|
||||
pid = p.get("pid")
|
||||
if pid:
|
||||
try:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
p["status"] = "stopped"
|
||||
p["stopped_at"] = datetime.now(timezone.utc).isoformat()
|
||||
_save_approved_tasks(data)
|
||||
return f"⏹ Ralph oprit pentru {name} (PID {pid} terminat)."
|
||||
except ProcessLookupError:
|
||||
p["status"] = "stopped"
|
||||
_save_approved_tasks(data)
|
||||
return f"PID {pid} nu mai rula pentru {name}. Status actualizat."
|
||||
except PermissionError:
|
||||
return f"❌ Nu am permisiune să opresc PID {pid}."
|
||||
else:
|
||||
return f"{name} nu are un PID activ (status: {p.get('status', 'unknown')})."
|
||||
|
||||
return f"Proiect '{name}' nu găsit în approved-tasks.json."
|
||||
|
||||
|
||||
def _ralph_propose(text: str) -> str:
|
||||
"""!propose <nume> <descriere> — adaugă un proiect pentru aprobare."""
|
||||
parts = text.split(None, 2)
|
||||
if len(parts) < 3:
|
||||
return "Folosire: !propose <nume-proiect> <descriere scurtă>\nEx: !propose roa2web Homepage redesign cu hero section și animații"
|
||||
|
||||
name = parts[1].strip()
|
||||
description = parts[2].strip()
|
||||
|
||||
data = _load_approved_tasks()
|
||||
|
||||
for p in data["projects"]:
|
||||
if p["name"].lower() == name.lower():
|
||||
return f"Proiectul '{name}' există deja cu status: {p.get('status', 'unknown')}."
|
||||
|
||||
data["projects"].append({
|
||||
"name": name,
|
||||
"description": description,
|
||||
"status": "pending",
|
||||
"proposed_at": datetime.now(timezone.utc).isoformat(),
|
||||
"approved_at": None,
|
||||
"started_at": None,
|
||||
"pid": None
|
||||
})
|
||||
_save_approved_tasks(data)
|
||||
return f"📋 Adăugat: {name}\n{description}\n\nAprobă cu: !approve {name}"
|
||||
|
||||
|
||||
def _get_channel_config(channel_id: str) -> dict | None:
|
||||
"""Find channel config by ID."""
|
||||
channels = _get_config().get("channels", {})
|
||||
|
||||
19
tools/ralph/prd-template.json
Normal file
19
tools/ralph/prd-template.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"projectName": "feature-name",
|
||||
"branchName": "ralph/feature-name",
|
||||
"description": "Descriere scurtă a feature-ului",
|
||||
"userStories": [
|
||||
{
|
||||
"id": "US-001",
|
||||
"title": "Titlu story",
|
||||
"description": "As a [user type], I want [feature] so that [benefit]",
|
||||
"priority": 1,
|
||||
"acceptanceCriteria": [
|
||||
"Criteriu specific și verificabil",
|
||||
"npm run typecheck passes"
|
||||
],
|
||||
"passes": false,
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
207
tools/ralph/prompt.md
Normal file
207
tools/ralph/prompt.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Ralph - Instrucțiuni pentru Iterație
|
||||
|
||||
Ești un agent autonom care implementează user stories dintr-un PRD. Aceasta este O SINGURĂ iterație - implementezi UN singur story și apoi te oprești.
|
||||
|
||||
## Workflow pentru această iterație
|
||||
|
||||
### 1. Citește contextul
|
||||
- PRD-ul și progress.txt sunt furnizate în context
|
||||
- Înțelege ce stories sunt deja complete (`passes: true`)
|
||||
- Identifică următorul story de implementat (prioritate cea mai mică dintre cele incomplete)
|
||||
- Notează `techStack.commands` din PRD pentru comenzile corecte
|
||||
|
||||
### 2. Management branch
|
||||
- Verifică dacă ești pe branch-ul corect (specificat în `branchName` din PRD)
|
||||
- Dacă nu, creează și checkout branch-ul:
|
||||
```bash
|
||||
git checkout -b <branchName>
|
||||
```
|
||||
- Dacă branch-ul există deja, doar checkout:
|
||||
```bash
|
||||
git checkout <branchName>
|
||||
```
|
||||
|
||||
### 3. Selectează story-ul
|
||||
- Alege story-ul cu cea mai mică prioritate care are `passes: false`
|
||||
- Citește atent acceptance criteria
|
||||
- Verifică câmpul `requiresBrowserCheck` - dacă e `true`, trebuie verificare vizuală
|
||||
|
||||
### 4. Implementare
|
||||
- Implementează DOAR acest story
|
||||
- Urmează patterns existente în codebase
|
||||
- Fii minimal și focusat - nu adăuga funcționalități extra
|
||||
|
||||
### 5. Quality Checks
|
||||
Rulează TOATE verificările înainte de commit. Folosește comenzile din `techStack.commands`:
|
||||
|
||||
```bash
|
||||
# Folosește comenzile din prd.json techStack.commands:
|
||||
{techStack.commands.typecheck} # Type checking
|
||||
{techStack.commands.lint} # Linting
|
||||
{techStack.commands.test} # Tests (dacă există)
|
||||
```
|
||||
|
||||
**Comenzi standard per stack:**
|
||||
|
||||
| Stack | Typecheck | Lint | Test |
|
||||
|-------|-----------|------|------|
|
||||
| Next.js/TS | npm run typecheck | npm run lint | npm test |
|
||||
| Node.js | npm run typecheck | npm run lint | npm test |
|
||||
| Python | mypy . | ruff check . | python -m pytest |
|
||||
| Go | - | golangci-lint run | go test ./... |
|
||||
|
||||
**IMPORTANT**: Nu face commit dacă verificările eșuează. Repară mai întâi.
|
||||
|
||||
### 6. Verificare Browser (pentru UI stories)
|
||||
|
||||
**DACĂ story-ul are `requiresBrowserCheck: true` sau implică UI:**
|
||||
|
||||
Folosește **agent-browser CLI** pentru verificare vizuală. Agent-browser e optimizat pentru agenți AI cu referințe compacte (@e1, @e2) care consumă minim tokeni.
|
||||
|
||||
#### 6.1 Pornește dev server-ul
|
||||
```bash
|
||||
# Folosește comanda din techStack.commands.start
|
||||
{techStack.commands.start}
|
||||
# Exemplu: npm run dev
|
||||
```
|
||||
|
||||
Așteaptă să pornească (verifică output-ul pentru "ready" sau similar).
|
||||
|
||||
#### 6.2 Navighează la pagină
|
||||
```bash
|
||||
agent-browser navigate "http://localhost:{techStack.port}"
|
||||
# Exemplu: agent-browser navigate "http://localhost:3000"
|
||||
```
|
||||
|
||||
#### 6.3 Ia snapshot pentru verificare
|
||||
```bash
|
||||
agent-browser snapshot
|
||||
```
|
||||
|
||||
Snapshot-ul returnează o listă de elemente cu referințe compacte:
|
||||
```
|
||||
@e1: heading "Welcome"
|
||||
@e2: button "Login"
|
||||
@e3: textbox "Email"
|
||||
@e4: textbox "Password"
|
||||
@e5: button "Submit"
|
||||
```
|
||||
|
||||
**Verifică în snapshot:**
|
||||
- Elementele cheie din acceptance criteria există
|
||||
- Textul e corect
|
||||
- Structura paginii e corectă
|
||||
|
||||
#### 6.4 Testează interacțiunile (dacă e cazul)
|
||||
```bash
|
||||
# Click pe un element
|
||||
agent-browser click @e2
|
||||
|
||||
# Fill un input
|
||||
agent-browser fill @e3 "test@example.com"
|
||||
|
||||
# Așteaptă o schimbare
|
||||
agent-browser snapshot # verifică noua stare
|
||||
```
|
||||
|
||||
#### 6.5 Salvează screenshot ca dovadă
|
||||
```bash
|
||||
agent-browser screenshot ./scripts/ralph/screenshots/US-{id}-$(date +%Y%m%d-%H%M%S).png
|
||||
# Exemplu: agent-browser screenshot ./scripts/ralph/screenshots/US-001-20240115-143022.png
|
||||
```
|
||||
|
||||
#### 6.6 Verifică erori
|
||||
```bash
|
||||
# Verifică console pentru erori
|
||||
agent-browser console
|
||||
```
|
||||
|
||||
**IMPORTANT**:
|
||||
- NU marca story-ul complete dacă verificarea vizuală eșuează!
|
||||
- Dacă găsești erori în browser, repară-le înainte de commit
|
||||
- Screenshots sunt salvate în `scripts/ralph/screenshots/` pentru referință
|
||||
|
||||
### 7. Documentare (dacă ai descoperit ceva util)
|
||||
Dacă ai descoperit patterns sau gotchas, actualizează `AGENTS.md` în directorul relevant:
|
||||
- API patterns
|
||||
- Dependențe non-evidente
|
||||
- Convenții de cod
|
||||
- Cum să testezi anumite funcționalități
|
||||
|
||||
### 8. Commit
|
||||
Format commit message:
|
||||
```
|
||||
feat: [Story ID] - [Story Title]
|
||||
```
|
||||
|
||||
### 9. Marchează story-ul ca complet
|
||||
**CRITIC**: Actualizează `scripts/ralph/prd.json`:
|
||||
- Setează `passes: true` pentru story-ul implementat
|
||||
- Adaugă note relevante în câmpul `notes`
|
||||
|
||||
### 10. Actualizează progress.txt
|
||||
Adaugă la sfârșitul fișierului `scripts/ralph/progress.txt`:
|
||||
|
||||
```markdown
|
||||
## Iterație: [timestamp]
|
||||
### Story implementat: [ID] - [Title]
|
||||
### Status: Complete
|
||||
|
||||
### Verificări:
|
||||
- Typecheck: PASS
|
||||
- Lint: PASS
|
||||
- Tests: PASS/SKIP
|
||||
- Browser check: PASS/N/A
|
||||
|
||||
### Learnings:
|
||||
- [Ce ai învățat]
|
||||
- [Patterns descoperite]
|
||||
|
||||
### Next steps:
|
||||
- [Ce rămâne de făcut]
|
||||
---
|
||||
```
|
||||
|
||||
## Reguli importante
|
||||
|
||||
1. **UN SINGUR STORY PE ITERAȚIE** - Nu implementa mai mult de un story
|
||||
2. **TOATE CHECKS TREBUIE SĂ TREACĂ** - Nu face commit cu erori
|
||||
3. **VERIFICARE BROWSER PENTRU UI** - Obligatorie dacă `requiresBrowserCheck: true`
|
||||
4. **ACTUALIZEAZĂ prd.json** - Altfel iterația următoare va repeta munca
|
||||
5. **FII CONCIS** - Nu over-engineer
|
||||
|
||||
## Comenzi agent-browser (referință rapidă)
|
||||
|
||||
```bash
|
||||
# Navigare
|
||||
agent-browser navigate "http://localhost:3000/page"
|
||||
|
||||
# Snapshot (vedere compactă a paginii)
|
||||
agent-browser snapshot
|
||||
|
||||
# Click pe element (folosind ref din snapshot)
|
||||
agent-browser click @e5
|
||||
|
||||
# Fill input
|
||||
agent-browser fill @e3 "value"
|
||||
|
||||
# Screenshot
|
||||
agent-browser screenshot ./path/to/file.png
|
||||
|
||||
# Console logs
|
||||
agent-browser console
|
||||
|
||||
# Așteaptă text
|
||||
agent-browser wait-for "Loading complete"
|
||||
```
|
||||
|
||||
## Condiție de terminare
|
||||
|
||||
Dacă TOATE stories au `passes: true`, răspunde cu:
|
||||
|
||||
```
|
||||
<promise>COMPLETE</promise>
|
||||
```
|
||||
|
||||
---
|
||||
ÎNCEPE IMPLEMENTAREA ACUM.
|
||||
229
tools/ralph/ralph.sh
Executable file
229
tools/ralph/ralph.sh
Executable file
@@ -0,0 +1,229 @@
|
||||
#!/bin/bash
|
||||
# Ralph pentru Claude Code - Loop autonom de agent AI
|
||||
# Adaptat din Ralph original (snarktank/ralph) pentru Claude Code CLI
|
||||
# Usage: ./ralph.sh [max_iterations] [project_dir]
|
||||
|
||||
set -e
|
||||
|
||||
MAX_ITERATIONS=${1:-10}
|
||||
PROJECT_DIR=${2:-$(pwd)}
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PRD_FILE="$SCRIPT_DIR/prd.json"
|
||||
PROGRESS_FILE="$SCRIPT_DIR/progress.txt"
|
||||
ARCHIVE_DIR="$SCRIPT_DIR/archive"
|
||||
SCREENSHOTS_DIR="$SCRIPT_DIR/screenshots"
|
||||
LAST_BRANCH_FILE="$SCRIPT_DIR/.last-branch"
|
||||
PROMPT_FILE="$SCRIPT_DIR/prompt.md"
|
||||
|
||||
# Verifică că jq este instalat
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "Eroare: jq nu este instalat. Rulează: apt install jq"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verifică că claude este instalat
|
||||
if ! command -v claude &> /dev/null; then
|
||||
echo "Eroare: Claude Code CLI nu este instalat."
|
||||
echo "Instalează cu: npm install -g @anthropic-ai/claude-code"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verifică agent-browser (opțional, pentru verificări UI)
|
||||
if ! command -v agent-browser &> /dev/null; then
|
||||
echo "Notă: agent-browser nu este instalat."
|
||||
echo "Pentru verificări vizuale UI, instalează cu: npm install -g agent-browser && agent-browser install"
|
||||
echo "Continuăm fără verificări browser..."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Verifică existența fișierelor necesare
|
||||
if [ ! -f "$PRD_FILE" ]; then
|
||||
echo "Eroare: prd.json nu există în $SCRIPT_DIR"
|
||||
echo "Generează mai întâi un PRD folosind skill-ul /prd și apoi /ralph"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$PROMPT_FILE" ]; then
|
||||
echo "Eroare: prompt.md nu există în $SCRIPT_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Arhivare rulare anterioară dacă branch-ul s-a schimbat
|
||||
if [ -f "$PRD_FILE" ] && [ -f "$LAST_BRANCH_FILE" ]; then
|
||||
CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
|
||||
LAST_BRANCH=$(cat "$LAST_BRANCH_FILE" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$CURRENT_BRANCH" ] && [ -n "$LAST_BRANCH" ] && [ "$CURRENT_BRANCH" != "$LAST_BRANCH" ]; then
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
FOLDER_NAME=$(echo "$LAST_BRANCH" | sed 's|^ralph/||')
|
||||
ARCHIVE_FOLDER="$ARCHIVE_DIR/$DATE-$FOLDER_NAME"
|
||||
|
||||
echo "Arhivare rulare anterioară: $LAST_BRANCH"
|
||||
mkdir -p "$ARCHIVE_FOLDER"
|
||||
[ -f "$PRD_FILE" ] && cp "$PRD_FILE" "$ARCHIVE_FOLDER/"
|
||||
[ -f "$PROGRESS_FILE" ] && cp "$PROGRESS_FILE" "$ARCHIVE_FOLDER/"
|
||||
echo " Arhivat în: $ARCHIVE_FOLDER"
|
||||
|
||||
# Reset progress file
|
||||
echo "# Ralph Progress Log" > "$PROGRESS_FILE"
|
||||
echo "Started: $(date)" >> "$PROGRESS_FILE"
|
||||
echo "Branch: $CURRENT_BRANCH" >> "$PROGRESS_FILE"
|
||||
echo "---" >> "$PROGRESS_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Salvează branch-ul curent
|
||||
if [ -f "$PRD_FILE" ]; then
|
||||
CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
|
||||
if [ -n "$CURRENT_BRANCH" ]; then
|
||||
echo "$CURRENT_BRANCH" > "$LAST_BRANCH_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Creează directoare necesare
|
||||
mkdir -p "$SCRIPT_DIR/logs" "$SCRIPT_DIR/archive" "$SCRIPT_DIR/screenshots"
|
||||
|
||||
# Creează .gitignore dacă nu există
|
||||
if [ ! -f "$PROJECT_DIR/.gitignore" ]; then
|
||||
cat > "$PROJECT_DIR/.gitignore" << 'GITIGNORE'
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.pyo
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
.coverage
|
||||
htmlcov/
|
||||
.pytest_cache/
|
||||
|
||||
# Virtual environment
|
||||
venv/
|
||||
.venv/
|
||||
|
||||
# Ralph runtime
|
||||
scripts/ralph/.ralph.pid
|
||||
scripts/ralph/.last-branch
|
||||
scripts/ralph/logs/
|
||||
scripts/ralph/screenshots/
|
||||
scripts/ralph/archive/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
GITIGNORE
|
||||
echo "Created .gitignore"
|
||||
fi
|
||||
|
||||
# Inițializare progress file dacă nu există
|
||||
if [ ! -f "$PROGRESS_FILE" ]; then
|
||||
echo "# Ralph Progress Log" > "$PROGRESS_FILE"
|
||||
echo "Started: $(date)" >> "$PROGRESS_FILE"
|
||||
echo "---" >> "$PROGRESS_FILE"
|
||||
fi
|
||||
|
||||
# Funcție pentru a verifica dacă toate story-urile sunt complete
|
||||
check_all_complete() {
|
||||
local incomplete=$(jq '[.userStories[] | select(.passes != true)] | length' "$PRD_FILE" 2>/dev/null || echo "999")
|
||||
[ "$incomplete" -eq 0 ]
|
||||
}
|
||||
|
||||
# Afișare status inițial
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " RALPH pentru Claude Code - Agent Autonom "
|
||||
echo "======================================================================="
|
||||
PROJECT_NAME=$(jq -r '.projectName // "Unknown"' "$PRD_FILE")
|
||||
BRANCH_NAME=$(jq -r '.branchName // "N/A"' "$PRD_FILE")
|
||||
TOTAL_STORIES=$(jq '.userStories | length' "$PRD_FILE")
|
||||
COMPLETE_STORIES=$(jq '[.userStories[] | select(.passes == true)] | length' "$PRD_FILE")
|
||||
echo " Proiect: $PROJECT_NAME"
|
||||
echo " Branch: $BRANCH_NAME"
|
||||
echo " Stories: $COMPLETE_STORIES / $TOTAL_STORIES complete"
|
||||
echo " Max iterații: $MAX_ITERATIONS"
|
||||
echo " Screenshots: $SCREENSHOTS_DIR"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
|
||||
# Verificare rapidă - poate toate sunt deja complete?
|
||||
if check_all_complete; then
|
||||
echo "Toate story-urile sunt deja complete!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Loop principal
|
||||
for i in $(seq 1 $MAX_ITERATIONS); do
|
||||
echo ""
|
||||
echo "==================================================================="
|
||||
echo " Ralph Iterația $i din $MAX_ITERATIONS"
|
||||
echo "==================================================================="
|
||||
|
||||
# Status curent
|
||||
COMPLETE_NOW=$(jq '[.userStories[] | select(.passes == true)] | length' "$PRD_FILE")
|
||||
NEXT_STORY=$(jq -r '[.userStories[] | select(.passes != true)] | sort_by(.priority) | .[0] | "\(.id): \(.title)"' "$PRD_FILE")
|
||||
echo " Progress: $COMPLETE_NOW / $TOTAL_STORIES stories complete"
|
||||
echo " Next: $NEXT_STORY"
|
||||
echo ""
|
||||
|
||||
# Pregătește prompt-ul cu context
|
||||
FULL_PROMPT=$(cat <<EOF
|
||||
# Context pentru această iterație Ralph
|
||||
|
||||
## PRD (prd.json):
|
||||
$(cat "$PRD_FILE")
|
||||
|
||||
## Progress până acum (progress.txt):
|
||||
$(cat "$PROGRESS_FILE")
|
||||
|
||||
## Instrucțiuni pentru această iterație:
|
||||
$(cat "$PROMPT_FILE")
|
||||
EOF
|
||||
)
|
||||
|
||||
# Execută Claude Code în modul non-interactiv
|
||||
LOG_FILE="$SCRIPT_DIR/logs/iteration-$i-$(date +%Y%m%d-%H%M%S).log"
|
||||
mkdir -p "$SCRIPT_DIR/logs"
|
||||
|
||||
# --output-format json avoids streaming mode issues
|
||||
echo "$FULL_PROMPT" | claude -p --dangerously-skip-permissions --output-format json 2>&1 | tee "$LOG_FILE" || true
|
||||
OUTPUT=$(cat "$LOG_FILE")
|
||||
|
||||
# Verifică dacă toate task-urile sunt complete
|
||||
if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
|
||||
echo ""
|
||||
echo "==================================================================="
|
||||
echo " RALPH A TERMINAT TOATE TASK-URILE!"
|
||||
echo " Completat la iterația $i din $MAX_ITERATIONS"
|
||||
echo "==================================================================="
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Verifică și prin prd.json
|
||||
if check_all_complete; then
|
||||
echo ""
|
||||
echo "==================================================================="
|
||||
echo " TOATE STORY-URILE DIN PRD SUNT COMPLETE!"
|
||||
echo "==================================================================="
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo " Iterația $i completă. Continuăm..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "==================================================================="
|
||||
echo " Ralph a atins limita de iterații ($MAX_ITERATIONS)"
|
||||
echo " Verifică progress.txt pentru status."
|
||||
echo "==================================================================="
|
||||
echo ""
|
||||
|
||||
# Afișează stories incomplete
|
||||
echo "Stories incomplete:"
|
||||
jq -r '.userStories[] | select(.passes != true) | " - \(.id): \(.title)"' "$PRD_FILE"
|
||||
|
||||
exit 1
|
||||
Reference in New Issue
Block a user