From ccd9362d1a0e52e22dbfcbf9c734dd6ad7c91e5b Mon Sep 17 00:00:00 2001 From: Echo Date: Mon, 9 Feb 2026 09:06:09 +0000 Subject: [PATCH] Update memory, root, tools (+2 ~2) --- AGENTS.md | 69 ++++--- TOOLS.md | 11 + memory/kb/tools/ralph-workflow.md | 281 +++++++++++++++++++++++++ tools/ralph_workflow.py | 331 ++++++++++++++++++++++++++++++ 4 files changed, 664 insertions(+), 28 deletions(-) create mode 100644 memory/kb/tools/ralph-workflow.md create mode 100755 tools/ralph_workflow.py diff --git a/AGENTS.md b/AGENTS.md index d3f76e1..d64249e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,48 +53,61 @@ When I receive errors, bugs, or new feature requests: **2. NOAPTE (23:00, 03:00) - night-execute:** -**A. Planning cu OPUS (eu, Echo):** -- SSH claude-agent: `ssh echo@10.0.20.201 "sudo pct exec 171 -- su - claude -c 'cd /workspace && bash'"` -- Pentru PROIECTE NOI: - 1. Folosesc skill `/prd` → PRD markdown (scop, obiective, user stories, acceptance criteria) - 2. Salvez în `/workspace/PROJECT-NAME/tasks/prd-PROJECT-NAME.md` - 3. Folosesc skill `/ralph` → prd.json (stories prioritizate) - 4. Salvez în `/workspace/PROJECT-NAME/scripts/ralph/prd.json` - 5. Git init + push la gitea.romfast.ro -- Pentru FEATURES EXISTENTE: - 1. Citesc PRD existent - 2. Generez noi user stories (ID consecutive, priority logică) - 3. Actualizez prd.json - 4. Git commit + push +**A. Planning cu OPUS (eu, Echo) - pe moltbot:** +```python +from tools.ralph_workflow import create_prd, convert_prd, run_ralph -**B. Implementare cu Ralph (Sonnet):** -- Rulează `./scripts/ralph/ralph.sh 20` (max 20 iterații) -- Ralph loop autonom: +# Pentru PROIECTE NOI +prd_file = create_prd( + project_name="project-name", # kebab-case + description="Descriere completă feature/proiect" +) +prd_json = convert_prd(prd_file) # → prd.json + configurare +run_ralph(prd_json, max_iterations=20, background=True) + +# Pentru FEATURES EXISTENTE +# Citesc PRD existent, generez noi stories, actualizez prd.json +# Apoi run_ralph() pentru implementare +``` + +- Workspace: `~/workspace/PROJECT-NAME/` +- Git init + commit + push către gitea.romfast.ro +- Marchează [x] în approved-tasks.md + +**B. Implementare cu Ralph (Sonnet) - automat în background:** +- Ralph loop autonom (ralph.sh): 1. Selectează story (priority minimă, passes=false) 2. Rulează Claude Code (Sonnet) pentru implementare 3. Quality checks: typecheck, lint, test 4. Commit dacă OK → passes: true 5. Update progress.txt cu learnings - 6. Repetă până toate complete -- Git push toate commit-urile -- Marchează [x] în approved-tasks.md + 6. Repetă până toate complete sau max 20 iterații +- Git push toate commit-urile automat **3. DIMINEAȚĂ (08:30) - morning-report:** -- Raportez ce proiecte/features s-au realizat +```python +from tools.ralph_workflow import check_status +from pathlib import Path + +status = check_status(Path.home() / "workspace" / "PROJECT-NAME") +# Raportez: stories complete/incomplete, learnings, link gitea +``` + - Status per story: ✅ complet / 🔄 în progres / ⚠️ blocat - Learnings din progress.txt - Link gitea: `https://gitea.romfast.ro/romfast/PROJECT-NAME` ### Mașină Development -**claude-agent (LXC 171):** -- IP: 10.0.20.171 -- User: claude -- Workspace: `/workspace/` -- Claude Code instalat + Git configurat pentru gitea -- Ralph plugin: `/workspace/ralph-claude/` - - Skills: `/prd` (generare PRD) + `/ralph` (conversie prd.json) - - Script: `ralph.sh` - loop autonom +**moltbot (LXC 110) - LOCAL:** +- User: moltbot +- Workspace: `~/workspace/` +- Claude Code: `~/.local/bin/claude` (v2.1.37) +- Ralph skill: `~/.claude/skills/ralph/` + - Comenzi: `/ralph:prd` (generare PRD) + `/ralph:convert` (conversie prd.json) + - Templates: ralph.sh, prompt.md (copiate automat) +- Helper Python: `~/clawd/tools/ralph_workflow.py` + - `create_prd()`, `convert_prd()`, `run_ralph()`, `check_status()` ### Model Strategy (OBLIGATORIU) - **Opus** → Planning, PRD, stories (eu, Echo în night-execute) diff --git a/TOOLS.md b/TOOLS.md index 75d98bd..15ed9cb 100644 --- a/TOOLS.md +++ b/TOOLS.md @@ -57,6 +57,17 @@ - **Format:** **Nume** → pași → 📊 Rezultat → 📚 Sursă - **Flux actualizare:** Automat via insights-extract job +### Ralph Workflow (Autonomous Code Generation) +- **Helper:** `python3 tools/ralph_workflow.py` +- **Skill:** `~/.claude/skills/ralph/` (Claude Code) +- **Workspace:** `~/workspace/` (proiecte generate) +- **Comenzi:** + - `create_prd(name, description)` → PRD markdown + - `convert_prd(prd_file)` → prd.json + config + - `run_ralph(prd_json, max_iter, bg)` → execuție autonomă + - `check_status(project_dir)` → progres stories +- **Doc:** `memory/kb/tools/ralph-workflow.md` + ## Cron Jobs **Principale:** morning-report (08:30), morning-coaching (09:00), respiratie-orar (09-19), anaf-monitor (10:00,16:00), evening-report (20:00), evening-coaching (21:00), night-execute (23:00) diff --git a/memory/kb/tools/ralph-workflow.md b/memory/kb/tools/ralph-workflow.md new file mode 100644 index 0000000..77b648d --- /dev/null +++ b/memory/kb/tools/ralph-workflow.md @@ -0,0 +1,281 @@ +# Ralph Workflow - Sistem Complet + +**Locație:** moltbot (LXC 110) - Claude Code instalat local +**Workspace:** `~/workspace/` +**Helper:** `~/clawd/tools/ralph_workflow.py` + +## Componente + +### 1. Skill Ralph (Claude Code) +**Locație:** `~/.claude/skills/ralph/` + +**Comenzi disponibile:** +- `/ralph:prd` - Generează PRD structurat cu întrebări adaptive +- `/ralph:convert` - Convertește PRD markdown în prd.json + configurare proiect + +**Features:** +- Detecție automată context (proiect nou vs feature existent) +- Întrebări adaptive (10 pentru nou, 5-7 pentru feature) +- Web research opțional pentru best practices +- Generare `.claude/settings.json` cu allow-list per tech stack +- Configurare automată scripts/ralph/ + +### 2. Ralph Loop (ralph.sh) +**Locație:** Copiat automat în `PROJECT/scripts/ralph/ralph.sh` + +**Funcționare:** +1. Citește prd.json +2. Selectează story cu priority minimă + passes=false +3. Rulează `claude` pentru implementare +4. Quality checks: typecheck, lint, test +5. Git commit dacă OK → passes: true +6. Update progress.txt cu learnings +7. Repetă până toate complete sau max iterations + +### 3. Helper Python +**Locație:** `~/clawd/tools/ralph_workflow.py` + +**Funcții:** +- `create_prd()` - Apelează Claude Code cu /ralph:prd +- `convert_prd()` - Apelează Claude Code cu /ralph:convert +- `run_ralph()` - Lansează ralph.sh în background/foreground +- `check_status()` - Verifică progres (stories complete/incomplete, learnings) + +## Workflow Complet (pentru Echo) + +### Night Execute (23:00) + +**Pas 1: Planning cu Opus (Echo)** + +```python +from tools.ralph_workflow import create_prd, convert_prd, run_ralph + +# Creează PRD +prd_file = create_prd( + project_name="task-tracker", # kebab-case + description=""" + Vreau un task tracker simplu pentru CLI. + Features: + - Add/list/done tasks + - SQLite storage + - Export markdown + """ +) + +# Convertește în prd.json +prd_json = convert_prd(prd_file) + +# Lansează Ralph în background +run_ralph(prd_json, max_iterations=20, background=True) +``` + +**Pas 2: Git init + push** + +```bash +cd ~/workspace/task-tracker +git init +git remote add origin https://gitea.romfast.ro/romfast/task-tracker +git add . +git commit -m "Initial commit with PRD and Ralph config" +git push -u origin main +``` + +### Morning Report (08:30) + +**Verifică status:** + +```python +from tools.ralph_workflow import check_status +from pathlib import Path + +status = check_status(Path.home() / "workspace" / "task-tracker") + +print(f"✅ Complete: {len(status['complete'])}") +print(f"🔄 Incomplete: {len(status['incomplete'])}") +print(f"📚 Learnings: {status['learnings'][-3:]}") +``` + +**Raportează în Discord:** + +```markdown +## 🔄 Proiecte Ralph + +### task-tracker +- Status: ✅ 5/8 stories complete +- Progress: + - ✅ US-001: Database schema + migrations + - ✅ US-002: Add task CLI command + - ✅ US-003: List tasks with filters + - ✅ US-004: Mark task as done + - ✅ US-005: Export to markdown + - 🔄 US-006: Tests for all commands (în progres) + - ⚠️ US-007: README documentation (blocat) + - 🔄 US-008: CI/CD setup + +📚 Learnings: +- SQLite migrations best practices +- Click CLI argument parsing +- Pytest fixtures for DB testing + +🔗 Link: https://gitea.romfast.ro/romfast/task-tracker +``` + +## Structură Proiect + +După workflow complet: + +``` +~/workspace/PROJECT-NAME/ +├── tasks/ +│ └── prd-PROJECT-NAME.md # PRD markdown (generat de /ralph:prd) +├── scripts/ +│ └── ralph/ +│ ├── prd.json # Stories pentru Ralph (generat de /ralph:convert) +│ ├── progress.txt # Learnings per iterație (generat de ralph.sh) +│ ├── ralph.sh # Loop autonom (copiat de /ralph:convert) +│ ├── prompt.md # Instrucțiuni per iterație (copiat) +│ ├── .ralph.pid # PID proces Ralph (pentru monitoring) +│ ├── logs/ +│ │ └── ralph.log # Output Ralph loop +│ ├── archive/ # Arhive rulări anterioare +│ └── screenshots/ # Screenshots verificări UI (dacă aplicabil) +├── src/ # Cod implementat de Ralph +├── .claude/ +│ └── settings.json # Permissions allow-list (generat de /ralph:convert) +└── .git/ # Git repo → gitea +``` + +## Comenzi Rapide + +### CLI Direct (pentru debug) + +```bash +# Verifică skill-uri disponibile +claude skills list | grep ralph + +# Test /ralph:prd (manual) +cd ~/workspace/test-project +claude exec "/ralph:prd + +Vreau să creez un calculator simplu." + +# Test /ralph:convert (manual) +claude exec "/ralph:convert + +Convertește PRD din tasks/prd-calculator.md" + +# Rulează Ralph manual +cd ~/workspace/test-project +./scripts/ralph/ralph.sh 10 # max 10 iterații +``` + +### Python Helper (pentru automatizare) + +```bash +# Creează proiect complet (PRD + conversie + launch) +python3 ~/clawd/tools/ralph_workflow.py create "task-tracker" "Task tracker simplu pentru CLI" + +# Verifică status +python3 ~/clawd/tools/ralph_workflow.py status "task-tracker" +``` + +## Monitorizare + +### În timpul execuției + +```bash +# Verifică dacă Ralph rulează +ps aux | grep ralph.sh + +# Tail logs live +tail -f ~/workspace/PROJECT/scripts/ralph/logs/ralph.log + +# Verifică stories complete +jq '.userStories[] | select(.passes == true) | {id, title}' \ + ~/workspace/PROJECT/scripts/ralph/prd.json +``` + +### După execuție + +```bash +# Stories incomplete (priority sort) +jq '.userStories[] | select(.passes != true) | {id, title, priority}' \ + ~/workspace/PROJECT/scripts/ralph/prd.json | jq -s 'sort_by(.priority)' + +# Ultimele learnings +tail -20 ~/workspace/PROJECT/scripts/ralph/progress.txt + +# Git commits de la Ralph +cd ~/workspace/PROJECT +git log --oneline --since="last night" +``` + +## Troubleshooting + +### Ralph nu pornește + +```bash +# Verifică Claude Code instalat +claude --version + +# Verifică skill ralph disponibil +ls -la ~/.claude/skills/ralph/ + +# Verifică prd.json valid +jq '.' ~/workspace/PROJECT/scripts/ralph/prd.json +``` + +### Ralph blochează pe o story + +```bash +# Verifică logs +tail -50 ~/workspace/PROJECT/scripts/ralph/logs/ralph.log + +# Marchează manual story ca done (pentru a continua) +jq '.userStories[2].passes = true' \ + ~/workspace/PROJECT/scripts/ralph/prd.json > tmp && mv tmp prd.json +``` + +### Oprește Ralph + +```bash +# Kill process +cat ~/workspace/PROJECT/scripts/ralph/.ralph.pid | xargs kill + +# Sau kill all +pkill -f ralph.sh +``` + +## Model Strategy (OBLIGATORIU) + +- **Opus** → Planning, PRD, stories (Echo în night-execute) + - Apelează `create_prd()` și `convert_prd()` +- **Sonnet** → Coding, debugging, implementare (Ralph loop) + - Ralph folosește Claude Code cu Sonnet implicit + +## Integration cu AGENTS.md + +**Pentru night-execute job:** + +```python +# În night-execute cron job +import sys +sys.path.append('/home/moltbot/clawd') + +from tools.ralph_workflow import create_prd, convert_prd, run_ralph +from pathlib import Path + +# Citește approved-tasks.md pentru proiecte aprobate +# ... + +# Pentru fiecare proiect aprobat +for project in approved_projects: + prd = create_prd(project['name'], project['description']) + prd_json = convert_prd(prd) + run_ralph(prd_json, max_iterations=20, background=True) +``` + +--- + +**Updated:** 2026-02-09 +**Version:** 2.0 (local pe moltbot, fără SSH) diff --git a/tools/ralph_workflow.py b/tools/ralph_workflow.py new file mode 100755 index 0000000..1765d49 --- /dev/null +++ b/tools/ralph_workflow.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python3 +""" +Ralph Workflow Helper - pentru Echo +Gestionează workflow-ul complet de creare și execuție proiecte cu Ralph + Claude Code +""" + +import os +import sys +import json +import subprocess +from pathlib import Path + +WORKSPACE = Path.home() / "workspace" +CLAUDE_BIN = Path.home() / ".local" / "bin" / "claude" + + +def create_prd(project_name: str, description: str, workspace_dir: Path = WORKSPACE): + """ + Pas 1: Creează PRD folosind Claude Code cu skill /ralph:prd + + Args: + project_name: Nume proiect (kebab-case) + description: Descriere feature/proiect + workspace_dir: Director workspace (default: ~/workspace) + + Returns: + Path către PRD generat + """ + project_dir = workspace_dir / project_name + project_dir.mkdir(parents=True, exist_ok=True) + + # Verifică dacă există package.json (feature mode) sau nu (new project mode) + config_exists = (project_dir / "package.json").exists() + mode = "FEATURE" if config_exists else "NEW_PROJECT" + + print(f"🔄 Creez PRD pentru {project_name} (mode: {mode})") + print(f"📁 Workspace: {project_dir}") + + # Construiește prompt pentru Claude Code + prompt = f"""/ralph:prd + +{description} +""" + + # Rulează Claude Code în director proiect + try: + result = subprocess.run( + [str(CLAUDE_BIN), "exec", prompt], + cwd=project_dir, + capture_output=True, + text=True, + timeout=300 # 5 min timeout + ) + + if result.returncode != 0: + print(f"❌ Eroare la generare PRD: {result.stderr}") + return None + + # Caută PRD generat + tasks_dir = project_dir / "tasks" + if tasks_dir.exists(): + prd_files = list(tasks_dir.glob("prd-*.md")) + if prd_files: + prd_file = prd_files[0] + print(f"✅ PRD generat: {prd_file}") + return prd_file + + print("⚠️ PRD generat dar nu găsit în tasks/") + return None + + except subprocess.TimeoutExpired: + print("❌ Timeout la generare PRD (>5 min)") + return None + except Exception as e: + print(f"❌ Eroare: {e}") + return None + + +def convert_prd(prd_file: Path): + """ + Pas 2: Convertește PRD în prd.json folosind /ralph:convert + + Args: + prd_file: Path către PRD markdown + + Returns: + Path către prd.json generat + """ + project_dir = prd_file.parent.parent + + print(f"🔄 Convertesc PRD în prd.json") + print(f"📄 PRD: {prd_file}") + + # Construiește prompt pentru conversie + prompt = f"""/ralph:convert + +Convertește PRD-ul din {prd_file.relative_to(project_dir)} +""" + + try: + result = subprocess.run( + [str(CLAUDE_BIN), "exec", prompt], + cwd=project_dir, + capture_output=True, + text=True, + timeout=180 # 3 min timeout + ) + + if result.returncode != 0: + print(f"❌ Eroare la conversie: {result.stderr}") + return None + + # Verifică prd.json + prd_json = project_dir / "scripts" / "ralph" / "prd.json" + if prd_json.exists(): + print(f"✅ prd.json generat: {prd_json}") + + # Afișează stories + with open(prd_json) as f: + data = json.load(f) + print(f"\n📋 Stories: {len(data.get('userStories', []))}") + for story in data.get('userStories', [])[:3]: + print(f" - {story['id']}: {story['title']}") + if len(data.get('userStories', [])) > 3: + print(f" ... și {len(data['userStories']) - 3} mai multe") + + return prd_json + + print("⚠️ Conversie completă dar prd.json nu găsit") + return None + + except subprocess.TimeoutExpired: + print("❌ Timeout la conversie (>3 min)") + return None + except Exception as e: + print(f"❌ Eroare: {e}") + return None + + +def run_ralph(prd_json: Path, max_iterations: int = 20, background: bool = False): + """ + Pas 3: Rulează ralph.sh pentru execuție autonomă + + Args: + prd_json: Path către prd.json + max_iterations: Max iterații Ralph + background: Dacă True, rulează în background (nohup) + + Returns: + subprocess.Popen object dacă background, altfel None + """ + project_dir = prd_json.parent.parent.parent + ralph_script = prd_json.parent / "ralph.sh" + + if not ralph_script.exists(): + print(f"❌ ralph.sh nu există în {ralph_script.parent}") + return None + + print(f"🚀 Lansez Ralph loop") + print(f"📁 Project: {project_dir}") + print(f"🔁 Max iterations: {max_iterations}") + print(f"🌙 Background: {background}") + + cmd = [str(ralph_script), str(max_iterations)] + + if background: + # Rulează în background cu nohup + log_file = ralph_script.parent / "logs" / "ralph.log" + log_file.parent.mkdir(exist_ok=True) + + with open(log_file, 'w') as f: + process = subprocess.Popen( + cmd, + cwd=project_dir, + stdout=f, + stderr=subprocess.STDOUT, + start_new_session=True # Detach de terminal + ) + + print(f"✅ Ralph pornit în background (PID: {process.pid})") + print(f"📋 Log: {log_file}") + + # Salvează PID pentru tracking + pid_file = ralph_script.parent / ".ralph.pid" + with open(pid_file, 'w') as f: + f.write(str(process.pid)) + + return process + else: + # Rulează în foreground (pentru debug) + print("⚠️ Rulare în foreground (pentru debug)") + result = subprocess.run(cmd, cwd=project_dir) + return None + + +def check_status(project_dir: Path): + """ + Verifică status Ralph pentru un proiect + + Args: + project_dir: Director proiect + + Returns: + Dict cu status (stories complete/incomplete, learnings) + """ + prd_json = project_dir / "scripts" / "ralph" / "prd.json" + progress_file = project_dir / "scripts" / "ralph" / "progress.txt" + pid_file = project_dir / "scripts" / "ralph" / ".ralph.pid" + + status = { + "project": project_dir.name, + "running": False, + "complete": [], + "incomplete": [], + "learnings": [] + } + + # Verifică dacă rulează + if pid_file.exists(): + with open(pid_file) as f: + pid = int(f.read().strip()) + try: + os.kill(pid, 0) # Verifică dacă procesul există + status["running"] = True + status["pid"] = pid + except OSError: + status["running"] = False + + # Citește stories + if prd_json.exists(): + with open(prd_json) as f: + data = json.load(f) + for story in data.get('userStories', []): + if story.get('passes'): + status["complete"].append({ + "id": story['id'], + "title": story['title'] + }) + else: + status["incomplete"].append({ + "id": story['id'], + "title": story['title'], + "priority": story.get('priority', 999) + }) + + # Citește learnings (ultimele 10 linii) + if progress_file.exists(): + with open(progress_file) as f: + lines = f.readlines() + status["learnings"] = [l.strip() for l in lines[-10:] if l.strip()] + + return status + + +def main(): + """CLI pentru testing""" + if len(sys.argv) < 2: + print("Usage:") + print(" python ralph_workflow.py create PROJECT_NAME 'description'") + print(" python ralph_workflow.py status PROJECT_NAME") + sys.exit(1) + + command = sys.argv[1] + + if command == "create": + if len(sys.argv) < 4: + print("Usage: python ralph_workflow.py create PROJECT_NAME 'description'") + sys.exit(1) + + project_name = sys.argv[2] + description = sys.argv[3] + + # Pas 1: Creează PRD + prd_file = create_prd(project_name, description) + if not prd_file: + print("❌ Eroare la creare PRD") + sys.exit(1) + + # Pas 2: Convertește în prd.json + prd_json = convert_prd(prd_file) + if not prd_json: + print("❌ Eroare la conversie") + sys.exit(1) + + # Pas 3: Lansează Ralph în background + run_ralph(prd_json, max_iterations=20, background=True) + + print("\n✅ Workflow complet!") + print(f"📁 Project: {prd_json.parent.parent.parent}") + + elif command == "status": + if len(sys.argv) < 3: + print("Usage: python ralph_workflow.py status PROJECT_NAME") + sys.exit(1) + + project_name = sys.argv[2] + project_dir = WORKSPACE / project_name + + if not project_dir.exists(): + print(f"❌ Proiect nu există: {project_dir}") + sys.exit(1) + + status = check_status(project_dir) + + print(f"\n📊 Status: {status['project']}") + print(f"🔄 Running: {'DA (PID: ' + str(status.get('pid', '')) + ')' if status['running'] else 'NU'}") + print(f"✅ Complete: {len(status['complete'])}") + print(f"🔄 Incomplete: {len(status['incomplete'])}") + + if status['complete']: + print("\n✅ Stories complete:") + for s in status['complete'][:5]: + print(f" - {s['id']}: {s['title']}") + + if status['incomplete']: + print("\n🔄 Stories incomplete:") + for s in sorted(status['incomplete'], key=lambda x: x['priority'])[:5]: + print(f" - {s['id']} (P{s['priority']}): {s['title']}") + + if status['learnings']: + print("\n📚 Recent learnings:") + for l in status['learnings'][-5:]: + print(f" {l}") + + else: + print(f"❌ Comandă necunoscută: {command}") + sys.exit(1) + + +if __name__ == "__main__": + main()