#!/bin/bash # kb-agent-loop — karpathy-style self-healing KB. # # Алгоритм: # 1. baseline = kb-health.py → score_before # 2. создать ветку auto-fix/YYYY-MM-DD # 3. Opus (claude -p) читает health-отчёт, правит файлы, коммитит # 4. score_after = kb-health.py # 5. если score_after < score_before → merge в main, push # иначе → удалить ветку, ничего не применять # # Работает ТОЛЬКО на code-server (LXC 132) где есть Max OAuth для Opus. # Запускать вручную или из cron в ночное окно (напр. воскр 03:00). set -u cd /root/knowledge-base || exit 1 LOG=/var/log/kb-agent-loop.log LOCK=/tmp/kb-agent-loop.lock DATE=$(date +%Y-%m-%d) BRANCH="auto-fix/$DATE" exec 9>"$LOCK" if ! flock -n 9; then echo "$(date -Iseconds) [SKIP] предыдущий прогон ещё идёт" >> "$LOG" exit 0 fi log() { echo "$(date -Iseconds) [INFO] $*" >> "$LOG"; } abort() { echo "$(date -Iseconds) [ABORT] $*" >> "$LOG"; exit 1; } # ──────────────── preflight ──────────────── log "=== kb-agent-loop start ===" # на main, чисто git checkout main -q || abort "checkout main failed" git pull --rebase --autostash -q origin main || abort "pull failed" if ! git diff --quiet || ! git diff --cached --quiet; then abort "working tree dirty" fi # свежий health-снимок python3 scripts/kb-health.py > /dev/null || abort "health.py failed" SCORE_BEFORE=$(python3 -c "import json; print(json.load(open('audit/health-latest.json'))['score'])") log "baseline score: $SCORE_BEFORE" # если score=0, нечего делать if [ "$SCORE_BEFORE" -eq 0 ]; then log "score=0, nothing to improve" exit 0 fi # ──────────────── branch ──────────────── git branch -D "$BRANCH" 2>/dev/null git checkout -b "$BRANCH" -q || abort "branch create failed" # ──────────────── Opus prompt ──────────────── HEALTH="audit/${DATE}-health.md" if [ ! -f "$HEALTH" ]; then abort "no health report for today" fi PROMPT=$(cat <\\ntags: [...]\\n---\`\`\` в начало. Дату бери из имени файла или git log. - broken_paths: обнови путь на правильный, или убери ссылку если файл исчез. - undated_todos: добавь "(YYYY-MM-DD)" в конец строки \`- [ ]\`. Дату — из имени daily/decision или сегодня. - duplicate_basenames: оставь как есть, если не очевидно что удалить. - orphan_files: добавь входящие ссылки в родственный файл если логично. Если не логично — пропусти. После правок сделай один или несколько git commit с префиксом "agent-loop: ...". Стоп когда считаешь что дальше делать нечего ИЛИ прошло много итераций. НЕ делай git push. НЕ делай merge. Это сделает обёрточный скрипт после проверки метрики. EOF ) log "calling claude -p (Opus)..." # --permission-mode acceptEdits — Claude Code сам применяет Edit без спроса # Bash-команды всё равно будут просить или быть ограниченными policy if ! claude -p --permission-mode acceptEdits "$PROMPT" >> "$LOG" 2>&1; then log "claude exit non-zero" fi # ──────────────── measure ──────────────── python3 scripts/kb-health.py > /dev/null || abort "health.py after run failed" SCORE_AFTER=$(python3 -c "import json; print(json.load(open('audit/health-latest.json'))['score'])") log "after score: $SCORE_AFTER" DELTA=$((SCORE_BEFORE - SCORE_AFTER)) # ──────────────── decide ──────────────── if [ "$SCORE_AFTER" -lt "$SCORE_BEFORE" ]; then log "✓ IMPROVEMENT: $SCORE_BEFORE → $SCORE_AFTER (−$DELTA), merging" # коммитим обновлённый health-report git add audit/ 2>/dev/null if ! git diff --cached --quiet; then git -c user.name=kb-agent-loop -c user.email=agent-loop@dttb.ru \ commit -m "agent-loop: health score $SCORE_BEFORE → $SCORE_AFTER" -q fi git checkout main -q if git merge --no-ff "$BRANCH" -m "kb-agent-loop: score $SCORE_BEFORE → $SCORE_AFTER (−$DELTA)" -q; then git push -q && log "✓ merged and pushed" git branch -d "$BRANCH" -q else log "✗ merge conflict, keeping branch $BRANCH for manual review" git merge --abort fi else log "✗ NO IMPROVEMENT: $SCORE_BEFORE → $SCORE_AFTER, resetting" git checkout main -q git branch -D "$BRANCH" -q # откатить audit/ если скрипт изменил git checkout -- audit/ 2>/dev/null fi log "=== kb-agent-loop end ==="