chore: auto-commit from dashboard
This commit is contained in:
@@ -104,7 +104,7 @@
|
|||||||
"949388626146517022"
|
"949388626146517022"
|
||||||
],
|
],
|
||||||
"user_name": "Marius",
|
"user_name": "Marius",
|
||||||
"default_voice": "M1",
|
"default_voice": "Marius 3",
|
||||||
"auto_leave_minutes": 5,
|
"auto_leave_minutes": 5,
|
||||||
"stt_model": "/home/moltbot/echo-core/models/whisper-small-ro-cv11-int8"
|
"stt_model": "/home/moltbot/echo-core/models/whisper-small-ro-cv11-int8"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
"report_on": "changes",
|
"report_on": "changes",
|
||||||
"timeout": 120,
|
"timeout": 120,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"last_run": "2026-07-10T16:00:00.002193+00:00",
|
"last_run": "2026-07-11T16:00:00.001582+00:00",
|
||||||
"last_status": "ok",
|
"last_status": "ok",
|
||||||
"next_run": "2026-07-11T10:00:00+00:00"
|
"next_run": "2026-07-14T10:00:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "security-audit-daily",
|
"name": "security-audit-daily",
|
||||||
@@ -243,9 +243,9 @@
|
|||||||
"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.",
|
"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": [],
|
"allowed_tools": [],
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"last_run": "2026-07-11T06:00:00.002630+00:00",
|
"last_run": "2026-07-11T14:00:00.001368+00:00",
|
||||||
"last_status": "ok",
|
"last_status": "ok",
|
||||||
"next_run": "2026-07-11T10:00:00+00:00"
|
"next_run": "2026-07-11T18:00:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "night-execute",
|
"name": "night-execute",
|
||||||
|
|||||||
1
scripts/ralph/.last-branch
Normal file
1
scripts/ralph/.last-branch
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ralph/echo-improve
|
||||||
124
scripts/ralph/prompt.md
Normal file
124
scripts/ralph/prompt.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# Ralph - Instrucțiuni pentru Iterație (smart gates)
|
||||||
|
|
||||||
|
Ești un agent autonom care implementează user stories dintr-un PRD. Aceasta este O SINGURĂ iterație — implementezi UN singur story, validezi prin gate-urile relevante, apoi te oprești.
|
||||||
|
|
||||||
|
## Workflow per iterație (4 faze, gates condiționale pe `story.tags`)
|
||||||
|
|
||||||
|
### Faza 0: Citește contextul
|
||||||
|
|
||||||
|
- PRD-ul (`prd.json`) și `progress.txt` sunt furnizate în context.
|
||||||
|
- Identifică următorul story candidate:
|
||||||
|
- `passes != true` ȘI `failed != true` ȘI `blocked != true`
|
||||||
|
- DAG: toate ID-urile din `dependsOn[]` au `passes == true` (altfel sare la următorul independent)
|
||||||
|
- Cea mai mică `priority` printre cele eligibile.
|
||||||
|
- Notează `techStack.commands` (lint, typecheck, test, start) și `techStack.port`.
|
||||||
|
- Notează `story.tags[]` — alegerea Faza 3 depinde de ele.
|
||||||
|
|
||||||
|
### Faza 1: IMPLEMENTARE (mereu)
|
||||||
|
|
||||||
|
1.1. **Branch management** — verifică să fii pe `branchName` din PRD; checkout/create dacă lipsește.
|
||||||
|
1.2. **Citește acceptance criteria** — fiecare criteriu e un test mental concret de trecut.
|
||||||
|
1.3. **Implementează cod minimal** — DOAR ce cere story-ul. Urmează patterns existente. Fără over-engineering, fără side features.
|
||||||
|
1.4. **Update `notes`** în `prd.json` cu fișierele atinse (pentru audit ulterior).
|
||||||
|
|
||||||
|
### Faza 2: QUALITY BASE (mereu, înainte de gates)
|
||||||
|
|
||||||
|
Folosește `techStack.commands`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
{techStack.commands.typecheck} # ex: npm run typecheck / mypy .
|
||||||
|
{techStack.commands.lint} # ex: npm run lint / ruff check .
|
||||||
|
{techStack.commands.test} # ex: npm test / pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
**Loop intern**: dacă vreuna eșuează → repară și repetă, max 3 retries în această fază. Dacă încă fail după 3, ieși cu sumar de erori în `progress.txt` (ralph.sh va decide retry-ul iterației).
|
||||||
|
|
||||||
|
### Faza 3: SMART GATES (dispatcher pe `story.tags`)
|
||||||
|
|
||||||
|
Tags posibile: `ui`, `db`, `vercel`, `refactor`, `docs`, `backend`, `infra`.
|
||||||
|
|
||||||
|
Aplică DOAR gate-urile potrivite — **nu rulează toate**:
|
||||||
|
|
||||||
|
| Tag | Gate |
|
||||||
|
|-------------|---------------------------------------------------------------------------------------|
|
||||||
|
| `refactor` | `/workflow:simplify` pe diff (reduce complexity fără behavior change) |
|
||||||
|
| `ui` | `/qa` Playwright/agent-browser snapshot pe `localhost:{techStack.port}` + screenshot |
|
||||||
|
| `ui` + `requiresDesignReview` | `/plan-design-review` pe screenshot capturat |
|
||||||
|
| `vercel` | push branch + `gh pr checks --watch` (timeout 5 min); fail dacă PR checks eșuează |
|
||||||
|
| `db` | verify schema diff (alembic / prisma migrate diff / `psql \\d+ tablename`) |
|
||||||
|
| `docs` | doar typecheck base (Faza 2 e suficient); skip gate dedicat |
|
||||||
|
| `backend` | `/review` pe diff (intern — second pass review pe API contracts, error handling) |
|
||||||
|
| `infra` | `/review` pe diff + manual smoke test al modificărilor (CI config, Dockerfile, etc.) |
|
||||||
|
| _(tags vide)_ | **run-all-gates fallback** — `/review` + `/qa` + `/workflow:simplify` (safe default) |
|
||||||
|
|
||||||
|
**Mecanism**: skill-urile gstack se invocă prin text mention în prompt — Claude (subprocess `claude -p`) le vede ca tool-uri disponibile via `~/.claude/skills/gstack/`.
|
||||||
|
|
||||||
|
**Multi-tag**: rulează gate-uri pentru fiecare tag (ex: `["ui", "backend"]` → atât `/qa` cât și `/review`).
|
||||||
|
|
||||||
|
**Important**: dacă vreun gate eșuează, NU marca `passes=true`. Repară (max 3 fix-uri în iterație) sau lasă pentru iterația următoare (ralph.sh se ocupă de retry counter).
|
||||||
|
|
||||||
|
### Faza 4: COMMIT + MARK
|
||||||
|
|
||||||
|
4.1. **Commit** cu mesaj descriptiv:
|
||||||
|
```
|
||||||
|
feat: [Story ID] - [Story Title]
|
||||||
|
|
||||||
|
- ce ai schimbat (1-3 bullets)
|
||||||
|
- gates rulate: typecheck PASS, lint PASS, /qa PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
4.2. **Update `prd.json`**:
|
||||||
|
- `passes: true` DOAR DACĂ toate gate-urile relevante au pasat
|
||||||
|
- `notes` populat cu rezultate gate (ex: "qa: ok, design-review: 8/10")
|
||||||
|
|
||||||
|
4.3. **Append `progress.txt`**:
|
||||||
|
```markdown
|
||||||
|
## Iterație: [timestamp]
|
||||||
|
### Story implementat: [ID] - [Title] (tags: [ui, backend])
|
||||||
|
### Status: Complete / Partial / Failed
|
||||||
|
|
||||||
|
### Gates rulate:
|
||||||
|
- Typecheck: PASS
|
||||||
|
- Lint: PASS
|
||||||
|
- Tests: PASS/SKIP
|
||||||
|
- /qa (ui): PASS — screenshot la scripts/ralph/screenshots/...
|
||||||
|
- /review (backend): PASS
|
||||||
|
|
||||||
|
### Learnings:
|
||||||
|
- [Patterns descoperite, gotchas]
|
||||||
|
|
||||||
|
### Next:
|
||||||
|
- [Stories eligibile pentru iterația următoare]
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reguli importante
|
||||||
|
|
||||||
|
1. **UN SINGUR STORY PE ITERAȚIE** — nu implementa mai mult de un story.
|
||||||
|
2. **DAG STRICT** — nu sări peste `dependsOn` neîmplinite.
|
||||||
|
3. **GATES PE TAGS** — rulează doar ce e relevant; tags vide = run-all-gates fallback.
|
||||||
|
4. **NU MARCA `passes=true` cu gate failed** — altfel ralph.sh nu va relua story-ul.
|
||||||
|
5. **FII CONCIS** — fără over-engineering, fără docs auto-generate dacă story-ul nu cere.
|
||||||
|
|
||||||
|
## Comenzi agent-browser (referință rapidă pentru gate `ui`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser navigate "http://localhost:{techStack.port}"
|
||||||
|
agent-browser snapshot # listă elemente compactă (@e1, @e2...)
|
||||||
|
agent-browser click @e5
|
||||||
|
agent-browser fill @e3 "value"
|
||||||
|
agent-browser screenshot ./scripts/ralph/screenshots/US-{id}-$(date +%Y%m%d-%H%M%S).png
|
||||||
|
agent-browser console # erori JS
|
||||||
|
agent-browser wait-for "Loading complete"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Condiție de terminare
|
||||||
|
|
||||||
|
Dacă TOATE story-urile au `passes: true` (sau combinat cu `failed: true` / `blocked: true` astfel că nimic nu mai e eligibil):
|
||||||
|
|
||||||
|
```
|
||||||
|
<promise>COMPLETE</promise>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
ÎNCEPE IMPLEMENTAREA ACUM.
|
||||||
387
scripts/ralph/ralph.sh
Executable file
387
scripts/ralph/ralph.sh
Executable file
@@ -0,0 +1,387 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Ralph pentru Claude Code - Loop autonom de agent AI (W3: smart gates + DAG + rate limit)
|
||||||
|
# Adaptat din Ralph original (snarktank/ralph) pentru Claude Code CLI
|
||||||
|
# Usage: ./ralph.sh [max_iterations] [project_dir]
|
||||||
|
#
|
||||||
|
# Env vars (opționale):
|
||||||
|
# RALPH_MAX_TURNS — --max-turns per iter (default 30)
|
||||||
|
# RALPH_RATE_LIMIT_SLEEP — sleep după rate limit detection (default 1800 = 30min)
|
||||||
|
# RALPH_DAG_HELPER — path la tools/ralph_dag.py (auto-detect default)
|
||||||
|
# RALPH_PYTHON — interpreter Python pentru DAG helper (default python3)
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
# W3 config
|
||||||
|
MAX_TURNS=${RALPH_MAX_TURNS:-30}
|
||||||
|
RATE_LIMIT_SLEEP=${RALPH_RATE_LIMIT_SLEEP:-1800}
|
||||||
|
RALPH_PYTHON=${RALPH_PYTHON:-python3}
|
||||||
|
|
||||||
|
# DAG helper auto-detect: prefer co-located cu echo-core; fallback la $SCRIPT_DIR
|
||||||
|
if [ -n "$RALPH_DAG_HELPER" ] && [ -f "$RALPH_DAG_HELPER" ]; then
|
||||||
|
DAG_HELPER="$RALPH_DAG_HELPER"
|
||||||
|
elif [ -f "/home/moltbot/echo-core/tools/ralph_dag.py" ]; then
|
||||||
|
DAG_HELPER="/home/moltbot/echo-core/tools/ralph_dag.py"
|
||||||
|
elif [ -f "/home/moltbot/echo-core-qc/tools/ralph_dag.py" ]; then
|
||||||
|
DAG_HELPER="/home/moltbot/echo-core-qc/tools/ralph_dag.py"
|
||||||
|
elif [ -f "$SCRIPT_DIR/ralph_dag.py" ]; then
|
||||||
|
DAG_HELPER="$SCRIPT_DIR/ralph_dag.py"
|
||||||
|
else
|
||||||
|
DAG_HELPER=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Usage helper auto-detect (rate limit budget tracking — best effort, niciodată
|
||||||
|
# blochează rularea Ralph dacă lipsește)
|
||||||
|
if [ -n "$RALPH_USAGE_HELPER" ] && [ -f "$RALPH_USAGE_HELPER" ]; then
|
||||||
|
USAGE_HELPER="$RALPH_USAGE_HELPER"
|
||||||
|
elif [ -f "/home/moltbot/echo-core/tools/ralph_usage.py" ]; then
|
||||||
|
USAGE_HELPER="/home/moltbot/echo-core/tools/ralph_usage.py"
|
||||||
|
elif [ -f "/home/moltbot/echo-core-instr/tools/ralph_usage.py" ]; then
|
||||||
|
USAGE_HELPER="/home/moltbot/echo-core-instr/tools/ralph_usage.py"
|
||||||
|
elif [ -f "$SCRIPT_DIR/ralph_usage.py" ]; then
|
||||||
|
USAGE_HELPER="$SCRIPT_DIR/ralph_usage.py"
|
||||||
|
else
|
||||||
|
USAGE_HELPER=""
|
||||||
|
fi
|
||||||
|
USAGE_FILE="$SCRIPT_DIR/usage.jsonl"
|
||||||
|
|
||||||
|
# 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 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# W3: nimic eligibil = toate sunt fie passes, fie failed, fie blocked
|
||||||
|
check_no_eligible() {
|
||||||
|
local n=$(jq '[.userStories[] | select(.passes != true and .failed != true and .blocked != true)] | length' "$PRD_FILE" 2>/dev/null || echo "999")
|
||||||
|
[ "$n" -eq 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# W3: alege next eligible story via DAG helper. Print story ID sau "" dacă nimic.
|
||||||
|
dag_next_story() {
|
||||||
|
if [ -n "$DAG_HELPER" ]; then
|
||||||
|
"$RALPH_PYTHON" "$DAG_HELPER" next-story "$PRD_FILE" 2>/dev/null || echo ""
|
||||||
|
else
|
||||||
|
# Fallback simplu (fără DAG): primul story cu passes!=true && failed!=true && blocked!=true, priority asc
|
||||||
|
jq -r '[.userStories[] | select(.passes != true and .failed != true and .blocked != true)] | sort_by(.priority) | .[0].id // ""' "$PRD_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dag_incr_retry() {
|
||||||
|
local sid="$1"
|
||||||
|
if [ -n "$DAG_HELPER" ]; then
|
||||||
|
"$RALPH_PYTHON" "$DAG_HELPER" incr-retry "$PRD_FILE" "$sid" 2>/dev/null || echo "0"
|
||||||
|
else
|
||||||
|
echo "0"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dag_mark_failed() {
|
||||||
|
local sid="$1" reason="$2"
|
||||||
|
if [ -n "$DAG_HELPER" ]; then
|
||||||
|
"$RALPH_PYTHON" "$DAG_HELPER" mark-failed "$PRD_FILE" "$sid" "$reason" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dag_force_tags() {
|
||||||
|
local sid="$1"
|
||||||
|
if [ -n "$DAG_HELPER" ]; then
|
||||||
|
"$RALPH_PYTHON" "$DAG_HELPER" force-tags "$PRD_FILE" "$sid" "$PROJECT_DIR" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# W3: detectează rate limit în output Claude (heuristic — Anthropic nu are exit code dedicat)
|
||||||
|
is_rate_limited() {
|
||||||
|
local output="$1"
|
||||||
|
echo "$output" | grep -qiE "rate limit|rate_limit_exceeded|429|too many requests"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Tracker pentru rate limit retry (max 1 retry de iterație-rate-limit per rulare)
|
||||||
|
RATE_LIMIT_RETRY_USED=0
|
||||||
|
|
||||||
|
# Loop principal
|
||||||
|
for i in $(seq 1 $MAX_ITERATIONS); do
|
||||||
|
echo ""
|
||||||
|
echo "==================================================================="
|
||||||
|
echo " Ralph Iterația $i din $MAX_ITERATIONS"
|
||||||
|
echo "==================================================================="
|
||||||
|
|
||||||
|
# W3: alege next story via DAG (propagă blocked dacă vreun dep a eșuat)
|
||||||
|
CURRENT_STORY=$(dag_next_story)
|
||||||
|
if [ -z "$CURRENT_STORY" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "==================================================================="
|
||||||
|
if check_all_complete; then
|
||||||
|
echo " TOATE STORY-URILE DIN PRD SUNT COMPLETE!"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo " NICIUN STORY ELIGIBIL (toate fie complete, fie failed, fie blocked)"
|
||||||
|
echo " Stories incomplete:"
|
||||||
|
jq -r '.userStories[] | select(.passes != true) | " - \(.id): \(.title) [failed=\(.failed // false) blocked=\(.blocked // false) retries=\(.retries // 0)]"' "$PRD_FILE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Status curent
|
||||||
|
COMPLETE_NOW=$(jq '[.userStories[] | select(.passes == true)] | length' "$PRD_FILE")
|
||||||
|
NEXT_TITLE=$(jq -r --arg id "$CURRENT_STORY" '.userStories[] | select(.id == $id) | "\(.id): \(.title)"' "$PRD_FILE")
|
||||||
|
STORY_TAGS=$(jq -r --arg id "$CURRENT_STORY" '.userStories[] | select(.id == $id) | (.tags // []) | join(",")' "$PRD_FILE")
|
||||||
|
STORY_RETRIES=$(jq -r --arg id "$CURRENT_STORY" '.userStories[] | select(.id == $id) | (.retries // 0)' "$PRD_FILE")
|
||||||
|
echo " Progress: $COMPLETE_NOW / $TOTAL_STORIES stories complete"
|
||||||
|
echo " Next: $NEXT_TITLE [tags: ${STORY_TAGS:-<none>}, retries: $STORY_RETRIES]"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Pregătește prompt-ul cu context
|
||||||
|
FULL_PROMPT=$(cat <<EOF
|
||||||
|
# Context pentru această iterație Ralph
|
||||||
|
|
||||||
|
## Story țintă (DAG-eligible):
|
||||||
|
$CURRENT_STORY (tags: ${STORY_TAGS:-<none>})
|
||||||
|
|
||||||
|
## 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 + --max-turns pentru control runtime
|
||||||
|
set +e
|
||||||
|
echo "$FULL_PROMPT" | claude -p \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--output-format json \
|
||||||
|
--max-turns "$MAX_TURNS" \
|
||||||
|
2>&1 | tee "$LOG_FILE"
|
||||||
|
CLAUDE_EXIT=${PIPESTATUS[1]}
|
||||||
|
set -e
|
||||||
|
OUTPUT=$(cat "$LOG_FILE")
|
||||||
|
|
||||||
|
# Rate limit budget tracking (best-effort, never blocks Ralph)
|
||||||
|
if [ -n "$USAGE_HELPER" ]; then
|
||||||
|
"$RALPH_PYTHON" "$USAGE_HELPER" append \
|
||||||
|
"$USAGE_FILE" "$LOG_FILE" \
|
||||||
|
--slug "$PROJECT_NAME" \
|
||||||
|
--story-id "$CURRENT_STORY" \
|
||||||
|
--iter "$i" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# W3: rate limit detection (max 1 retry per rulare)
|
||||||
|
if is_rate_limited "$OUTPUT" || [ "$CLAUDE_EXIT" = "29" ]; then
|
||||||
|
if [ "$RATE_LIMIT_RETRY_USED" = "0" ]; then
|
||||||
|
echo ""
|
||||||
|
echo " ⏸️ Rate limit detectat. Sleep ${RATE_LIMIT_SLEEP}s, apoi retry o dată."
|
||||||
|
RATE_LIMIT_RETRY_USED=1
|
||||||
|
echo "## Rate limit la iter $i — sleep $RATE_LIMIT_SLEEP" >> "$PROGRESS_FILE"
|
||||||
|
sleep "$RATE_LIMIT_SLEEP"
|
||||||
|
continue # retry aceeași iterație
|
||||||
|
else
|
||||||
|
echo " ❌ Rate limit din nou — abort run, mark $CURRENT_STORY rate_limited"
|
||||||
|
dag_mark_failed "$CURRENT_STORY" "rate_limited"
|
||||||
|
echo "## Rate limit final la iter $i — abort" >> "$PROGRESS_FILE"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# W3: tag validation post-iter — chiar dacă Opus a marcat docs, dacă diff atinge .vue/.tsx, force ui
|
||||||
|
dag_force_tags "$CURRENT_STORY" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# W3: dacă story-ul curent ÎNCĂ nu trece (passes==false), incrementăm retries
|
||||||
|
STILL_INCOMPLETE=$(jq -r --arg id "$CURRENT_STORY" '.userStories[] | select(.id == $id) | (.passes == true)' "$PRD_FILE")
|
||||||
|
if [ "$STILL_INCOMPLETE" != "true" ]; then
|
||||||
|
NEW_RETRY=$(dag_incr_retry "$CURRENT_STORY")
|
||||||
|
echo " Story $CURRENT_STORY încă incomplet. Retries: $NEW_RETRY/3"
|
||||||
|
if [ "$NEW_RETRY" -ge 3 ] 2>/dev/null; then
|
||||||
|
echo " ❌ $CURRENT_STORY failed: max_retries — sare la următorul"
|
||||||
|
# mark-failed e deja făcut de incr-retry când >=3, dar idempotent o re-aplicăm
|
||||||
|
dag_mark_failed "$CURRENT_STORY" "max_retries"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ✅ Story $CURRENT_STORY marcat passes=true în iterația asta."
|
||||||
|
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
|
||||||
15
scripts/ralph/usage.jsonl
Normal file
15
scripts/ralph/usage.jsonl
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{"ts": "2026-07-11T09:59:58+00:00", "slug": "echo-core", "story_id": "US-001", "iter": 1, "total_cost_usd": 1.1788641000000002, "input_tokens": 6801, "output_tokens": 10662, "cache_read": 2148867, "model": null, "duration_ms": 220891}
|
||||||
|
{"ts": "2026-07-11T10:03:08+00:00", "slug": "echo-core", "story_id": "US-001", "iter": 2, "total_cost_usd": 1.5010484999999998, "input_tokens": 6500, "output_tokens": 11928, "cache_read": 2264505, "model": null, "duration_ms": 186645}
|
||||||
|
{"ts": "2026-07-11T10:05:11+00:00", "slug": "echo-core", "story_id": "US-002", "iter": 3, "total_cost_usd": 1.1340510999999998, "input_tokens": 6635, "output_tokens": 5361, "cache_read": 1580897, "model": null, "duration_ms": 119118}
|
||||||
|
{"ts": "2026-07-11T10:08:05+00:00", "slug": "echo-core", "story_id": "US-003", "iter": 4, "total_cost_usd": 1.4051268999999997, "input_tokens": 6788, "output_tokens": 12175, "cache_read": 2484183, "model": null, "duration_ms": 170071}
|
||||||
|
{"ts": "2026-07-11T10:12:32+00:00", "slug": "echo-core", "story_id": "US-004", "iter": 5, "total_cost_usd": 1.6546155, "input_tokens": 6800, "output_tokens": 14493, "cache_read": 2492325, "model": null, "duration_ms": 263624}
|
||||||
|
{"ts": "2026-07-11T10:15:29+00:00", "slug": "echo-core", "story_id": "US-004", "iter": 6, "total_cost_usd": 1.0671055, "input_tokens": 6631, "output_tokens": 11521, "cache_read": 1651815, "model": null, "duration_ms": 172588}
|
||||||
|
{"ts": "2026-07-11T10:17:28+00:00", "slug": "echo-core", "story_id": "US-005", "iter": 7, "total_cost_usd": 1.3640780000000001, "input_tokens": 6646, "output_tokens": 6338, "cache_read": 2280720, "model": null, "duration_ms": 115308}
|
||||||
|
{"ts": "2026-07-11T10:23:30+00:00", "slug": "echo-core", "story_id": "US-006", "iter": 8, "total_cost_usd": 2.7279519999999997, "input_tokens": 6788, "output_tokens": 27979, "cache_read": 2733777, "model": null, "duration_ms": 358414}
|
||||||
|
{"ts": "2026-07-11T10:28:15+00:00", "slug": "echo-core", "story_id": "US-006", "iter": 9, "total_cost_usd": 2.57715855, "input_tokens": 679, "output_tokens": 7970, "cache_read": 1385564, "model": null, "duration_ms": 122619}
|
||||||
|
{"ts": "2026-07-11T10:31:41+00:00", "slug": "echo-core", "story_id": "US-007", "iter": 10, "total_cost_usd": 1.4841890000000002, "input_tokens": 6801, "output_tokens": 14697, "cache_read": 2548080, "model": null, "duration_ms": 201899}
|
||||||
|
{"ts": "2026-07-11T10:36:11+00:00", "slug": "echo-core", "story_id": "US-008", "iter": 11, "total_cost_usd": 1.6913165, "input_tokens": 10857, "output_tokens": 20527, "cache_read": 2717765, "model": null, "duration_ms": 266234}
|
||||||
|
{"ts": "2026-07-11T10:38:19+00:00", "slug": "echo-core", "story_id": "US-008", "iter": 12, "total_cost_usd": 0.9906942, "input_tokens": 16171, "output_tokens": 7730, "cache_read": 1350904, "model": null, "duration_ms": 123527}
|
||||||
|
{"ts": "2026-07-11T10:42:30+00:00", "slug": "echo-core", "story_id": "US-009", "iter": 13, "total_cost_usd": 1.7101233999999998, "input_tokens": 7439, "output_tokens": 19964, "cache_read": 2799168, "model": null, "duration_ms": 247394}
|
||||||
|
{"ts": "2026-07-11T10:44:39+00:00", "slug": "echo-core", "story_id": "US-009", "iter": 14, "total_cost_usd": 1.0942976, "input_tokens": 6648, "output_tokens": 7613, "cache_read": 1931572, "model": null, "duration_ms": 125381}
|
||||||
|
{"ts": "2026-07-11T11:16:45+00:00", "slug": "echo-core", "story_id": "US-010", "iter": 15, "total_cost_usd": 1.1445886, "input_tokens": 6795, "output_tokens": 8082, "cache_read": 2054902, "model": null, "duration_ms": 124204}
|
||||||
104
tasks/prd-echo-core.md
Normal file
104
tasks/prd-echo-core.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# PRD: Echo Core
|
||||||
|
|
||||||
|
## 1. Introducere
|
||||||
|
|
||||||
|
Integrare pocket-tts (Kyutai) ca engine TTS nou in Echo Core.
|
||||||
|
|
||||||
|
**Data:** 2026-07-11
|
||||||
|
**Status:** Draft
|
||||||
|
**Mode:** FEATURE
|
||||||
|
|
||||||
|
## 2. Context Tehnic
|
||||||
|
|
||||||
|
**Proiect existent detectat:**
|
||||||
|
- Stack: python-legacy
|
||||||
|
- Config: requirements.txt
|
||||||
|
- Scripts:
|
||||||
|
|
||||||
|
## 3. Obiective
|
||||||
|
|
||||||
|
### Obiectiv Principal
|
||||||
|
Integrare pocket-tts (Kyutai) ca engine TTS nou in Echo Core
|
||||||
|
|
||||||
|
### Obiective Secundare
|
||||||
|
- Integrare pocket-tts (Kyutai) ca engine TTS nou in Echo Core
|
||||||
|
|
||||||
|
### Metrici de Succes
|
||||||
|
- Toate funcționalitățile implementate conform spec
|
||||||
|
- Tests passing (coverage > 80%)
|
||||||
|
- Code quality: lint + typecheck pass
|
||||||
|
|
||||||
|
## 4. User Stories
|
||||||
|
|
||||||
|
### US-001: Integrare pocket-tts (Kyutai) ca engine TTS nou in Echo Core
|
||||||
|
**Ca** utilizator
|
||||||
|
**Vreau** integrare pocket-tts (kyutai) ca engine tts nou in echo core
|
||||||
|
**Pentru că** pot folosi aplicația eficient
|
||||||
|
|
||||||
|
**Acceptance Criteria:**
|
||||||
|
- [ ] Funcționalitatea implementată conform descrierii
|
||||||
|
- [ ] Input validation în loc
|
||||||
|
- [ ] Error handling pentru cazuri edge
|
||||||
|
- [ ] Tests cu pytest (coverage > 80%)
|
||||||
|
- [ ] Code quality: lint + typecheck pass
|
||||||
|
|
||||||
|
**Priority:** 10
|
||||||
|
|
||||||
|
### US-002: Tests și Documentație
|
||||||
|
**Ca** developer
|
||||||
|
**Vreau** teste comprehensive și documentație
|
||||||
|
**Pentru că** asigur calitatea codului
|
||||||
|
|
||||||
|
**Acceptance Criteria:**
|
||||||
|
- [ ] Unit tests pentru toate funcțiile (coverage > 80%)
|
||||||
|
- [ ] Integration tests pentru flow-uri principale
|
||||||
|
- [ ] README cu instrucțiuni de utilizare
|
||||||
|
- [ ] Docstrings pentru funcții publice
|
||||||
|
- [ ] pytest rulează fără erori
|
||||||
|
|
||||||
|
**Priority:** 20
|
||||||
|
|
||||||
|
## 5. Cerințe Funcționale
|
||||||
|
|
||||||
|
1. [REQ-001] Integrare pocket-tts (Kyutai) ca engine TTS nou in Echo Core
|
||||||
|
|
||||||
|
## 6. Non-Goals (Ce NU facem)
|
||||||
|
|
||||||
|
- Interfață grafică (GUI) - doar CLI/API
|
||||||
|
- Suport multiple limbaje - doar Python
|
||||||
|
- Deployment infrastructure - doar cod functional
|
||||||
|
|
||||||
|
## 7. Considerații Tehnice
|
||||||
|
|
||||||
|
### Stack/Tehnologii
|
||||||
|
- Limbaj: Python
|
||||||
|
- Testing: pytest
|
||||||
|
- Linting: pylint / eslint (depinde de stack)
|
||||||
|
- Type checking: mypy / typescript
|
||||||
|
|
||||||
|
### Patterns de Urmat
|
||||||
|
- Clean code principles
|
||||||
|
- SOLID principles unde aplicabil
|
||||||
|
- Error handling consistent
|
||||||
|
- Input validation strict
|
||||||
|
|
||||||
|
### Riscuri Tehnice
|
||||||
|
- Edge cases la input validation
|
||||||
|
- Performance pentru volume mari de date (dacă aplicabil)
|
||||||
|
|
||||||
|
## 8. Considerații Security
|
||||||
|
|
||||||
|
- Input validation pentru toate datele externe
|
||||||
|
- Error messages fără info sensibilă
|
||||||
|
- Principle of least privilege
|
||||||
|
|
||||||
|
## 9. Open Questions
|
||||||
|
|
||||||
|
- [ ] Performance requirements specifice?
|
||||||
|
- [ ] Limite pe input sizes?
|
||||||
|
- [ ] Specific error handling patterns preferați?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Generated by:** Echo (Ralph PRD Generator)
|
||||||
|
**Date:** 2026-07-11 09:54
|
||||||
Reference in New Issue
Block a user