Update memory, root, tools (+2 ~2)
This commit is contained in:
69
AGENTS.md
69
AGENTS.md
@@ -53,48 +53,61 @@ When I receive errors, bugs, or new feature requests:
|
|||||||
|
|
||||||
**2. NOAPTE (23:00, 03:00) - night-execute:**
|
**2. NOAPTE (23:00, 03:00) - night-execute:**
|
||||||
|
|
||||||
**A. Planning cu OPUS (eu, Echo):**
|
**A. Planning cu OPUS (eu, Echo) - pe moltbot:**
|
||||||
- SSH claude-agent: `ssh echo@10.0.20.201 "sudo pct exec 171 -- su - claude -c 'cd /workspace && bash'"`
|
```python
|
||||||
- Pentru PROIECTE NOI:
|
from tools.ralph_workflow import create_prd, convert_prd, run_ralph
|
||||||
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
|
|
||||||
|
|
||||||
**B. Implementare cu Ralph (Sonnet):**
|
# Pentru PROIECTE NOI
|
||||||
- Rulează `./scripts/ralph/ralph.sh 20` (max 20 iterații)
|
prd_file = create_prd(
|
||||||
- Ralph loop autonom:
|
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)
|
1. Selectează story (priority minimă, passes=false)
|
||||||
2. Rulează Claude Code (Sonnet) pentru implementare
|
2. Rulează Claude Code (Sonnet) pentru implementare
|
||||||
3. Quality checks: typecheck, lint, test
|
3. Quality checks: typecheck, lint, test
|
||||||
4. Commit dacă OK → passes: true
|
4. Commit dacă OK → passes: true
|
||||||
5. Update progress.txt cu learnings
|
5. Update progress.txt cu learnings
|
||||||
6. Repetă până toate complete
|
6. Repetă până toate complete sau max 20 iterații
|
||||||
- Git push toate commit-urile
|
- Git push toate commit-urile automat
|
||||||
- Marchează [x] în approved-tasks.md
|
|
||||||
|
|
||||||
**3. DIMINEAȚĂ (08:30) - morning-report:**
|
**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
|
- Status per story: ✅ complet / 🔄 în progres / ⚠️ blocat
|
||||||
- Learnings din progress.txt
|
- Learnings din progress.txt
|
||||||
- Link gitea: `https://gitea.romfast.ro/romfast/PROJECT-NAME`
|
- Link gitea: `https://gitea.romfast.ro/romfast/PROJECT-NAME`
|
||||||
|
|
||||||
### Mașină Development
|
### Mașină Development
|
||||||
|
|
||||||
**claude-agent (LXC 171):**
|
**moltbot (LXC 110) - LOCAL:**
|
||||||
- IP: 10.0.20.171
|
- User: moltbot
|
||||||
- User: claude
|
- Workspace: `~/workspace/`
|
||||||
- Workspace: `/workspace/`
|
- Claude Code: `~/.local/bin/claude` (v2.1.37)
|
||||||
- Claude Code instalat + Git configurat pentru gitea
|
- Ralph skill: `~/.claude/skills/ralph/`
|
||||||
- Ralph plugin: `/workspace/ralph-claude/`
|
- Comenzi: `/ralph:prd` (generare PRD) + `/ralph:convert` (conversie prd.json)
|
||||||
- Skills: `/prd` (generare PRD) + `/ralph` (conversie prd.json)
|
- Templates: ralph.sh, prompt.md (copiate automat)
|
||||||
- Script: `ralph.sh` - loop autonom
|
- Helper Python: `~/clawd/tools/ralph_workflow.py`
|
||||||
|
- `create_prd()`, `convert_prd()`, `run_ralph()`, `check_status()`
|
||||||
|
|
||||||
### Model Strategy (OBLIGATORIU)
|
### Model Strategy (OBLIGATORIU)
|
||||||
- **Opus** → Planning, PRD, stories (eu, Echo în night-execute)
|
- **Opus** → Planning, PRD, stories (eu, Echo în night-execute)
|
||||||
|
|||||||
11
TOOLS.md
11
TOOLS.md
@@ -57,6 +57,17 @@
|
|||||||
- **Format:** **Nume** → pași → 📊 Rezultat → 📚 Sursă
|
- **Format:** **Nume** → pași → 📊 Rezultat → 📚 Sursă
|
||||||
- **Flux actualizare:** Automat via insights-extract job
|
- **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
|
## 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)
|
**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)
|
||||||
|
|||||||
281
memory/kb/tools/ralph-workflow.md
Normal file
281
memory/kb/tools/ralph-workflow.md
Normal file
@@ -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)
|
||||||
331
tools/ralph_workflow.py
Executable file
331
tools/ralph_workflow.py
Executable file
@@ -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()
|
||||||
Reference in New Issue
Block a user