Files
knowledge-base/scripts/kb-agent-loop.sh

132 lines
6.0 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 <<EOF
Ты работаешь в knowledge-base (ветка $BRANCH).
Запусти анализ: прочитай $HEALTH — это отчёт по качеству KB.
Цель: снизить score ($SCORE_BEFORE → меньше). Меньше = лучше.
Правила (жёсткие):
- НЕ удаляй .md-файлы. Только редактируй.
- НЕ трогай: .git/, .obsidian/, .claude/worktrees/, notes/claude/ (автосейвы), audit/*.md.
- НЕ меняй содержание proxmox-inventory.md, credentials.md, npm-proxy-hosts.md — это факт-файлы.
Что можно фиксить:
- missing_frontmatter: добавь блок \`\`\`---\\ndate: YYYY-MM-DD\\ntype: <decision|note|daily|project|reference>\\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 без спроса.
# Timeout 30 мин: если Opus зациклится / утечёт в Max-лимит — жёсткий kill.
# --model sonnet если хочется дешевле (но качество правок ниже).
if ! timeout 1800 claude -p --permission-mode acceptEdits "$PROMPT" >> "$LOG" 2>&1; then
rc=$?
log "claude exit rc=$rc (124 = timeout)"
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 ==="