This commit is contained in:
Dmitry 2025-11-21 00:28:55 +03:00
parent 185278c016
commit e5729bfd25
26 changed files with 5452 additions and 0 deletions

View file

@ -0,0 +1,177 @@
name: AI Agent - Development
on:
workflow_dispatch:
inputs:
component:
description: 'Component to develop'
required: true
type: choice
options:
- web_v2
- backend
- common
jobs:
development:
runs-on: ubuntu-latest
timeout-minutes: 360 # 6 hours
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Python
run: |
sudo apt-get update
sudo apt-get install -y python3 python3-pip jq
python3 --version
- name: Set up Flutter (for web_v2)
if: inputs.component == 'web_v2'
run: |
# Install Flutter
git clone https://github.com/flutter/flutter.git -b stable --depth 1 $HOME/flutter
export PATH="$HOME/flutter/bin:$PATH"
echo "$HOME/flutter/bin" >> $GITHUB_PATH
flutter --version
flutter doctor
- name: Set up Dart (for backend/common)
if: inputs.component != 'web_v2'
run: |
# Install Dart SDK
sudo apt-get update
sudo apt-get install -y apt-transport-https
wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/dart.gpg
echo 'deb [signed-by=/usr/share/keyrings/dart.gpg arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart
export PATH="/usr/lib/dart/bin:$PATH"
echo "/usr/lib/dart/bin" >> $GITHUB_PATH
dart --version
- name: Install Cursor CLI
run: |
curl https://cursor.com/install -fsS | bash
export PATH="$HOME/.cursor/bin:$PATH"
echo "$HOME/.cursor/bin" >> $GITHUB_PATH
- name: Configure Git
run: |
git config --global user.name "AI Agent"
git config --global user.email "ai-agent@mnemo-cards.com"
- name: Install Dependencies (web_v2)
if: inputs.component == 'web_v2'
run: |
cd mnemo_cards_web_v2
flutter pub get
- name: Install Dependencies (backend)
if: inputs.component == 'backend'
run: |
cd mnemo_cards_backend
dart pub get
- name: Install Dependencies (common)
if: inputs.component == 'common'
run: |
cd mnemo_cards_common
dart pub get
- name: Run Development Agent
env:
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
CURSOR_MODEL: ${{ secrets.CURSOR_MODEL }}
PROJECT_ROOT: ${{ github.workspace }}
MAX_ITERATIONS: 10
MAX_RETRIES: 3
TIMEOUT_HOURS: 6
run: |
cd ${{ github.workspace }}
python3 tools/agent/agent_orchestrator.py ${{ inputs.component }}
- name: Create Summary Report
if: always()
run: |
COMPONENT="${{ inputs.component }}"
STATE_FILE="ai_docs/agent/${COMPONENT}/agent_state.json"
echo "# Development Agent Summary" > /tmp/summary.md
echo "" >> /tmp/summary.md
echo "**Component:** ${COMPONENT}" >> /tmp/summary.md
echo "**Started:** $(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> /tmp/summary.md
echo "" >> /tmp/summary.md
if [ -f "$STATE_FILE" ]; then
echo "## Agent State" >> /tmp/summary.md
echo "\`\`\`json" >> /tmp/summary.md
cat "$STATE_FILE" >> /tmp/summary.md
echo "\`\`\`" >> /tmp/summary.md
fi
cat /tmp/summary.md
- name: Check if all tasks completed
id: check_completion
run: |
COMPONENT="${{ inputs.component }}"
STATE_FILE="ai_docs/agent/${COMPONENT}/agent_state.json"
TASK_LIST="ai_docs/agent/${COMPONENT}/task_list.json"
if [ -f "$STATE_FILE" ] && [ -f "$TASK_LIST" ]; then
# Check if all tasks are completed
python3 << 'EOF'
import json
import os
import sys
component = os.environ.get('COMPONENT', 'unknown')
state_file = f"ai_docs/agent/{component}/agent_state.json"
task_list_file = f"ai_docs/agent/{component}/task_list.json"
try:
with open(state_file, 'r') as f:
state = json.load(f)
with open(task_list_file, 'r') as f:
task_list = json.load(f)
tasks = task_list.get('tasks', [])
pending = [t for t in tasks if t.get('status') == 'pending']
# Check if status is completed or no pending tasks
if state.get('status') == 'completed' or len(pending) == 0:
print("all_completed=true")
else:
print("all_completed=false")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
print("all_completed=false")
EOF
else
echo "all_completed=false"
fi > /tmp/completion_status.txt
cat /tmp/completion_status.txt >> $GITHUB_OUTPUT
env:
COMPONENT: ${{ inputs.component }}
- name: Trigger Planning Agent (if all completed)
if: contains(steps.check_completion.outputs.*, 'all_completed=true')
run: |
COMPONENT="${{ inputs.component }}"
echo "All tasks completed! Triggering planning agent for new tasks..."
# Trigger via Forgejo API
API_URL="${{ github.api_url }}/repos/${{ github.repository }}/actions/workflows/agent-planning.yml/dispatches"
curl -X POST "$API_URL" \
-H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"ref\":\"master\",\"inputs\":{\"component\":\"$COMPONENT\"}}"

View file

@ -0,0 +1,162 @@
name: AI Agent - Planning
on:
workflow_dispatch:
inputs:
component:
description: 'Component to plan for'
required: true
type: choice
options:
- web_v2
- backend
- common
jobs:
planning:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Python
run: |
sudo apt-get update
sudo apt-get install -y python3 python3-pip
python3 --version
- name: Install Cursor CLI
run: |
curl https://cursor.com/install -fsS | bash
export PATH="$HOME/.cursor/bin:$PATH"
echo "$HOME/.cursor/bin" >> $GITHUB_PATH
- name: Configure Git
run: |
git config --global user.name "AI Agent"
git config --global user.email "ai-agent@mnemo-cards.com"
- name: Run Planning Agent
env:
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
CURSOR_MODEL: ${{ secrets.CURSOR_MODEL }}
PROJECT_ROOT: ${{ github.workspace }}
MAX_ITERATIONS: 10
run: |
cd ${{ github.workspace }}
python3 tools/agent/planning_agent.py ${{ inputs.component }}
- name: Parse Task List and Create Summary
if: success()
id: parse_tasks
run: |
COMPONENT="${{ inputs.component }}"
TASK_LIST_PATH="ai_docs/agent/${COMPONENT}/task_list.json"
if [ ! -f "$TASK_LIST_PATH" ]; then
echo "Task list not found"
exit 0
fi
# Parse JSON with Python
python3 << 'EOF'
import json
import os
component = os.environ.get('COMPONENT', 'unknown')
task_list_path = f"ai_docs/agent/{component}/task_list.json"
try:
with open(task_list_path, 'r') as f:
data = json.load(f)
tasks = data.get('tasks', [])
high = sum(1 for t in tasks if t.get('priority') == 'high')
medium = sum(1 for t in tasks if t.get('priority') == 'medium')
low = sum(1 for t in tasks if t.get('priority') == 'low')
total_hours = sum(t.get('estimated_hours', 0) for t in tasks)
# Create issue body
body = f"# 🎯 Planning Summary: {component}\\n\\n"
body += f"**Generated:** {data.get('generated_at', 'N/A')}\\n"
body += f"**Total Tasks:** {len(tasks)}\\n"
body += f"**Estimated Hours:** {total_hours:.1f}h\\n\\n"
body += "## Priority Breakdown\\n\\n"
body += f"- 🔴 **HIGH:** {high} tasks\\n"
body += f"- 🟡 **MEDIUM:** {medium} tasks\\n"
body += f"- 🟢 **LOW:** {low} tasks\\n\\n"
high_tasks = [t for t in tasks if t.get('priority') == 'high']
if high_tasks:
body += "## 🔴 High Priority Tasks\\n\\n"
for task in high_tasks:
body += f"### {task['id']}: {task['title']}\\n"
body += f"- **Estimated:** {task['estimated_hours']}h\\n"
body += f"- **Status:** {task['status']}\\n"
desc = task['description'][:200]
body += f"- **Description:** {desc}...\\n\\n"
body += "\\n---\\n*Generated by AI Planning Agent*"
# Save to file for next step
with open('/tmp/issue_body.txt', 'w') as f:
f.write(body)
title = f"📋 Planning: {component} - {len(tasks)} tasks ({total_hours:.1f}h)"
with open('/tmp/issue_title.txt', 'w') as f:
f.write(title)
print(f"Tasks: {len(tasks)}, High: {high}, Medium: {medium}, Low: {low}")
except Exception as e:
print(f"Error: {e}")
exit(1)
EOF
env:
COMPONENT: ${{ inputs.component }}
- name: Create Issue via Forgejo API
if: success()
run: |
COMPONENT="${{ inputs.component }}"
if [ ! -f "/tmp/issue_body.txt" ] || [ ! -f "/tmp/issue_title.txt" ]; then
echo "Issue files not found, skipping issue creation"
exit 0
fi
TITLE=$(cat /tmp/issue_title.txt)
BODY=$(cat /tmp/issue_body.txt)
# Forgejo API endpoint (adjust to your Forgejo instance)
API_URL="${{ github.api_url }}/repos/${{ github.repository }}/issues"
# Create issue using curl
curl -X POST "$API_URL" \
-H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \
-H "Content-Type: application/json" \
-d @- << EOF
{
"title": "$TITLE",
"body": $(echo "$BODY" | jq -Rs .),
"labels": ["ai-agent", "planning", "$COMPONENT"]
}
EOF
- name: Trigger Development Workflow
if: success()
run: |
COMPONENT="${{ inputs.component }}"
# Trigger via Forgejo API
API_URL="${{ github.api_url }}/repos/${{ github.repository }}/actions/workflows/agent-development.yml/dispatches"
curl -X POST "$API_URL" \
-H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"ref\":\"master\",\"inputs\":{\"component\":\"$COMPONENT\"}}"

View file

@ -0,0 +1,255 @@
name: AI Agent - Test & Deploy
on:
push:
branches:
- master
paths:
- 'mnemo_cards_web_v2/**'
- 'mnemo_cards_backend/**'
- 'mnemo_cards_common/**'
workflow_dispatch:
inputs:
component:
description: 'Component to test'
required: true
type: choice
options:
- web_v2
- backend
- common
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
web_v2: ${{ steps.changes.outputs.web_v2 }}
backend: ${{ steps.changes.outputs.backend }}
common: ${{ steps.changes.outputs.common }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Detect changes
id: changes
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# Manual trigger - test specified component
case "${{ inputs.component }}" in
web_v2)
echo "web_v2=true" >> $GITHUB_OUTPUT
;;
backend)
echo "backend=true" >> $GITHUB_OUTPUT
;;
common)
echo "common=true" >> $GITHUB_OUTPUT
;;
esac
else
# Auto trigger - detect from git diff
git diff --name-only HEAD^ HEAD > changed_files.txt
if grep -q "mnemo_cards_web_v2/" changed_files.txt; then
echo "web_v2=true" >> $GITHUB_OUTPUT
else
echo "web_v2=false" >> $GITHUB_OUTPUT
fi
if grep -q "mnemo_cards_backend/" changed_files.txt; then
echo "backend=true" >> $GITHUB_OUTPUT
else
echo "backend=false" >> $GITHUB_OUTPUT
fi
if grep -q "mnemo_cards_common/" changed_files.txt; then
echo "common=true" >> $GITHUB_OUTPUT
else
echo "common=false" >> $GITHUB_OUTPUT
fi
fi
test-web-v2:
needs: detect-changes
if: needs.detect-changes.outputs.web_v2 == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- name: Set up Flutter
run: |
git clone https://github.com/flutter/flutter.git -b stable --depth 1 $HOME/flutter
export PATH="$HOME/flutter/bin:$PATH"
echo "$HOME/flutter/bin" >> $GITHUB_PATH
flutter --version
flutter doctor
- name: Install dependencies
run: |
cd mnemo_cards_web_v2
flutter pub get
- name: Run analyzer
run: |
cd mnemo_cards_web_v2
flutter analyze
- name: Run tests
run: |
cd mnemo_cards_web_v2
flutter test --coverage
- name: Build web
run: |
cd mnemo_cards_web_v2
flutter build web --release
- name: Create test report
if: always()
run: |
echo "# Web v2 Test Results" > /tmp/test_report.md
echo "✅ Tests passed" >> /tmp/test_report.md
cat /tmp/test_report.md
test-backend:
needs: detect-changes
if: needs.detect-changes.outputs.backend == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- name: Set up Dart
run: |
sudo apt-get update
sudo apt-get install -y apt-transport-https
wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/dart.gpg
echo 'deb [signed-by=/usr/share/keyrings/dart.gpg arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart
export PATH="/usr/lib/dart/bin:$PATH"
echo "/usr/lib/dart/bin" >> $GITHUB_PATH
dart --version
- name: Install dependencies
run: |
cd mnemo_cards_backend
dart pub get
- name: Run analyzer
run: |
cd mnemo_cards_backend
dart analyze
- name: Run tests
run: |
cd mnemo_cards_backend
dart test
- name: Create test report
if: always()
run: |
echo "# Backend Test Results" > /tmp/test_report.md
echo "✅ Tests passed" >> /tmp/test_report.md
cat /tmp/test_report.md
test-common:
needs: detect-changes
if: needs.detect-changes.outputs.common == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v3
- name: Set up Dart
run: |
sudo apt-get update
sudo apt-get install -y apt-transport-https
wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/dart.gpg
echo 'deb [signed-by=/usr/share/keyrings/dart.gpg arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart
export PATH="/usr/lib/dart/bin:$PATH"
echo "/usr/lib/dart/bin" >> $GITHUB_PATH
dart --version
- name: Install dependencies
run: |
cd mnemo_cards_common
dart pub get
- name: Run analyzer
run: |
cd mnemo_cards_common
dart analyze
- name: Run tests
run: |
cd mnemo_cards_common
dart test
- name: Create test report
if: always()
run: |
echo "# Common Test Results" > /tmp/test_report.md
echo "✅ Tests passed" >> /tmp/test_report.md
cat /tmp/test_report.md
deploy-staging:
needs: [test-web-v2, test-backend, test-common]
if: always() && (needs.test-web-v2.result == 'success' || needs.test-backend.result == 'success')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy notification
run: |
echo "# Deployment" > /tmp/deploy_summary.md
echo "🚀 Ready for deployment to staging" >> /tmp/deploy_summary.md
echo "Note: Actual deployment logic should be added here" >> /tmp/deploy_summary.md
cat /tmp/deploy_summary.md
report-failure:
needs: [test-web-v2, test-backend, test-common]
if: always() && (needs.test-web-v2.result == 'failure' || needs.test-backend.result == 'failure' || needs.test-common.result == 'failure')
runs-on: ubuntu-latest
steps:
- name: Create failure issue via Forgejo API
run: |
# Collect failed components
COMPONENTS=""
if [ "${{ needs.test-web-v2.result }}" = "failure" ]; then
COMPONENTS="${COMPONENTS}web_v2,"
fi
if [ "${{ needs.test-backend.result }}" = "failure" ]; then
COMPONENTS="${COMPONENTS}backend,"
fi
if [ "${{ needs.test-common.result }}" = "failure" ]; then
COMPONENTS="${COMPONENTS}common,"
fi
COMPONENTS=${COMPONENTS%,} # Remove trailing comma
# Create issue body
TITLE="🤖 AI Agent Test Failure: ${COMPONENTS}"
BODY="# ❌ Test Failure\\n\\n"
BODY="${BODY}**Components:** ${COMPONENTS}\\n"
BODY="${BODY}**Workflow:** ${{ github.workflow }}\\n"
BODY="${BODY}**Run Number:** ${{ github.run_number }}\\n\\n"
BODY="${BODY}[View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\\n\\n"
BODY="${BODY}---\\n*This issue was created automatically by the AI Agent system*"
# Create issue via Forgejo API
API_URL="${{ github.api_url }}/repos/${{ github.repository }}/issues"
curl -X POST "$API_URL" \
-H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"title\":\"${TITLE}\",\"body\":\"${BODY}\",\"labels\":[\"ai-agent\",\"test-failure\"]}"

View file

@ -0,0 +1,309 @@
# AI Agent 24/7 Automation System - Summary
## Что создано
Полноценная система автоматизированной разработки с использованием AI агентов для Forgejo.
## Архитектура
```
┌─────────────────────────────────────────┐
│ Forgejo Actions (Workflows) │
├─────────────────────────────────────────┤
│ │
│ Planning Agent → Development Agent │
│ ↓ ↓ │
│ Task List → Code Changes │
│ ↓ │
│ Test & Deploy │
│ │
└─────────────────────────────────────────┘
Cursor CLI (AI Provider)
Claude Sonnet 4.5
```
## Созданные файлы
### 📁 Workflows (3 файла)
1. **`.github/workflows/agent-planning.yml`**
- Запускается вручную (workflow_dispatch)
- Анализирует проект
- Создает task_list.json
- Создает issue с summary
- Триггерит development workflow
2. **`.github/workflows/agent-development.yml`**
- Читает задачи из task_list.json
- Использует Cursor CLI для написания кода
- Запускает тесты
- Делает коммиты
- Цикл до завершения всех задач (макс 10 итераций, 6 часов)
3. **`.github/workflows/agent-test-deploy.yml`**
- Триггерится автоматически после push
- Определяет измененные компоненты
- Запускает analyze + tests для каждого
- Готовит к деплою если тесты прошли
- Создает issue если тесты не прошли
### 🐍 Python Scripts (5 файлов)
1. **`tools/agent/config.py`** (124 строки)
- Конфигурация для каждого компонента
- Пути к файлам состояния
- Команды для тестов/линтера/сборки
2. **`tools/agent/task_manager.py`** (334 строки)
- Класс `Task` - представление задачи
- Класс `AgentState` - состояние агента
- Класс `TaskManager` - работа с задачами и состоянием
- Класс `GlobalLock` - глобальная блокировка для shared resources
3. **`tools/agent/cursor_cli_wrapper.py`** (300 строк)
- Wrapper для Cursor CLI
- Парсинг stream-json output
- Отслеживание прогресса (tool calls, files modified)
- Обработка ошибок
4. **`tools/agent/agent_orchestrator.py`** (423 строки)
- Главный оркестратор development цикла
- Чтение задач
- Выполнение через Cursor CLI
- Запуск тестов
- Создание коммитов
- Обработка ретраев
5. **`tools/agent/planning_agent.py`** (334 строки)
- Planning агент
- Анализ текущего состояния проекта
- Генерация task_list.json через Cursor CLI
- Валидация JSON
- Коммит task list
### 📝 Prompts (2 файла)
1. **`ai_docs/agent/prompts/development_prompt.md`** (~400 строк)
- Инструкции для development агента
- Code standards (Clean Architecture, yx_state)
- Примеры кода
- Testing guidelines
- DO/DON'T список
2. **`ai_docs/agent/prompts/planning_prompt.md`** (~300 строк)
- Инструкции для planning агента
- Формат task list
- Priority guidelines
- Acceptance criteria examples
- Task ordering strategy
### 📊 State Files (7 файлов)
1. **`ai_docs/agent/web_v2/task_list.json`** - задачи для web_v2
2. **`ai_docs/agent/web_v2/agent_state.json`** - состояние агента web_v2
3. **`ai_docs/agent/backend/task_list.json`** - задачи для backend
4. **`ai_docs/agent/backend/agent_state.json`** - состояние агента backend
5. **`ai_docs/agent/common/task_list.json`** - задачи для common
6. **`ai_docs/agent/common/agent_state.json`** - состояние агента common
7. **`ai_docs/agent/global_lock.json`** - глобальная блокировка
### 📚 Documentation (4 файла)
1. **`ai_docs/agent/README.md`** (~600 строк)
- Полная документация системы
- Архитектура
- Как работают агенты
- Multi-agent support
- Мониторинг и отладка
- Best practices
- Примеры использования
2. **`ai_docs/agent/CONFIGURATION.md`** (~500 строк)
- Детальная конфигурация для Forgejo
- Настройка Forgejo Runner
- Настройка secrets
- Cursor CLI setup
- Troubleshooting
- Мониторинг
- Security best practices
3. **`ai_docs/agent/FORGEJO_SETUP.md`** (~400 строк)
- Пошаговая инструкция на 15-30 минут
- Настройка Forgejo Actions
- Установка runner
- Создание secrets
- Первый запуск
- Checklist проверки
4. **`ai_docs/agent/QUICKSTART.md`** (~200 строк)
- Быстрый старт за 5 минут
- Основные команды
- Структура проекта
- Troubleshooting
- Советы
## Ключевые особенности
### ✅ Multi-Agent Support
- Параллельная работа нескольких агентов
- Каждый агент работает над своим компонентом (web_v2, backend, common)
- Per-component locking
- Global lock для shared resources (mnemo_cards_common)
### ✅ Cursor CLI Integration
- Использует Cursor CLI вместо прямых API вызовов
- Автоматическая оптимизация контекста
- Уважает .cursorrules
- Stream-json для отслеживания прогресса
### ✅ Forgejo Native
- Полностью адаптировано под Forgejo
- Используется Forgejo API для issues и workflow triggers
- Работает с Forgejo Runner
- Без зависимостей от GitHub
### ✅ Safety & Limits
- Максимум 10 итераций за запуск
- Timeout 6 часов
- Максимум 3 ретрая для задачи
- Автоматический git pull перед коммитом
- Stale lock detection (2 часа)
### ✅ Autonomous Operation
- Агенты работают полностью автономно
- Не требуют вмешательства человека
- Автоматический retry при ошибках
- Создание issues при критических ошибках
## Workflow процесс
### Planning Agent
1. **Trigger**: Вручную через UI или API
2. **Input**: tasks.md, workflow_state.md, agent_state.json
3. **Process**:
- Cursor CLI анализирует проект
- Генерирует task_list.json
4. **Output**:
- Обновленный task_list.json
- GitHub issue с summary
- Триггер development workflow
### Development Agent
1. **Trigger**: Автоматически после planning или вручную
2. **Input**: task_list.json
3. **Process**:
- Цикл по задачам (по приоритету)
- Для каждой задачи:
- Cursor CLI пишет код
- Запускаются тесты
- При успехе: коммит + next task
- При ошибке: retry до 3 раз
4. **Output**:
- Git commits с изменениями
- Обновленный agent_state.json
- Issues для failed tasks
### Test & Deploy
1. **Trigger**: Автоматически после push
2. **Input**: Git diff
3. **Process**:
- Определить измененные компоненты
- Для каждого: analyze + test
4. **Output**:
- Test reports
- Deployment (если тесты прошли)
- Issues (если тесты не прошли)
## Требования
### Обязательные
- **Forgejo**: 1.20+ с Actions
- **Forgejo Runner**: установлен и запущен
- **Cursor**: активная подписка + API key
- **Python**: 3.8+
- **Flutter**: 3.24.0+ (для web_v2)
- **Dart**: 3.0+ (для backend/common)
### Secrets
- `CURSOR_API_KEY` - API ключ от Cursor
- `FORGEJO_TOKEN` - Personal Access Token для Forgejo
## Быстрый старт
### 1. Установите Runner
```bash
wget https://code.forgejo.org/forgejo/runner/releases/download/v3.3.0/forgejo-runner-3.3.0-linux-amd64
sudo mv forgejo-runner-3.3.0-linux-amd64 /usr/local/bin/forgejo-runner
sudo chmod +x /usr/local/bin/forgejo-runner
sudo forgejo-runner register --instance https://your-forgejo.com --token TOKEN
sudo forgejo-runner daemon
```
### 2. Добавьте Secrets
Repository -> Settings -> Secrets and Variables -> Actions
- `CURSOR_API_KEY`
- `FORGEJO_TOKEN`
### 3. Запустите Planning
Actions -> AI Agent - Planning -> Run workflow -> web_v2
**Готово!** Агент начнет работу.
## Документация
- **Быстрый старт**: [QUICKSTART.md](./ai_docs/agent/QUICKSTART.md)
- **Полная настройка**: [FORGEJO_SETUP.md](./ai_docs/agent/FORGEJO_SETUP.md)
- **Конфигурация**: [CONFIGURATION.md](./ai_docs/agent/CONFIGURATION.md)
- **Документация**: [README.md](./ai_docs/agent/README.md)
## Статистика
- **Всего файлов**: 21
- **Python код**: ~1500 строк
- **Workflows**: ~500 строк
- **Prompts**: ~700 строк
- **Documentation**: ~1700 строк
- **Общий объем**: ~4400 строк кода и документации
## Возможности расширения
### В будущем можно добавить:
- [ ] Telegram уведомления о прогрессе
- [ ] Dashboard для мониторинга
- [ ] Автоматические расписания (cron triggers)
- [ ] Metrics и аналитика (сколько задач выполнено, время и т.д.)
- [ ] Интеграция с CI/CD для автодеплоя
- [ ] A/B тестирование разных AI моделей
- [ ] Система приоритетов на основе бизнес-метрик
- [ ] Автоматический rollback при критических ошибках
## Лицензия
Использует:
- **Cursor CLI**: Коммерческая лицензия Cursor
- **Forgejo**: MIT License
- **Остальной код**: Собственная разработка для проекта mnemo_cards
---
**Создано**: 2025-11-20
**Версия**: 1.0
**Powered by**: Cursor CLI + Claude Sonnet 4.5 + Forgejo Actions

96
ai_docs/README.md Normal file
View file

@ -0,0 +1,96 @@
# AI Documentation
Документация для AI агентов и разработчиков проекта mnemo_cards.
## 🤖 AI Agent 24/7 Automation System
Система автоматизированной разработки с использованием AI агентов для Forgejo.
### Быстрый старт
1. **За 5 минут**: [agent/QUICKSTART.md](./agent/QUICKSTART.md)
2. **За 30 минут**: [agent/FORGEJO_SETUP.md](./agent/FORGEJO_SETUP.md)
3. **Полная документация**: [agent/README.md](./agent/README.md)
### Что это?
AI агенты которые:
- 📋 Анализируют проект и создают задачи (Planning Agent)
- 💻 Пишут код автоматически (Development Agent)
- ✅ Тестируют и деплоят (Test & Deploy)
- 🔄 Работают 24/7 параллельно на разных компонентах
### Документация
- [**QUICKSTART.md**](./agent/QUICKSTART.md) - запуск за 5 минут
- [**FORGEJO_SETUP.md**](./agent/FORGEJO_SETUP.md) - пошаговая настройка
- [**README.md**](./agent/README.md) - полная документация
- [**CONFIGURATION.md**](./agent/CONFIGURATION.md) - детальная конфигурация
- [**AGENT_SYSTEM_SUMMARY.md**](./AGENT_SYSTEM_SUMMARY.md) - краткое описание системы
### Структура
```
ai_docs/
├── README.md (этот файл)
├── AGENT_SYSTEM_SUMMARY.md
└── agent/
├── README.md # Полная документация
├── QUICKSTART.md # Быстрый старт
├── FORGEJO_SETUP.md # Пошаговая настройка
├── CONFIGURATION.md # Детальная конфигурация
├── web_v2/ # Состояние агента web_v2
├── backend/ # Состояние агента backend
├── common/ # Состояние агента common
├── global_lock.json # Глобальная блокировка
└── prompts/ # Промпты для агентов
├── development_prompt.md
└── planning_prompt.md
```
### Workflows
```
.github/workflows/
├── agent-planning.yml # Planning Agent
├── agent-development.yml # Development Agent
└── agent-test-deploy.yml # Test & Deploy
```
### Scripts
```
tools/agent/
├── config.py # Конфигурация
├── task_manager.py # Менеджер задач
├── cursor_cli_wrapper.py # Wrapper для Cursor CLI
├── agent_orchestrator.py # Главный оркестратор
└── planning_agent.py # Planning агент
```
## 🚀 Как использовать
### 1. Настройте систему
Следуйте инструкции: [agent/FORGEJO_SETUP.md](./agent/FORGEJO_SETUP.md)
### 2. Запустите Planning Agent
```
Actions -> AI Agent - Planning -> Run workflow -> выберите компонент
```
### 3. Наблюдайте за работой
Development Agent запустится автоматически и начнет писать код.
## 📚 Дополнительно
- [Cursor CLI Docs](https://cursor.com/docs/cli/headless)
- [Forgejo Actions Docs](https://forgejo.org/docs/latest/user/actions/)
---
**Версия**: 1.0
**Дата**: 2025-11-20

View file

@ -0,0 +1,522 @@
# AI Agent System - Configuration Guide
Детальная инструкция по настройке системы AI агентов для Forgejo.
## Содержание
- [Требования](#требования)
- [Настройка Forgejo](#настройка-forgejo)
- [Настройка Secrets](#настройка-secrets)
- [Настройка Runners](#настройка-runners)
- [Cursor CLI](#cursor-cli)
- [Тестирование](#тестирование)
- [Troubleshooting](#troubleshooting)
## Требования
### Минимальные требования
- **Forgejo**: версия 1.20+ (с поддержкой Actions)
- **Forgejo Runner**: настроенный и запущенный runner
- **Cursor**: активная подписка с API доступом
- **Python**: 3.8+
- **Flutter**: 3.24.0+ (для web_v2)
- **Dart**: 3.0+ (для backend/common)
### Рекомендуемые ресурсы для runner
- **CPU**: 4+ cores
- **RAM**: 8+ GB
- **Disk**: 50+ GB свободного места
- **Network**: стабильное интернет соединение
## Настройка Forgejo
### 1. Включение Forgejo Actions
Отредактируйте `app.ini` вашего Forgejo сервера:
```ini
[actions]
ENABLED = true
DEFAULT_ACTIONS_URL = https://code.forgejo.org
```
Перезапустите Forgejo:
```bash
sudo systemctl restart forgejo
```
### 2. Установка Forgejo Runner
```bash
# Скачайте Forgejo Runner
wget https://code.forgejo.org/forgejo/runner/releases/download/v3.3.0/forgejo-runner-3.3.0-linux-amd64
# Переместите в /usr/local/bin
sudo mv forgejo-runner-3.3.0-linux-amd64 /usr/local/bin/forgejo-runner
sudo chmod +x /usr/local/bin/forgejo-runner
# Создайте директорию для runner
sudo mkdir -p /etc/forgejo-runner
cd /etc/forgejo-runner
# Создайте конфигурацию
sudo forgejo-runner create-runner-file
# Зарегистрируйте runner
# Получите registration token из Forgejo: Settings -> Actions -> Runners
sudo forgejo-runner register --no-interactive \
--instance https://your-forgejo-instance.com \
--token YOUR_REGISTRATION_TOKEN \
--name ai-agent-runner \
--labels ubuntu-latest:docker://node:16-bullseye
```
### 3. Настройка systemd service для runner
Создайте `/etc/systemd/system/forgejo-runner.service`:
```ini
[Unit]
Description=Forgejo Runner
After=network.target
[Service]
Type=simple
User=forgejo-runner
WorkingDirectory=/etc/forgejo-runner
ExecStart=/usr/local/bin/forgejo-runner daemon
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
Запустите runner:
```bash
sudo systemctl daemon-reload
sudo systemctl enable forgejo-runner
sudo systemctl start forgejo-runner
sudo systemctl status forgejo-runner
```
## Настройка Secrets
### В Forgejo Repository
1. Перейдите в ваш репозиторий
2. Settings -> Secrets and Variables -> Actions
3. Добавьте следующие secrets:
#### Обязательные Secrets
**CURSOR_API_KEY**
- Описание: API ключ от Cursor
- Как получить:
1. Откройте Cursor
2. Settings (⚙️) -> Account -> API Keys
3. Create new API key
4. Скопируйте ключ (отобразится только один раз!)
- Пример: `sk-cursor-xxx...`
**FORGEJO_TOKEN**
- Описание: Personal Access Token для Forgejo API
- Как получить:
1. Forgejo -> Settings -> Applications
2. Generate New Token
3. Выберите scopes: `repo`, `write:issue`, `write:workflow`
4. Скопируйте token
- Пример: `ghp_xxxxx...`
#### Опциональные Secrets
**CURSOR_MODEL**
- Описание: Модель AI для использования
- По умолчанию: `claude-3-5-sonnet-20241022`
- Альтернативы:
- `gpt-4-turbo-preview`
- `claude-3-opus-20240229`
**MAX_ITERATIONS**
- Описание: Максимум итераций за один запуск
- По умолчанию: `10`
- Рекомендуется: `10-20`
**MAX_RETRIES**
- Описание: Максимум попыток для задачи
- По умолчанию: `3`
- Рекомендуется: `2-5`
### Проверка Secrets
```bash
# Используйте Forgejo CLI (если установлен)
forgejo-cli secrets list --repo your-org/your-repo
# Или через curl
curl -X GET \
"https://your-forgejo.com/api/v1/repos/your-org/your-repo/actions/secrets" \
-H "Authorization: token YOUR_FORGEJO_TOKEN"
```
## Настройка Runners
### Runner Labels
Убедитесь что runner имеет правильные labels для workflows:
```yaml
# В .forgejo/workflows/agent-development.yml
runs-on: ubuntu-latest
```
Проверьте labels вашего runner:
```bash
forgejo-runner list
```
Если нужно, добавьте labels при регистрации:
```bash
sudo forgejo-runner register \
--labels ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://ubuntu:22.04
```
### Docker vs Host режим
**Docker режим (рекомендуется)**
- Изолированное окружение
- Легкая очистка
- Требует Docker
**Host режим**
- Выполнение на хосте
- Быстрее
- Нужна ручная очистка
Выбор в конфигурации runner:
```yaml
# .runner файл
labels:
- "ubuntu-latest:docker://node:16-bullseye" # Docker mode
- "ubuntu-latest:host" # Host mode
```
## Cursor CLI
### Установка на Runner
Cursor CLI устанавливается автоматически в workflows, но можно предустановить:
```bash
# На машине runner
curl https://cursor.com/install -fsS | bash
# Добавьте в PATH
echo 'export PATH="$HOME/.cursor/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Проверьте
cursor-agent --version
```
### Аутентификация Cursor CLI
Cursor CLI использует переменную окружения `CURSOR_API_KEY`:
```bash
export CURSOR_API_KEY="sk-cursor-xxx..."
cursor-agent -p "Test query"
```
В workflows это настроено автоматически через secrets.
### Лимиты и квоты
Cursor API имеет лимиты:
- **Free tier**: 500 requests/день
- **Pro tier**: 5000 requests/день
- **Team tier**: 20000 requests/день
Для 24/7 агентов рекомендуется **Pro или Team**.
## Тестирование
### 1. Тест Forgejo Runner
```bash
# Проверьте статус runner
sudo systemctl status forgejo-runner
# Проверьте логи
sudo journalctl -u forgejo-runner -f
```
### 2. Тест Secrets
Создайте тестовый workflow `.forgejo/workflows/test-secrets.yml`:
```yaml
name: Test Secrets
on:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Test CURSOR_API_KEY
run: |
if [ -z "${{ secrets.CURSOR_API_KEY }}" ]; then
echo "❌ CURSOR_API_KEY not set"
exit 1
else
echo "✅ CURSOR_API_KEY is set"
fi
- name: Test FORGEJO_TOKEN
run: |
if [ -z "${{ secrets.FORGEJO_TOKEN }}" ]; then
echo "❌ FORGEJO_TOKEN not set"
exit 1
else
echo "✅ FORGEJO_TOKEN is set"
fi
```
Запустите через Actions -> Test Secrets -> Run workflow
### 3. Тест Cursor CLI
```yaml
name: Test Cursor CLI
on:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Cursor CLI
run: |
curl https://cursor.com/install -fsS | bash
export PATH="$HOME/.cursor/bin:$PATH"
cursor-agent --version
- name: Test Cursor Agent
env:
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
run: |
export PATH="$HOME/.cursor/bin:$PATH"
cursor-agent -p "What is 2+2?"
```
### 4. Тест Planning Agent
Запустите Planning Agent через UI:
1. Actions -> AI Agent - Planning
2. Run workflow
3. Выберите component: `web_v2`
4. Run workflow
Проверьте:
- ✅ Workflow запустился
- ✅ Cursor CLI установлен
- ✅ Planning agent выполнился
- ✅ task_list.json создан
- ✅ Issue создан
- ✅ Development workflow триггернут
## Troubleshooting
### Runner не запускается
**Проблема**: `forgejo-runner daemon` fails
**Решение**:
```bash
# Проверьте логи
sudo journalctl -u forgejo-runner -n 50
# Проверьте конфигурацию
cat /etc/forgejo-runner/.runner
# Переререгистрируйте
sudo forgejo-runner register --no-interactive \
--instance https://your-forgejo.com \
--token NEW_TOKEN
```
### Workflow не запускается
**Проблема**: Workflow pending forever
**Решение**:
1. Проверьте что runner запущен и online (Settings -> Actions -> Runners)
2. Проверьте labels: `runs-on` должен совпадать с runner labels
3. Проверьте логи runner: `sudo journalctl -u forgejo-runner -f`
### Cursor CLI authentication failed
**Проблема**: `Error: Invalid API key`
**Решение**:
1. Проверьте что `CURSOR_API_KEY` secret настроен
2. Проверьте ключ в Cursor: Settings -> Account -> API Keys
3. Создайте новый ключ если старый истек
4. Обновите secret в Forgejo
### Python script import errors
**Проблема**: `ModuleNotFoundError: No module named 'config'`
**Решение**:
```yaml
# В workflow добавьте:
- name: Set PYTHONPATH
run: |
export PYTHONPATH="${{ github.workspace }}/tools/agent:$PYTHONPATH"
echo "PYTHONPATH=$PYTHONPATH" >> $GITHUB_ENV
```
### Git push failed
**Проблема**: `remote: Permission denied`
**Решение**:
1. Проверьте что `FORGEJO_TOKEN` имеет `repo` scope
2. Настройте git credentials в workflow:
```yaml
- name: Configure Git
run: |
git config --global user.name "AI Agent"
git config --global user.email "ai-agent@mnemo-cards.com"
git config --global credential.helper store
echo "https://ai-agent:${{ secrets.FORGEJO_TOKEN }}@your-forgejo.com" > ~/.git-credentials
```
### Task list JSON invalid
**Проблема**: Planning agent generates invalid JSON
**Решение**:
1. Проверьте логи planning agent
2. Валидируйте JSON вручную: `cat task_list.json | jq .`
3. Если Cursor генерирует невалидный JSON, улучшите prompt
### Disk space full on runner
**Проблема**: Runner runs out of disk space
**Решение**:
```bash
# Очистите Docker images
docker system prune -af
# Очистите старые builds
cd /etc/forgejo-runner/_work
find . -type d -mtime +7 -exec rm -rf {} +
# Настройте auto-cleanup в workflow
- name: Cleanup
if: always()
run: |
docker system prune -f
rm -rf ${{ github.workspace }}/*
```
## Мониторинг
### Forgejo Actions UI
- **Workflows**: Actions tab -> All workflows
- **Runs**: Каждый workflow run с логами
- **Runners**: Settings -> Actions -> Runners
### Логи Runner
```bash
# Real-time logs
sudo journalctl -u forgejo-runner -f
# Last 100 lines
sudo journalctl -u forgejo-runner -n 100
# Logs with errors
sudo journalctl -u forgejo-runner | grep -i error
```
### Agent State
```bash
# Web v2
cat ai_docs/agent/web_v2/agent_state.json | jq
# Backend
cat ai_docs/agent/backend/agent_state.json | jq
# Check current task
cat ai_docs/agent/web_v2/agent_state.json | jq -r '.current_task_id'
```
### Metrics
Добавьте мониторинг в workflow:
```yaml
- name: Report Metrics
if: always()
run: |
echo "Workflow: ${{ github.workflow }}"
echo "Duration: ${{ steps.agent.duration }}s"
echo "Status: ${{ job.status }}"
# Send to monitoring system (Prometheus, Grafana, etc.)
```
## Best Practices
### Security
1. ✅ Используйте отдельный Forgejo token для агентов (не ваш личный)
2. ✅ Ограничьте scopes token до минимума (`repo`, `write:issue`)
3. ✅ Ротируйте API ключи регулярно (раз в 3 месяца)
4. ✅ Не логируйте secrets в workflow outputs
5. ✅ Используйте runner в изолированной среде (Docker)
### Performance
1. ✅ Кешируйте dependencies (Flutter, Dart)
2. ✅ Используйте concurrent jobs где возможно
3. ✅ Ограничьте `MAX_ITERATIONS` разумным значением (10-15)
4. ✅ Настройте timeouts для jobs (6 часов max)
### Reliability
1. ✅ Мониторьте runner uptime
2. ✅ Настройте alerts для failed workflows
3. ✅ Регулярно проверяйте agent_state.json
4. ✅ Имейте fallback план (ручная разработка)
## Дополнительные ресурсы
- [Forgejo Actions Documentation](https://forgejo.org/docs/latest/user/actions/)
- [Cursor CLI Documentation](https://cursor.com/docs/cli/headless)
- [Project README](./README.md)
---
**Последнее обновление**: 2025-11-20
**Версия**: 1.0

View file

@ -0,0 +1,304 @@
# Быстрая настройка для Forgejo
Пошаговая инструкция для быстрого запуска AI Agent системы на Forgejo.
## Шаг 1: Настройка Forgejo Actions
### 1.1 Включите Actions в Forgejo
Отредактируйте `/etc/forgejo/app.ini`:
```ini
[actions]
ENABLED = true
DEFAULT_ACTIONS_URL = https://code.forgejo.org
```
Перезапустите:
```bash
sudo systemctl restart forgejo
```
### 1.2 Установите Forgejo Runner
```bash
# Скачайте runner
wget https://code.forgejo.org/forgejo/runner/releases/download/v3.3.0/forgejo-runner-3.3.0-linux-amd64
sudo mv forgejo-runner-3.3.0-linux-amd64 /usr/local/bin/forgejo-runner
sudo chmod +x /usr/local/bin/forgejo-runner
# Создайте директорию
sudo mkdir -p /etc/forgejo-runner
cd /etc/forgejo-runner
```
### 1.3 Получите Registration Token
1. Откройте Forgejo в браузере
2. Перейдите в ваш репозиторий
3. Settings -> Actions -> Runners
4. Нажмите "Create new Runner"
5. Скопируйте Registration Token
### 1.4 Зарегистрируйте Runner
```bash
cd /etc/forgejo-runner
sudo forgejo-runner register \
--no-interactive \
--instance https://your-forgejo.com \
--token ВАШТОКЕН \
--name ai-agent-runner \
--labels ubuntu-latest:docker://node:16-bullseye
```
### 1.5 Создайте systemd service
```bash
sudo tee /etc/systemd/system/forgejo-runner.service > /dev/null <<EOF
[Unit]
Description=Forgejo Runner
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/etc/forgejo-runner
ExecStart=/usr/local/bin/forgejo-runner daemon
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# Запустите
sudo systemctl daemon-reload
sudo systemctl enable forgejo-runner
sudo systemctl start forgejo-runner
```
### 1.6 Проверьте статус
```bash
sudo systemctl status forgejo-runner
# Должно быть: Active: active (running)
```
## Шаг 2: Настройка Cursor API
### 2.1 Получите API Key
1. Откройте Cursor
2. Settings (⚙️)
3. Account -> API Keys
4. "Create new API key"
5. Скопируйте ключ (показывается только один раз!)
Сохраните ключ, например: `sk-cursor-abc123...`
## Шаг 3: Настройка Secrets в Forgejo
### 3.1 Создайте Forgejo Token
1. Forgejo -> Settings -> Applications
2. Generate New Token
3. Token Name: `ai-agent-token`
4. Select Scopes:
- ✅ `repo` (Full control of repositories)
- ✅ `write:issue` (Create and edit issues)
- ✅ `write:workflow` (Update workflow files)
5. Generate Token
6. Скопируйте token
### 3.2 Добавьте Secrets в Repository
1. Откройте ваш репозиторий в Forgejo
2. Settings -> Secrets and Variables -> Actions
3. Добавьте два обязательных secrets:
**Secret 1: CURSOR_API_KEY**
- Name: `CURSOR_API_KEY`
- Value: `sk-cursor-abc123...` (ваш ключ из Step 2.1)
**Secret 2: FORGEJO_TOKEN**
- Name: `FORGEJO_TOKEN`
- Value: ваш token из Step 3.1
**Optional Secret 3: CURSOR_MODEL**
- Name: `CURSOR_MODEL`
- Value: `claude-3-5-sonnet-20241022`
## Шаг 4: Проверка файлов
Убедитесь что эти файлы существуют в вашем репозитории:
```bash
ls -la .github/workflows/
# Должны быть:
# - agent-planning.yml
# - agent-development.yml
# - agent-test-deploy.yml
ls -la tools/agent/
# Должны быть:
# - config.py
# - task_manager.py
# - cursor_cli_wrapper.py
# - agent_orchestrator.py
# - planning_agent.py
ls -la ai_docs/agent/
# Должны быть:
# - web_v2/task_list.json
# - web_v2/agent_state.json
# - backend/task_list.json
# - backend/agent_state.json
# - global_lock.json
# - prompts/development_prompt.md
# - prompts/planning_prompt.md
```
## Шаг 5: Первый запуск
### 5.1 Запустите Planning Agent
1. В Forgejo откройте репозиторий
2. Перейдите на вкладку **Actions**
3. Слева выберите **"AI Agent - Planning"**
4. Справа нажмите **"Run workflow"**
5. Выберите:
- Branch: `master`
- Component: `web_v2`
6. Нажмите **"Run workflow"**
### 5.2 Наблюдайте за выполнением
1. Вернитесь на вкладку Actions
2. Увидите новый workflow run
3. Кликните на него
4. Смотрите логи в реальном времени
### 5.3 Проверьте результат
После завершения Planning Agent:
```bash
# Клонируйте репозиторий (если еще не клонировали)
git clone https://your-forgejo.com/user/repo.git
cd repo
# Проверьте task list
cat ai_docs/agent/web_v2/task_list.json | jq
# Должен быть JSON с задачами
```
Также:
- Должен появиться новый Issue с summary
- Должен автоматически запуститься Development Agent
### 5.4 Наблюдайте Development Agent
Development Agent запустится автоматически:
1. Actions -> "AI Agent - Development"
2. Найдите активный workflow run
3. Смотрите как агент:
- Читает задачи
- Пишет код
- Запускает тесты
- Делает коммиты
## Проверка что все работает
### ✅ Checklist
- [ ] Forgejo Actions включены (`app.ini`)
- [ ] Forgejo Runner запущен и online
- [ ] Cursor API Key получен
- [ ] Forgejo Token создан
- [ ] Оба secrets добавлены в repository
- [ ] Workflow файлы существуют в `.github/workflows/`
- [ ] Python скрипты существуют в `tools/agent/`
- [ ] State файлы существуют в `ai_docs/agent/*/`
- [ ] Planning Agent успешно запущен
- [ ] Task list JSON создан
- [ ] Development Agent запущен автоматически
### 🐛 Если что-то не работает
**Runner offline:**
```bash
sudo systemctl status forgejo-runner
sudo journalctl -u forgejo-runner -f
```
**Workflow не запускается:**
- Проверьте что runner online (Settings -> Actions -> Runners)
- Проверьте labels runner совпадают с `runs-on: ubuntu-latest`
**Secrets не работают:**
- Перепроверьте названия: `CURSOR_API_KEY` и `FORGEJO_TOKEN`
- Убедитесь что ввели правильные значения
- Попробуйте пересоздать secrets
**Python ошибки:**
- Проверьте логи workflow
- Убедитесь что все файлы в `tools/agent/` существуют
- Проверьте синтаксис Python скриптов
## Дальнейшие шаги
После успешного первого запуска:
1. **Мониторинг**: Следите за прогрессом в Actions tab
2. **Ревью**: Проверяйте коммиты от агента
3. **Настройка**: Отредактируйте `tasks.md` для новых задач
4. **Масштабирование**: Запустите агентов для других компонентов (backend, common)
## Полезные команды
```bash
# Проверка runner
sudo systemctl status forgejo-runner
# Логи runner
sudo journalctl -u forgejo-runner -f
# Проверка agent state
cat ai_docs/agent/web_v2/agent_state.json | jq
# Проверка task list
cat ai_docs/agent/web_v2/task_list.json | jq
# Триггер planning через API
curl -X POST \
"https://your-forgejo.com/api/v1/repos/user/repo/actions/workflows/agent-planning.yml/dispatches" \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ref":"master","inputs":{"component":"web_v2"}}'
# Список workflow runs
curl -X GET \
"https://your-forgejo.com/api/v1/repos/user/repo/actions/runs" \
-H "Authorization: token $FORGEJO_TOKEN"
```
## Поддержка
Если возникли проблемы:
1. Проверьте [CONFIGURATION.md](./CONFIGURATION.md) для детальной настройки
2. Посмотрите [README.md](./README.md) для полной документации
3. Проверьте логи: `sudo journalctl -u forgejo-runner -f`
4. Создайте issue в репозитории
---
**Время на настройку**: 15-30 минут
**Сложность**: Средняя
**Требует**: Root доступ к серверу Forgejo

186
ai_docs/agent/QUICKSTART.md Normal file
View file

@ -0,0 +1,186 @@
# AI Agent System - Quick Start
Быстрый старт для запуска AI агентов на Forgejo.
## 🚀 За 5 минут
### 1. Установите Forgejo Runner
```bash
wget https://code.forgejo.org/forgejo/runner/releases/download/v3.3.0/forgejo-runner-3.3.0-linux-amd64
sudo mv forgejo-runner-3.3.0-linux-amd64 /usr/local/bin/forgejo-runner
sudo chmod +x /usr/local/bin/forgejo-runner
# Получите token: Repository -> Settings -> Actions -> Runners -> Create
sudo forgejo-runner register --instance https://your-forgejo.com --token YOUR_TOKEN
# Запустите
sudo forgejo-runner daemon
```
### 2. Добавьте Secrets
**Repository -> Settings -> Secrets and Variables -> Actions**
1. `CURSOR_API_KEY` = ваш Cursor API key (Cursor -> Settings -> Account -> API Keys)
2. `FORGEJO_TOKEN` = ваш Forgejo Personal Access Token (Settings -> Applications)
### 3. Запустите Planning Agent
**Actions -> AI Agent - Planning -> Run workflow**
Выберите component: `web_v2`
**Готово!** 🎉
Агент:
- Проанализирует проект
- Создаст task list
- Автоматически запустит Development Agent
- Начнет писать код
---
## 📚 Подробная документация
- [Полная настройка для Forgejo](./FORGEJO_SETUP.md) - пошаговая инструкция 15-30 мин
- [Детальная конфигурация](./CONFIGURATION.md) - все настройки и troubleshooting
- [README](./README.md) - архитектура и как это работает
## 🎯 Основные команды
### Проверка статуса runner
```bash
sudo systemctl status forgejo-runner
```
### Просмотр agent state
```bash
# Web v2
cat ai_docs/agent/web_v2/agent_state.json | jq
# Backend
cat ai_docs/agent/backend/agent_state.json | jq
```
### Триггер через API
```bash
curl -X POST \
"https://your-forgejo.com/api/v1/repos/user/repo/actions/workflows/agent-planning.yml/dispatches" \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ref":"master","inputs":{"component":"web_v2"}}'
```
## 🔧 Структура проекта
```
ai_docs/agent/
├── README.md # Полная документация
├── QUICKSTART.md # Этот файл
├── FORGEJO_SETUP.md # Пошаговая настройка
├── CONFIGURATION.md # Детальная конфигурация
├── web_v2/
│ ├── task_list.json # Список задач
│ └── agent_state.json # Состояние агента
├── backend/
│ ├── task_list.json
│ └── agent_state.json
├── common/
│ ├── task_list.json
│ └── agent_state.json
├── global_lock.json # Глобальная блокировка
└── prompts/
├── development_prompt.md # Инструкции для dev агента
└── planning_prompt.md # Инструкции для planning агента
.github/workflows/
├── agent-planning.yml # Planning workflow
├── agent-development.yml # Development workflow
└── agent-test-deploy.yml # Test & Deploy workflow
tools/agent/
├── config.py # Конфигурация
├── task_manager.py # Менеджер задач
├── cursor_cli_wrapper.py # Wrapper для Cursor CLI
├── agent_orchestrator.py # Главный оркестратор
└── planning_agent.py # Planning агент
```
## ⚡ Работа с несколькими компонентами
Запустите агентов параллельно:
```bash
# Planning для web_v2
Actions -> AI Agent - Planning -> web_v2
# Planning для backend (одновременно)
Actions -> AI Agent - Planning -> backend
```
Агенты работают независимо, но координируются через `global_lock.json` когда нужно.
## 📊 Мониторинг
### В Forgejo UI
- **Actions tab** - все workflow runs
- **Issues** - автоматические отчеты от агентов
### В командной строке
```bash
# Логи runner
sudo journalctl -u forgejo-runner -f
# Текущая задача
cat ai_docs/agent/web_v2/agent_state.json | jq -r '.current_task_id'
# Завершенные задачи
cat ai_docs/agent/web_v2/agent_state.json | jq -r '.completed_tasks[]'
```
## 🛠️ Troubleshooting
**Runner не online?**
```bash
sudo systemctl restart forgejo-runner
sudo journalctl -u forgejo-runner -n 50
```
**Workflow не запускается?**
- Проверьте что runner online: Settings -> Actions -> Runners
- Проверьте labels: должен быть `ubuntu-latest`
**Secrets не работают?**
- Проверьте названия: точно `CURSOR_API_KEY` и `FORGEJO_TOKEN`
- Перепроверьте значения
- Пересоздайте если нужно
**Подробнее**: [CONFIGURATION.md](./CONFIGURATION.md#troubleshooting)
## 💡 Советы
1. **Начните с web_v2** - самый активный компонент
2. **Проверяйте коммиты** - агент делает коммиты с описанием
3. **Ревьюйте changes** - агент может ошибаться
4. **Обновляйте tasks.md** - для новых задач
5. **Мониторьте ресурсы** - агент потребляет CPU/RAM
## 🎓 Дальше
После первого успешного запуска:
1. Почитайте [README.md](./README.md) чтобы понять как все работает
2. Настройте дополнительные параметры в [CONFIGURATION.md](./CONFIGURATION.md)
3. Запустите агентов для других компонентов (backend, common)
4. Настройте автоматические расписания (cron)
---
**Нужна помощь?** Смотрите [полную документацию](./README.md) или создайте issue.

434
ai_docs/agent/README.md Normal file
View file

@ -0,0 +1,434 @@
# AI Agent 24/7 Automation System
Автоматизированная система разработки с использованием AI агентов, которые работают круглосуточно над проектом mnemo_cards.
## Обзор
Система состоит из трех типов агентов:
1. **Planning Agent** - анализирует проект и создает список задач
2. **Development Agent** - читает задачи, пишет код, делает коммиты
3. **Test & Deploy** - тестирует изменения и деплоит на staging
## Архитектура
```
┌─────────────────────────────────────────────────────────────┐
│ GitHub/Forgejo Actions │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Planning │───▶│ Development │───▶│ Test/Deploy │ │
│ │ Agent │ │ Agent │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Cursor CLI (AI Provider) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
┌────────────────────────┐
│ Project Repository │
│ - mnemo_cards_web_v2 │
│ - mnemo_cards_backend │
│ - mnemo_cards_common │
└────────────────────────┘
```
## Компоненты
### 1. Task Management
Файлы состояния для каждого компонента:
```
ai_docs/agent/
├── web_v2/
│ ├── task_list.json # Список задач
│ └── agent_state.json # Состояние агента
├── backend/
│ ├── task_list.json
│ └── agent_state.json
├── common/
│ ├── task_list.json
│ └── agent_state.json
└── global_lock.json # Глобальная блокировка
```
### 2. Python Scripts
Оркестрация и управление агентами:
```
tools/agent/
├── config.py # Конфигурация
├── task_manager.py # Менеджер задач
├── cursor_cli_wrapper.py # Wrapper для Cursor CLI
├── agent_orchestrator.py # Главный оркестратор (development)
└── planning_agent.py # Planning агент
```
### 3. Prompts
Инструкции для агентов:
```
ai_docs/agent/prompts/
├── development_prompt.md # Инструкции для development агента
└── planning_prompt.md # Инструкции для planning агента
```
### 4. GitHub Actions Workflows
```
.github/workflows/
├── agent-planning.yml # Planning workflow
├── agent-development.yml # Development workflow
└── agent-test-deploy.yml # Test & Deploy workflow
```
## Быстрый старт
### 1. Настройка Forgejo Runner
```bash
# Установка Forgejo Runner
wget https://code.forgejo.org/forgejo/runner/releases/download/v3.3.0/forgejo-runner-3.3.0-linux-amd64
sudo mv forgejo-runner-3.3.0-linux-amd64 /usr/local/bin/forgejo-runner
sudo chmod +x /usr/local/bin/forgejo-runner
# Регистрация runner
# Получите registration token из Forgejo: Settings -> Actions -> Runners
sudo forgejo-runner register --no-interactive \
--instance https://your-forgejo-instance.com \
--token YOUR_REGISTRATION_TOKEN \
--name ai-agent-runner \
--labels ubuntu-latest:docker://node:16-bullseye
# Запуск runner (или через systemd)
sudo forgejo-runner daemon
```
### 2. Настройка Cursor CLI
```bash
# Установка Cursor CLI (на машине runner или автоматически в workflows)
curl https://cursor.com/install -fsS | bash
# Получение API ключа
# 1. Откройте Cursor
# 2. Settings -> Account -> API Keys
# 3. Create new API key
```
### 3. Настройка Secrets в Forgejo
Добавьте следующие secrets в настройках репозитория:
**Repository Settings -> Secrets and Variables -> Actions**
- `CURSOR_API_KEY` - API ключ от Cursor (обязательно)
- `FORGEJO_TOKEN` - Personal Access Token для Forgejo API (обязательно, scopes: repo, write:issue, write:workflow)
- `CURSOR_MODEL` - Модель AI (опционально, по умолчанию claude-3-5-sonnet-20241022)
- `MAX_ITERATIONS` - Максимум итераций (опционально, по умолчанию 10)
- `MAX_RETRIES` - Максимум попыток (опционально, по умолчанию 3)
### 3. Запуск Planning Agent
1. Перейдите в **Actions** в вашем репозитории
2. Выберите workflow **"AI Agent - Planning"**
3. Нажмите **"Run workflow"**
4. Выберите компонент (web_v2, backend, или common)
5. Нажмите **"Run workflow"**
Planning agent:
- Проанализирует текущее состояние проекта
- Создаст список задач в `ai_docs/agent/{component}/task_list.json`
- Создаст issue с summary
- Автоматически запустит Development Agent
### 4. Проверка настройки
Создайте тестовый workflow или проверьте:
```bash
# Проверьте что runner запущен
sudo systemctl status forgejo-runner
# Проверьте secrets через Forgejo UI
# Repository -> Settings -> Secrets and Variables -> Actions
# Проверьте что .github/workflows/ содержит файлы
ls -la .github/workflows/
```
### 5. Запуск Development Agent
Development agent запускается автоматически после Planning Agent, или можно запустить вручную:
1. Actions -> **"AI Agent - Development"**
2. Run workflow -> выбрать компонент
3. Agent начнет работу над задачами
**Примечание**: Убедитесь что Forgejo Runner запущен и online перед запуском workflows!
## Как это работает
### Planning Agent Workflow
1. **Анализ**: Читает `tasks.md`, `workflow_state.md`, текущий `task_list.json`
2. **Генерация**: Использует Cursor CLI для создания нового task list
3. **Валидация**: Проверяет корректность JSON и структуры задач
4. **Коммит**: Сохраняет task_list.json в репозиторий
5. **Запуск**: Триггерит Development Agent
### Development Agent Workflow
1. **Инициализация**: Загружает task list и agent state
2. **Выбор задачи**: Берет следующую pending задачу по приоритету
3. **Блокировка**: Проверяет, нужна ли глобальная блокировка
4. **Выполнение**:
- Использует Cursor CLI для написания кода
- Запускает линтер
- Запускает тесты
- Делает коммит с описанием изменений
5. **Обновление**: Обновляет статус задачи и agent state
6. **Повтор**: Берет следующую задачу или завершается
### Test & Deploy Workflow
1. **Триггер**: Автоматически запускается после коммитов
2. **Обнаружение**: Определяет, какие компоненты изменились
3. **Тестирование**:
- Запускает flutter/dart analyze
- Запускает тесты
- Собирает coverage
4. **Результат**:
- ✅ Если тесты прошли - готово к деплою
- ❌ Если тесты не прошли - создает issue
## Multi-Agent Support
Система поддерживает параллельную работу нескольких агентов:
```bash
# Агент 1: работает над web_v2
Actions -> Planning -> web_v2
└─> Development -> web_v2
# Агент 2: одновременно работает над backend
Actions -> Planning -> backend
└─> Development -> backend
```
### Координация агентов
- **Per-component locking**: Каждый агент блокирует только свой компонент
- **Global lock**: Если задача модифицирует `mnemo_cards_common`, берется глобальная блокировка
- **Conflict resolution**: Перед коммитом делается git pull --rebase
## Мониторинг и отладка
### Просмотр состояния агента
```bash
# Web v2
cat ai_docs/agent/web_v2/agent_state.json
# Backend
cat ai_docs/agent/backend/agent_state.json
```
### Просмотр текущих задач
```bash
# Web v2
cat ai_docs/agent/web_v2/task_list.json
# Backend
cat ai_docs/agent/backend/task_list.json
```
### Логи workflow
1. Actions -> выберите workflow run
2. Посмотрите логи каждого step
3. В summary будет краткий отчет
### Ручной запуск на локальной машине
```bash
# Установите переменные окружения
export CURSOR_API_KEY="your-key-here"
export PROJECT_ROOT="/path/to/mnemo_cards"
# Planning agent
python tools/agent/planning_agent.py web_v2
# Development agent
python tools/agent/agent_orchestrator.py web_v2
```
## Ограничения и safety
### Автоматические ограничения
- **Max iterations**: 10 итераций за один запуск
- **Timeout**: 6 часов на workflow run
- **Max retries**: 3 попытки на задачу
- **Stale lock**: Блокировка считается устаревшей через 2 часа
### Что агенты НЕ могут делать
❌ Удалять .git директорию
❌ Force push
❌ Модифицировать production secrets
❌ Деплоить на production (только staging)
❌ Удалять файлы без причины
### Безопасность
- Все коммиты проверяются через Test workflow
- Агенты не имеют доступа к production secrets
- Изменения можно ревьюить через Git history
- Можно откатить любой коммит
## Troubleshooting
### Агент не запускается
1. Проверьте, что `CURSOR_API_KEY` настроен в Secrets
2. Проверьте, что Cursor CLI установлен (в логах workflow)
3. Проверьте, что task_list.json существует и валиден
### Тесты не проходят
1. Агент попробует исправить 3 раза
2. После 3 попыток создается issue
3. Агент переходит к следующей задаче
### Агент "завис"
1. Agent state показывает "in_progress" более 2 часов
2. Запустите новый workflow - он сбросит состояние
3. Или вручную отредактируйте agent_state.json
### Конфликты коммитов
1. Агент делает `git pull --rebase` перед коммитом
2. Если конфликт - задача помечается skipped
3. Planning agent переназначит задачу позже
## Best Practices
### Для человека-разработчика
1. ✅ Обновляйте `tasks.md` с новыми фичами
2. ✅ Ревьюите коммиты от агентов
3. ✅ Запускайте Planning Agent раз в неделю
4. ❌ Не редактируйте task_list.json вручную (используйте tasks.md)
5. ❌ Не коммитьте в те же файлы, над которыми работает агент
### Для Planning Agent
- Создавайте задачи на 2-8 часов работы
- Разбивайте большие фичи на подзадачи
- Указывайте четкие acceptance criteria
- Правильно выставляйте dependencies
### Для Development Agent
- Всегда пишите тесты
- Следуйте существующим паттернам кода
- Не оставляйте TODOs
- Делайте атомарные коммиты
## Примеры использования
### Сценарий 1: Добавление новой фичи
1. Добавьте описание фичи в `mnemo_cards_web_v2/tasks.md`
2. Запустите Planning Agent для web_v2
3. Planning Agent создаст задачи
4. Development Agent автоматически начнет работу
5. Мониторьте прогресс в Actions
6. Ревьюйте коммиты от агента
### Сценарий 2: Исправление багов
1. Создайте issue с описанием бага
2. Запустите Planning Agent
3. Planning Agent добавит задачу в task list
4. Development Agent исправит баг
5. Test workflow проверит, что баг исправлен
### Сценарий 3: Рефакторинг
1. Опишите план рефакторинга в tasks.md
2. Запустите Planning Agent
3. Agent создаст задачи для рефакторинга
4. Development Agent выполнит рефакторинг поэтапно
5. Все тесты должны продолжать проходить
## Особенности Forgejo
### Отличия от GitHub Actions
1. **API endpoints**: Используется Forgejo API вместо GitHub API
2. **Actions**: Некоторые GitHub Actions могут не работать, используются альтернативы
3. **Secrets**: Настраиваются через Forgejo UI (Settings -> Secrets and Variables -> Actions)
4. **Runner**: Требует установки и настройки Forgejo Runner
5. **Workflows**: Синтаксис совместим, но используется `.github/workflows/` (не `.forgejo/workflows/`)
### Forgejo API для триггера workflows
```bash
# Trigger Planning Agent
curl -X POST \
"https://your-forgejo.com/api/v1/repos/user/repo/actions/workflows/agent-planning.yml/dispatches" \
-H "Authorization: token YOUR_FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ref":"master","inputs":{"component":"web_v2"}}'
# Trigger Development Agent
curl -X POST \
"https://your-forgejo.com/api/v1/repos/user/repo/actions/workflows/agent-development.yml/dispatches" \
-H "Authorization: token YOUR_FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ref":"master","inputs":{"component":"web_v2"}}'
```
### Создание Issues через API
```bash
# Create issue
curl -X POST \
"https://your-forgejo.com/api/v1/repos/user/repo/issues" \
-H "Authorization: token YOUR_FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Issue title","body":"Issue body","labels":["ai-agent"]}'
```
## Дополнительная информация
- [Конфигурация](./CONFIGURATION.md) - детальная настройка системы для Forgejo
- [Forgejo Actions Docs](https://forgejo.org/docs/latest/user/actions/) - документация Forgejo Actions
- [Cursor CLI Docs](https://cursor.com/docs/cli/headless) - документация Cursor CLI
- [Development Prompt](./prompts/development_prompt.md) - инструкции для разработки
- [Planning Prompt](./prompts/planning_prompt.md) - инструкции для планирования
## Контакты и поддержка
При проблемах:
1. Проверьте логи в Actions
2. Посмотрите agent_state.json
3. Создайте issue с тегом `ai-agent`
---
**Версия:** 1.0
**Дата:** 2025-11-20
**Powered by:** Cursor CLI + Claude Sonnet 4.5

View file

@ -0,0 +1,15 @@
{
"component": "backend",
"current_task_id": null,
"iteration_count": 0,
"max_iterations": 10,
"started_at": null,
"last_commit": null,
"retry_count": 0,
"max_retries": 3,
"status": "idle",
"errors": [],
"completed_tasks": [],
"skipped_tasks": []
}

View file

@ -0,0 +1,9 @@
{
"project": "mnemo_cards_backend",
"component": "backend",
"version": "1.0",
"generated_at": null,
"generated_by": "planning_agent",
"tasks": []
}

View file

@ -0,0 +1,15 @@
{
"component": "common",
"current_task_id": null,
"iteration_count": 0,
"max_iterations": 10,
"started_at": null,
"last_commit": null,
"retry_count": 0,
"max_retries": 3,
"status": "idle",
"errors": [],
"completed_tasks": [],
"skipped_tasks": []
}

View file

@ -0,0 +1,9 @@
{
"project": "mnemo_cards_common",
"component": "common",
"version": "1.0",
"generated_at": null,
"generated_by": "planning_agent",
"tasks": []
}

View file

@ -0,0 +1,7 @@
{
"locked": false,
"locked_by": null,
"locked_at": null,
"reason": null
}

View file

@ -0,0 +1,271 @@
# AI Agent Development Instructions
You are an autonomous AI software engineer working on the mnemo_cards project. Your role is to implement features, fix bugs, and improve code quality according to the assigned tasks.
## Project Context
This is a language learning application consisting of multiple components:
- **mnemo_cards_web_v2**: Flutter web frontend (main user interface)
- **mnemo_cards_backend**: Dart backend server (API, data storage)
- **mnemo_cards_common**: Shared code between frontend and backend
## Your Responsibilities
1. **Read and understand the task** - Analyze the requirements and acceptance criteria
2. **Implement the solution** - Write clean, maintainable code following project conventions
3. **Write comprehensive tests** - Ensure all new code has unit tests
4. **Verify your work** - Make sure tests pass and linting is clean
## Code Standards
### Architecture
- Follow **Clean Architecture** principles
- Use **yx_state** and **yx_scope** for state management (web_v2)
- Separate concerns: domain, data, presentation layers
- Keep business logic independent of frameworks
### Dart/Flutter Conventions
- Use descriptive names for classes, methods, and variables
- Follow Dart style guide (effective dart)
- Prefer composition over inheritance
- Use const constructors where possible
- Add proper documentation comments (///)
### State Management (web_v2)
```dart
// Use yx_state for reactive state
class MyStateManager extends YxStateManager {
final _counter = YxState<int>(0);
int get counter => _counter.value;
void increment() {
_counter.value++;
}
}
// Use yx_scope for dependency injection
class MyModule extends YxModule {
@override
void configure() {
bind<MyService>().toSingleton((scope) => MyService());
}
}
```
### Testing Requirements
- **Unit tests** for all services, managers, and utilities
- **Widget tests** for UI components (web_v2)
- **Integration tests** for API endpoints (backend)
- Aim for **>80% code coverage**
- Test happy path AND error cases
### File Organization
```
lib/
├── domain/ # Business logic, entities, interfaces
├── data/ # Data sources, repositories, DTOs
├── presentation/ # UI, pages, widgets (web_v2)
├── di/ # Dependency injection modules
└── utils/ # Utilities and helpers
```
## Task Execution Process
### 1. Analysis Phase
- Read the task description carefully
- Understand acceptance criteria
- Identify files that need to be created/modified
- Check for dependencies on other tasks
### 2. Implementation Phase
- Create/modify files according to requirements
- Follow existing code patterns in the project
- Use proper error handling
- Add logging where appropriate
### 3. Testing Phase
- Write unit tests for all new code
- Run existing tests to ensure nothing broke
- Fix any test failures
- Ensure linter passes
### 4. Verification Phase
- Review your changes
- Check that all acceptance criteria are met
- Ensure code is production-ready (no TODOs, no placeholders)
## Important Guidelines
### DO ✅
- Follow the existing code style and patterns
- Write comprehensive tests
- Handle errors gracefully
- Add meaningful comments for complex logic
- Update documentation if needed
- Check that all acceptance criteria are satisfied
- Make atomic, focused commits
### DON'T ❌
- Leave TODOs or placeholder code
- Skip writing tests
- Modify unrelated files
- Break existing functionality
- Ignore linter warnings
- Copy code without understanding it
- Make changes outside the task scope
## API v2 Guidelines (Backend/Frontend)
When working with API v2:
- Use `/api/v2/` endpoints
- Follow REST conventions
- Use proper HTTP status codes
- Include error messages in responses
- Add request/response DTOs in mnemo_cards_common
- Document endpoints in OpenAPI spec (open_api.yaml)
### Backend (Dart Shelf)
```dart
class MyApiV2 {
Router get router {
final router = Router();
router.get('/api/v2/resource', _getResource);
router.post('/api/v2/resource', _createResource);
return router;
}
Future<Response> _getResource(Request request) async {
try {
// Implementation
return Response.ok(json.encode(result));
} catch (e) {
return Response.internalServerError(
body: json.encode({'error': e.toString()})
);
}
}
}
```
### Frontend (HttpRepositoryV2)
```dart
class HttpRepositoryV2 {
Future<ResourceResponse> getResource(String id) async {
final response = await _client.get(
Uri.parse('${_baseUrl}/api/v2/resource/$id'),
headers: await _getHeaders(),
);
if (response.statusCode == 200) {
return ResourceResponse.fromJson(
json.decode(response.body)
);
} else {
throw ApiException(response.statusCode, response.body);
}
}
}
```
## Testing Examples
### Unit Test (Dart)
```dart
void main() {
group('MyService', () {
late MyService service;
setUp(() {
service = MyService();
});
test('should return correct result', () {
// Arrange
final input = 'test';
// Act
final result = service.process(input);
// Assert
expect(result, equals('expected'));
});
test('should handle error case', () {
// Assert
expect(
() => service.process(null),
throwsA(isA<ArgumentError>()),
);
});
});
}
```
### Widget Test (Flutter)
```dart
void main() {
testWidgets('MyWidget displays correctly', (tester) async {
// Build widget
await tester.pumpWidget(
MaterialApp(home: MyWidget())
);
// Verify
expect(find.text('Hello'), findsOneWidget);
expect(find.byType(ElevatedButton), findsOneWidget);
});
}
```
## Error Handling
Always handle errors gracefully:
```dart
try {
final result = await service.fetchData();
return Success(result);
} on ApiException catch (e) {
return Failure('API error: ${e.message}');
} on NetworkException catch (e) {
return Failure('Network error: ${e.message}');
} catch (e) {
return Failure('Unexpected error: $e');
}
```
## Git Workflow
Your commits will be automatically created. Make sure your changes are:
- **Atomic**: One logical change per commit
- **Complete**: All files needed for the feature
- **Tested**: All tests pass
- **Clean**: Linter passes
## Reference Materials
You can reference these files for context:
- `project_config.md` - Project overview and setup
- `workflow_state.md` - Current development state
- `tasks.md` - Human-readable task list
- Existing code in the repository
## Success Criteria
A task is only complete when:
1. ✅ All acceptance criteria are met
2. ✅ All new code has unit tests
3. ✅ All tests pass (old and new)
4. ✅ Linter passes with no warnings
5. ✅ Code is production-ready (no TODOs)
6. ✅ Changes are committed and pushed
---
**Remember**: You are an autonomous agent. Make decisions confidently, but always prioritize code quality and test coverage. If you're unsure about something, check existing code patterns in the repository for guidance.
Good luck! 🚀

View file

@ -0,0 +1,277 @@
# AI Agent Planning Instructions
You are an autonomous AI planning agent for the mnemo_cards project. Your role is to analyze the project state, review existing tasks, and create a prioritized task list for development agents.
## Project Context
This is a language learning application with multiple components:
- **mnemo_cards_web_v2**: Flutter web frontend
- **mnemo_cards_backend**: Dart backend server
- **mnemo_cards_common**: Shared common package
## Your Responsibilities
1. **Analyze project state** - Review current code, recent commits, existing tasks
2. **Identify priorities** - Determine what needs to be done next
3. **Create task list** - Generate detailed, actionable tasks in JSON format
4. **Set dependencies** - Ensure tasks are ordered correctly
## Input Sources
Review these files to understand current state:
- `tasks.md` - Human-defined tasks and priorities
- `workflow_state.md` - Current development state and progress
- `ai_docs/agent/{component}/task_list.json` - Current task list
- `ai_docs/agent/{component}/agent_state.json` - Agent execution state
- Recent commits - What has been completed recently
- Open issues - Known problems and feature requests
## Task Generation Guidelines
### Task Structure
Each task should have:
```json
{
"id": "TASK-XXX",
"title": "Short descriptive title",
"priority": "high|medium|low",
"status": "pending",
"estimated_hours": 4.0,
"description": "Detailed description of what needs to be done",
"acceptance_criteria": [
"Specific, testable criterion 1",
"Specific, testable criterion 2",
"Comprehensive test coverage (N+ tests)"
],
"dependencies": ["TASK-YYY", "backend:TASK-ZZZ"],
"files_to_modify": [
"path/to/file1.dart",
"path/to/file2.dart"
],
"component": "web_v2|backend|common"
}
```
### Task Sizing
- **Small tasks** (2-4 hours): Single feature or bug fix
- **Medium tasks** (4-8 hours): Feature with multiple files
- **Large tasks** (8+ hours): Break into smaller subtasks
### Priority Guidelines
**HIGH Priority:**
- Critical bugs affecting users
- Security vulnerabilities
- Blocking other development work
- Core features needed for launch
- API endpoints needed by frontend
**MEDIUM Priority:**
- Nice-to-have features
- Performance improvements
- Code refactoring
- UI/UX enhancements
- Non-critical bug fixes
**LOW Priority:**
- Code cleanup
- Documentation updates
- Minor optimizations
- Optional features
- Technical debt
### Acceptance Criteria
Make acceptance criteria:
- **Specific**: Clearly defined, not ambiguous
- **Measurable**: Can be objectively verified
- **Testable**: Can write a test for it
- **Complete**: Covers all aspects of the task
Examples:
- ✅ "All 5 subscription endpoints return 200 status for valid requests"
- ✅ "15+ unit tests pass with >80% coverage"
- ✅ "Linter passes with zero warnings"
- ❌ "Implement subscription feature" (too vague)
- ❌ "Make it work" (not measurable)
## Task Ordering Strategy
### Dependency-First Approach
1. **Foundation first**: Common models and DTOs
2. **Backend before Frontend**: APIs before UI
3. **Service before UI**: Business logic before presentation
4. **Tests alongside code**: Not as separate tasks
### Example Order:
```
Phase 1: Backend Foundation
- TASK-001: Create DTOs in mnemo_cards_common
- TASK-002: Implement backend API endpoints
- TASK-003: Write API integration tests
Phase 2: Frontend Integration
- TASK-004: Update HttpRepository with new methods
- TASK-005: Create service layer
- TASK-006: Create state managers
Phase 3: UI Implementation
- TASK-007: Create UI components
- TASK-008: Create pages
- TASK-009: Write widget tests
Phase 4: Quality & Polish
- TASK-010: Fix linter issues
- TASK-011: Increase test coverage
- TASK-012: Performance optimization
```
## Cross-Component Dependencies
When a task in one component depends on another:
```json
{
"id": "WEB-005",
"dependencies": ["backend:API-002", "common:MODEL-001"],
"description": "Cannot start until backend API is ready"
}
```
## Task List Generation Process
### 1. Analysis Phase
- Read `tasks.md` for human-defined priorities
- Check `workflow_state.md` for current focus
- Review recent commits to see what's been done
- Check agent state to see completed tasks
- Identify gaps and blockers
### 2. Categorization Phase
- Group related tasks together
- Identify dependencies between tasks
- Determine component ownership
- Estimate effort for each task
### 3. Prioritization Phase
- Apply priority guidelines
- Consider business value
- Factor in dependencies
- Balance quick wins with long-term goals
### 4. Generation Phase
- Create JSON task list
- Add detailed descriptions
- Define clear acceptance criteria
- Specify files to modify
- Set dependencies
## Output Format
Generate `task_list.json` for each component:
```json
{
"project": "mnemo_cards_web_v2",
"component": "web_v2",
"version": "1.0",
"generated_at": "2025-11-20T10:00:00Z",
"generated_by": "planning_agent",
"tasks": [
{
"id": "WEB-001",
"title": "Implement Subscription Service",
"priority": "high",
"status": "pending",
"estimated_hours": 6,
"description": "Create SubscriptionService to handle subscription operations using HttpRepositoryV2. Include methods for fetching plans, purchasing, checking status, and cancelling subscriptions.",
"acceptance_criteria": [
"SubscriptionService created with all CRUD methods",
"Service uses HttpRepositoryV2 for API calls",
"Proper error handling for all edge cases",
"10+ unit tests pass with >80% coverage",
"Mock tests don't make real API calls"
],
"dependencies": ["backend:API-007"],
"files_to_modify": [
"mnemo_cards_web_v2/lib/domain/services/subscription_service.dart",
"mnemo_cards_web_v2/test/domain/services/subscription_service_test.dart"
],
"component": "web_v2"
}
]
}
```
## Guidelines for Different Components
### mnemo_cards_web_v2 (Flutter Web)
Focus on:
- API integration (HttpRepositoryV2)
- State management (yx_state, yx_scope)
- UI/UX implementation
- Widget tests
- Responsive design
### mnemo_cards_backend (Dart Server)
Focus on:
- API endpoints (Shelf Router)
- Business logic
- Data persistence (Isar)
- Integration tests
- Security
### mnemo_cards_common (Shared Package)
Focus on:
- DTOs and models
- Shared utilities
- Validation logic
- Serialization
- Documentation
## Quality Checks
Before finalizing task list:
1. ✅ All tasks have unique IDs
2. ✅ Dependencies are valid (tasks exist)
3. ✅ Priorities are balanced (not all high)
4. ✅ Estimates are reasonable (2-8 hours)
5. ✅ Acceptance criteria are specific
6. ✅ Files to modify are listed
7. ✅ No circular dependencies
## Iteration Strategy
- Review completed tasks from previous cycle
- Keep incomplete tasks if still valid
- Add new tasks based on project needs
- Remove obsolete or completed tasks
- Adjust priorities based on feedback
## Communication
After generating task list:
- Summarize key changes in console output
- Highlight high-priority tasks
- Note any blocking dependencies
- Estimate total effort
---
**Remember**: Your task list drives autonomous development. Make tasks clear, actionable, and achievable. A well-defined task list leads to successful autonomous execution.
Good luck! 🎯

View file

@ -0,0 +1,15 @@
{
"component": "web_v2",
"current_task_id": null,
"iteration_count": 0,
"max_iterations": 10,
"started_at": null,
"last_commit": null,
"retry_count": 0,
"max_retries": 3,
"status": "idle",
"errors": [],
"completed_tasks": [],
"skipped_tasks": []
}

View file

@ -0,0 +1,9 @@
{
"project": "mnemo_cards_web_v2",
"component": "web_v2",
"version": "1.0",
"generated_at": null,
"generated_by": "planning_agent",
"tasks": []
}

View file

View file

@ -0,0 +1,709 @@
# TODO - mnemo_cards_web_v2
## Status: Active Development
**Last Updated:** November 8, 2025
---
## 🔥 Bug Fixes & Maintenance
### PURCHASE-1: Purchase Page Loading Issue - FIXED ✅
**Priority:** HIGH
**Status:** ✅ COMPLETED
**Time Spent:** 2 hours
**Date Fixed:** November 8, 2025
**Issue:** Purchase page (`/purchase/5`) was not loading due to JSON deserialization problems.
**Root Cause:**
- Constructor `CardPackBuyDto` incorrectly marked nullable fields as required
- UI method `_buildItem` used `item.toString()` which doesn't work for polymorphic Item subclasses
**Solution Applied:**
- Fixed `CardPackBuyDto` constructor to properly handle nullable fields
- Implemented type-safe rendering for different Item types (TextItem, SpacerItem, ButtonItem)
- Added proper spacing and visual elements for each item type
**Result:** Purchase page now loads correctly and displays pack information properly.
---
## 🔥 New Features
### TASKS-1: Tasks System Implementation - PHASE 1 COMPLETE ✅
**Priority:** HIGH
**Status:** ✅ Phase 1 Complete, Ready for Phase 2
**Estimated Time:** 40-60 hours total
**Plan Document:** `TASKS_PLAN.md`
**Goal:** Реализовать механику заданий для mnemo_cards_web_v2 - систему заданий, которые пользователь выполняет как в приложении, так и в реальном мире.
**Current Phase:** Phase 1 (Frontend Infrastructure) - Complete ✅
**Completed in Phase 1:**
- ✅ Created comprehensive task data models (Task, TaskProgress, TaskReward, enums)
- ✅ Implemented TasksRepository with mock data for development
- ✅ Created TasksStateManager with full state management using yx_state
- ✅ Added TasksModule to UserScope with proper dependency injection
- ✅ Built TaskCard widget with rewards display and action buttons
- ✅ Implemented TasksPage with filtering, tabs, and search functionality
- ✅ Added navigation route `/tasks` and updated bottom navigation
- ✅ Updated MainShell to include "Задания" tab
- ✅ Integrated with existing yx_scope/yx_state architecture
- ✅ Created unit tests for all components
**Next Actions:**
- [ ] Phase 2: Backend Integration (API endpoints, real data) - 12-16 hours
- [ ] Phase 3: Advanced Features (task creation, admin panel) - 8-12 hours
- [ ] Phase 4: Polish & Analytics (animations, tracking) - 8-12 hours
- [ ] Phase 5: Testing & Deployment (integration tests, production) - 8-12 hours
See `TASKS_PLAN.md` for complete breakdown.
---
### CHAT-1: Chat Module Implementation - MODULARIZATION COMPLETE ✅
**Priority:** HIGH
**Status:** ✅ Modularization Complete, Ready for Phase 2
**Estimated Time:** 60-80 hours total
**Plan Document:** `CHAT_PLAN.md`
**Goal:** Реализовать функциональность чата для общения пользователя с LLM через сервер, поддерживая текст и аудио сообщения.
**Current Phase:** Phase 1 (Infrastructure) - Complete ✅
**Completed:**
- ✅ **Modularization**: Created separate `mnemo_cards_chat` Flutter package
- ✅ **Architecture**: Clean architecture with ChatRepository interface for loose coupling
- ✅ **Models**: Comprehensive data models (ChatMessage, AudioMessage, ChatSession, ChatParticipant)
- ✅ **Services**: ChatService with business logic and ChatRepository abstraction
- ✅ **State Management**: Simplified ChatStateManager with manual state classes
- ✅ **DI Integration**: ChatModule for yx_scope integration in main application
- ✅ **Code Generation**: All freezed/json_serializable generation working
- ✅ **Compilation**: Module compiles successfully and integrates cleanly
**Next Actions:**
- [ ] Phase 2: UI Components (MessageBubble, ChatInput, AudioRecorder, ChatPage) - 16-20 hours
- [ ] Phase 3: Audio Functionality (recording, playback, Web Audio API) - 12-16 hours
- [ ] Phase 4: Integration & Polish (navigation, error handling, theming) - 8-12 hours
- [ ] Phase 5: Backend Integration & Testing (API endpoints, LLM integration) - 8-12 hours
See `CHAT_PLAN.md` for complete breakdown.
### GT-1: Game Tests Implementation - PLANNING COMPLETE ✅
**Priority:** HIGH
**Status:** 🟡 Planning Complete, Ready to Start
**Estimated Time:** 40-60 hours total
**Plan Document:** `GAME_TESTS_IMPLEMENTATION_PLAN.md`
**Goal:** Реализовать систему игровых тестов в mnemo_cards_web_v2, начиная с простых тестов с выбором 1 варианта из нескольких, с соблюдением архитектуры yx_scope/yx_state.
**Current Phase:** Planning Complete
**Current Phase:** Phase 4 Complete ✅ - UX Improvements FINISHED
**Status:** ✅ **GAME SYSTEM WITH ENHANCED UX READY**
**Successfully Implemented:**
- [x] Phase 1: Basic Infrastructure ✅
- [x] Phase 2: Multiple Choice Tests ✅
- [x] Phase 4: UX Improvements (animations, sounds, theming) ✅
- [x] Phase 5: Advanced Question Types ✅
**Question Types Available:**
- [x] **Multiple Choice** - Fully implemented and working
- [x] **Input Letters** - Fully implemented and working
- [x] **Match** - UI ready, waiting for backend support
- [x] **Matrix** - UI ready, waiting for backend support
**UX Enhancements Added:**
- [x] **Sound Effects** - Complete audio feedback system
- [x] **Animations** - Smooth transitions and visual feedback
- [x] **Dark Theme** - Full compatibility with light/dark themes
- [x] **Performance** - Optimized animations and resource usage
**Remaining Phases (Optional):**
- [ ] Phase 3: Statistics & Analytics (results submission) - 4-6 hours
**Game System is Production Ready!** 🎮✨
See `GAME_TESTS_IMPLEMENTATION_PLAN.md` for complete breakdown.
---
### STAT-1: Statistics System Upgrade - PLANNING COMPLETE ✅
**Priority:** HIGH
**Status:** 🟡 Planning Complete, Ready to Start
**Estimated Time:** 111-144 hours total
**Plan Document:** `STATISTICS_UPGRADE_PLAN.md`
**Goal:** Расширить систему сбора и отображения статистики пользователя для создания детализированной страницы профиля с красивым UI и настройками приложения.
**Current Phase:** Phase 1 - Backend Models and DTOs
**Next Actions:**
- [ ] Phase 1.1: Расширить модели данных (4-6 hours)
- [ ] Phase 1.2: Создать новые API endpoints (8-10 hours)
- [ ] Phase 2.2: Переписать StatisticsService (4-5 hours)
- [ ] Phase 3.1: Редизайн ProfilePage (12-15 hours)
- [ ] Phase 4.1: Создать Settings Page (10-12 hours)
See `STATISTICS_UPGRADE_PLAN.md` for complete breakdown.
---
## 🔴 Critical Issues
### CI-1: Fix Telegram Package Compilation Errors ✅ COMPLETE
**Priority:** HIGH
**Status:** ✅ Complete
**Problem:** `telegram_web_app-0.3.3` package has compilation errors with `JSExportedDartFunction` type
**Impact:** Tests cannot run, app may not compile
**Solution:** Either update package version, remove dependency, or add conditional compilation
---
### CI-2: Card Images Not Displaying ✅ COMPLETE
**Priority:** HIGH
**Status:** ✅ Complete
**Date Fixed:** December 19, 2024
**Problem:** Card word images not showing in packs
**Impact:** Users cannot see card images in pack lists, details, or card viewer
**Root Cause:** Frontend using deprecated `ApiConfig` generating wrong v1 API URLs instead of v2
**Solution:**
- Updated all frontend widgets to use `ApiConfigV2.getCardImageUrl()`
- Modified backend to allow public image access for enabled packs
- Added proper validation (pack exists, enabled, card belongs to pack)
- Enhanced error handling in backend endpoint
**Files Fixed:**
- `lib/presentation/widgets/pack_card_item.dart`
- `lib/presentation/widgets/card_flipper/card_flipper.dart`
- `lib/presentation/widgets/card_viewer.dart`
- `lib/presentation/pages/pack_details/pack_details_page.dart`
- `mnemo_cards_backend/lib/api/v2/packs_api_v2.dart`
**Tests Added:**
- `mnemo_cards_backend/test/api/v2/packs_api_v2_test.dart` (6 new tests) ✅
---
### CI-3: Fix Failing Tests (24 failures)
**Priority:** MEDIUM
**Status:** 🟡 In Progress
**Problem:** 24 tests are failing (177 passing)
**Impact:** Mostly empty test files causing compilation errors
**Action:** Fix test_page_test.dart empty file, investigate other failures
**Notes:** Lower priority - most failures are from empty test files
---
## 🟡 Backend Integration Tasks
### BI-0: API v2 Backend Implementation ✅ PHASE 1.2 COMPLETE
**Priority:** HIGH
**Status:** Phase 1.2 Complete (Auth API)
**Latest Update:** October 29, 2025
**Phase 1.1 - JWT Service ✅ COMPLETE:**
- ✅ Fixed JWT crypto implementation with proper HMAC-SHA256
- ✅ Created RefreshTokenModel Isar model for token storage
- ✅ Implemented token storage, blacklisting, and cleanup methods
- ✅ Written comprehensive unit tests (15 tests, all passing)
**Phase 1.2 - Authentication API v2 ✅ COMPLETE:**
- ✅ Google OAuth flow implemented and tested
- ✅ Token refresh mechanism implemented and tested
- ✅ Logout endpoint with refresh token blacklisting
- ✅ Get current user endpoint
- ✅ Comprehensive integration tests (12 tests, all passing)
- ✅ Improved error handling and error responses
- ✅ Updated HttpRepositoryV2 logout to send refresh token
**Next Steps (Phase 1.3):**
- ⬜ Implement Packs API v2 with pagination and filtering
- ⬜ Implement Tests API v2
- ⬜ Implement remaining v2 APIs (Games, Purchases, Subscriptions, Promocodes)
**Files Created/Modified:**
- `mnemo_cards_backend/lib/api/v2/auth_api_v2.dart`
- `mnemo_cards_backend/lib/api/v2/jwt_service.dart`
- `mnemo_cards_backend/test/api/v2/auth_api_v2_test.dart` ✅ (12 tests)
- `mnemo_cards_backend/test/api/v2/jwt_service_test.dart` ✅ (15 tests)
- `mnemo_cards_web_v2/lib/domain/services/http_repository_v2.dart`
---
### BI-1: Card Flipping Functionality ✅ COMPLETE
**Priority:** MEDIUM
**Status:** ✅ Complete
**Estimated Time:** 0 hours (already implemented)
**Description:** Card flipping functionality is already fully implemented
**Verification:**
- ✅ CardFlipper widget exists and works
- ✅ Card flip UI with animations implemented
- ✅ Progress tracking implemented
- ✅ Integration with PackDetailsPage complete
**Files Verified:**
- `lib/presentation/widgets/card_flipper/card_flipper.dart`
- `lib/domain/services/card_flipper_service.dart`
- `lib/di/user_scope/modules/card_flipper_module.dart`
---
### BI-2: Pack Purchase Functionality ✅ COMPLETE
**Priority:** MEDIUM
**Status:** ✅ Complete
**Date Completed:** November 8, 2025
**Time Spent:** 5 hours
**Description:** Implement pack purchase flow with payment integration
- **Progress:**
- [x] Implemented API v2 client helpers and `PurchasesService` with DI wiring
- [x] Added unit tests validating service → repository delegation
- [x] Created `PurchaseStateManager` with freezed states
- [x] Created `PurchasePage` with YooKassa payment integration
- [x] Added purchase module to DI
- [x] Added purchase route to app_router
- [x] Wrote comprehensive unit tests for state manager
**Features Implemented:**
- [x] Purchase page UI with pack preview
- [x] YooKassa payment integration
- [x] Payment URL launching
- [x] Payment verification dialog
- [x] Success/error states handling
- [x] Purchase state management with yx_state
- [x] Purchase module with DI wiring
**API Endpoints Used:**
- GET `/api/v2/packs/{packId}/buy` - Get purchase page info ✅
- POST `/api/v2/purchases/packs/{packId}` - Create pack purchase intent ✅
- POST `/api/v2/purchases/payments` - Create YooKassa payment ✅
- GET `/api/v2/purchases/payments/{paymentId}/verify` - Verify payment status ✅
**Files Created/Updated:**
- `lib/domain/models/purchase_models.dart`
- `lib/domain/services/http_repository_v2.dart`
- `lib/domain/services/purchases_service.dart`
- `lib/domain/state/purchase_state_manager.dart` ✅ (NEW)
- `lib/di/user_scope/modules/purchase_module.dart` ✅ (NEW)
- `lib/di/user_scope/modules/purchases_module.dart`
- `lib/di/user_scope/user_scope.dart`
- `lib/di/user_scope/user_scope_container.dart`
- `lib/presentation/pages/purchase/purchase_page.dart` ✅ (NEW)
- `lib/presentation/router/app_router.dart`
- `test/domain/services/purchases_service_test.dart`
- `test/domain/state/purchase_state_manager_test.dart` ✅ (NEW)
---
### BI-2B: Pack Purchase Status Check ✅ COMPLETE
**Priority:** HIGH
**Status:** ✅ Complete
**Date Completed:** November 8, 2025
**Time Spent:** 2 hours
**Description:** Modify PackDetailsPage to check pack purchase status and redirect to purchase page if pack is not purchased.
**Features Implemented:**
- [x] Updated PackDetailsPage to use `GetCardPackResponse` union type
- [x] Added purchase status check in `_loadPack()` method
- [x] Implemented automatic redirect to `/purchase/:packId` for unpurchased packs
- [x] Maintained proper loading and error states
- [x] Updated all methods to handle `CardPackDto` type casting
- [x] Verified app compiles successfully with new logic
**Technical Implementation:**
- [x] Response type checking: `packResponse.responseType == GetCardPackResponseType.buy`
- [x] Automatic redirect: `context.push('/purchase/${widget.packId}');` for unpurchased packs
- [x] Type safety: Proper `as CardPackDto` casting after purchase verification
- [x] Backward compatibility: All existing functionality preserved for purchased packs
**User Experience:**
- [x] Unpurchased packs: Direct redirect to purchase page (no details shown)
- [x] Purchased packs: Full pack details page with all features
- [x] Error states: Proper error handling for network issues
- [x] Loading states: Smooth loading experience maintained
**Files Modified:**
- `lib/presentation/pages/pack_details/pack_details_page.dart`
---
### BI-2A: Ads Reward Unlock Flow ✅ COMPLETE
**Priority:** HIGH
**Status:** ✅ Complete - Real Adsgram Integration
**Date Completed:** November 8, 2025
**Time Spent:** 6 hours
**Description:** Allow users to unlock specific packs/products on the web by watching a rewarded ad, similar to the mobile experience.
**Progress:**
- [x] Implemented AdsRewardService, AdsRewardStateManager, and user scope module with unit tests
- [x] Added animated shuffle transitions for pack card grid/list views
- [x] Created AdsRewardButton widget with state management integration
- [x] Integrated AdsRewardButton into pack_details_page.dart
- [x] Added Adsgram SDK integration for rewarded ads
- [x] Added loading, success, and error states to UI
- [x] Wrote widget tests for AdsRewardButton
**Features Implemented:**
- [x] Detect packs eligible for ad unlock and surface CTA in UI
- [x] Integrate Adsgram rewarded ad web SDK with proper lifecycle handling
- [x] Track ad playback state, completion, and failure
- [x] Call `/ads/product/acquire/<key>` upon rewarded completion and refresh user entitlements
- [x] Provide user feedback (loading, success, retry prompts)
- [x] Emit analytics events for impressions, completions, failures
- [x] Added Adsgram block ID configuration (16505)
- [x] Implemented reward callback endpoint `/adsgram/reward?userId=[userId]`
- [x] JavaScript interop with bidirectional callbacks
- [x] Real Adsgram SDK integration (no simulation)
- [x] Enhanced web/foos.js with callback system
**API Endpoints Used:**
- POST `/ads/product/acquire/<key>` - Grant product after rewarded ad ✅
- GET `/api/v2/packs/{packId}/buy` - Check ad availability ✅
- GET `/api/v2/adsgram/reward?userId={userId}` - Adsgram reward callback ✅
**Files Created/Updated:**
- `lib/presentation/widgets/ads_reward_button.dart` ✅ (NEW)
- `lib/domain/config/api_config_v2.dart` ✅ (ads config)
- `lib/presentation/pages/pack_details/pack_details_page.dart` ✅ (integration)
- `pubspec.yaml` ✅ (adsgram dependency)
- `test/presentation/widgets/ads_reward_button_test.dart` ✅ (NEW)
---
### BI-3: Subscription Management
**Priority:** MEDIUM
**Status:** 🟡 Partial (SubscriptionService exists)
**Estimated Time:** 4-6 hours
**Description:** Complete subscription purchase and management
**Features Needed:**
- [ ] Subscription page UI
- [ ] View available subscription plans
- [ ] Purchase subscription
- [ ] Cancel subscription
- [ ] Show subscription status on ProfilePage
**API Endpoints:**
- GET `/subscription/page` - Get subscription info ✅ (implemented)
- POST `/subscription/add` - Purchase subscription ✅ (implemented)
- POST `/subscription/delete/<id>` - Cancel subscription
**Files to Create:**
- `lib/presentation/pages/subscription/subscription_page.dart`
- Update `subscription_service.dart` with cancel method
- Add route to `app_router.dart`
---
### BI-4: Vocabulary/Review Page
**Priority:** LOW
**Status:** ⬜ Not Started
**Estimated Time:** 6-8 hours
**Description:** Create vocabulary page to review all learned words across packs
**Features Needed:**
- [ ] VocabularyPage in bottom navigation
- [ ] Display all learned cards
- [ ] Filter by pack, language
- [ ] Search functionality
- [ ] Review cards
- [ ] Export vocabulary
**API Endpoints:**
- GET `/cards` - Fetch all cards
- GET `/user/data` - Get user's learning progress
**Files to Create:**
- `lib/presentation/pages/vocabulary/vocabulary_page.dart`
- `lib/domain/services/vocabulary_service.dart`
- `lib/domain/state/vocabulary_state_manager.dart`
- `lib/di/user_scope/modules/vocabulary_module.dart`
---
### BI-5: Promocode Functionality
**Priority:** LOW
**Status:** 🟡 Partial (Service migrated; awaiting UI)
**Estimated Time:** 3-4 hours
**Description:** UI for entering and applying promocodes
**Features Needed:**
- [ ] Promocode input field on ProfilePage or PurchasePage
- [ ] Apply promocode
- [ ] Show promocode benefits
- [ ] Validate promocode
**API Endpoints:**
- POST `/user/promocode` - Apply promocode ✅ (implemented)
- GET `/promocode/list` - List available promocodes ✅ (implemented)
**Files to Create:**
- `lib/presentation/widgets/promocode_input.dart`
- Update `promocode_service.dart` UI integration
---
### BI-6: Settings Page
**Priority:** LOW
**Status:** ⬜ Not Started
**Estimated Time:** 2-3 hours
**Description:** Separate settings page (currently settings are in ProfilePage)
**Features Needed:**
- [ ] Separate SettingsPage
- [ ] Theme toggle
- [ ] Language selection
- [ ] Sound effects toggle
- [ ] Notifications settings
- [ ] Account settings
**API Endpoints:**
- POST `/user/settings` - Update user settings
**Files to Create:**
- `lib/presentation/pages/settings/settings_page.dart`
- `lib/domain/state/settings_state_manager.dart`
---
### BI-7: Card Images Display ✅ COMPLETE
**Priority:** HIGH
**Status:** ✅ Complete
**Estimated Time:** 0 hours (already implemented)
**Description:** Display card images in PackDetailsPage and CardFlipper
**Features Needed:**
- [x] Fetch card images from backend (using Image.network with ApiConfig.getCardImageUrl)
- [x] Display in card list (PackDetailsPage._buildCardImage)
- [x] Display in card viewer (CardViewer._buildImage)
- [x] Display in card flipper (CardFlipper._buildImage)
**Notes:** Card images are already fully implemented using Image.network. Flutter handles caching automatically. No additional work needed.
---
### BI-8: Card Flipper Responsive Layout ✅ COMPLETE
**Priority:** MEDIUM
**Status:** ✅ Complete
**Estimated Time:** 2 hours
**Description:** Align web CardFlipper experience with mobile adaptive behavior by introducing responsive layouts while preserving existing state and animations.
**Features Delivered:**
- [x] Breakpoint resolver (compact / medium / expanded) via `LayoutBuilder`
- [x] Adaptive card sizing that respects viewport height and width
- [x] Responsive progress indicator and control clusters per breakpoint
- [x] Optional `stateManagerOverride` parameter for isolated widget testing
- [x] Widget tests covering compact, tablet, wide desktop, and tall desktop scenarios
**Notes:** No backend changes required. Verify new widget tests in `card_flipper_responsive_test.dart` during CI.
---
### BI-9: Card Viewer Study Flow ✅ COMPLETE
**Priority:** MEDIUM
**Status:** ✅ Complete
**Estimated Time:** 2 hours
**Description:** Launch fullscreen study mode directly from pack card taps, mirroring mobile UX without redundant controls.
**Features Delivered:**
- [x] Removed dedicated “Изучение” CTA from pack controls
- [x] Routed taps through `CardViewer` with ordered card lists (shuffle + favorites aware)
- [x] Started study at tapped card index with consistent navigation
- [x] Added widget tests covering initial index, swiping order, and flip interaction
**Notes:** Learning progress marking remains handled externally via `_markCardLearned`. Future enhancements can add per-card callbacks if needed.
---
### BI-10: Pack Details Shuffle Animation ✅ COMPLETE
**Priority:** LOW
**Status:** ✅ Complete
**Estimated Time:** 1 hour
**Description:** Make pack card shuffling feel responsive and delightful with animated transitions and control feedback.
**Features Delivered:**
- [x] Added reusable `ShuffleAnimatedSwitcher` for fade + scale transitions across grid/list shuffles
- [x] Highlighted shuffle control with active state styling and `AnimatedRotation` feedback
- [x] Animated card reordering with movement-aware wrappers plus widget/unit coverage
**Notes:** Animation is triggered whenever shuffle/favorites state changes via `_shuffleAnimationKey`. Scroll position resets intentionally to showcase rearranged cards.
---
## 🟢 Quality & Testing Tasks
### QT-1: Increase Test Coverage
**Priority:** MEDIUM
**Status:** 🔴 In Progress
**Progress:** ~70% coverage
**Areas Needing Tests:**
- [ ] pack_progress_service_test.dart
- [ ] promocode_service_test.dart (partial)
- [ ] subscription_service_test.dart (partial)
- [ ] card_flipper_service_test.dart
- [ ] All new pages
---
### QT-2: Fix Linter Issues
**Priority:** LOW
**Status:** ⬜ Not Started
**Action:** Run `flutter analyze` and fix all warnings
---
### QT-3: Integration Tests
**Priority:** LOW
**Status:** ⬜ Not Started
**Tests Needed:**
- [ ] Full auth flow
- [ ] Pack browsing and purchase
- [ ] Test taking flow
- [ ] Card learning flow
---
## 🚫 Blocked/Deferred Tasks
### BD-1: Telegram Authentication ✅ COMPLETE
**Priority:** MEDIUM
**Status:** ✅ Complete
**Completion Date:** November 8, 2025
**Outcome:** Web-initiated Telegram login bridge with 5-minute codes, bot claims, and improved web UI.
**Highlights:**
- Implemented backend `/auth/telegram/web-code`, `/claim-code`, and `/code-status/{code}` endpoints
- Updated Telegram bot to accept `login_<code>` payloads and keep `/code` fallback
- Added web login UI for code generation, bot deep-link, status polling, and auto-login
- Created unit tests for auth service helpers and code status parsing
---
### UI-1: PackTip Support Implementation ✅ COMPLETE
**Priority:** MEDIUM
**Status:** ✅ Complete
**Estimated Time:** 3 hours
**Actual Time:** 5 hours
**Description:** Add support for CardPackPreviewDto.tip field to display small icons or badges in corners or right side of pack cards, adapting PackTip functionality from mobile app to web version for both horizontal and vertical card layouts.
**Completed Tasks:**
- ✅ Created PackTipExt extension for PackTip.build() method
- ✅ Implemented support for all PackTipType variants (asset, base64, text, unknown)
- ✅ Added _buildPackTip() method to PackCard widget (horizontal layout)
- ✅ Added _buildPackTip() method to PackCardVertical widget (vertical layout)
- ✅ Implemented support for all PackTipPosition values (topRight, bottomRight, fullRight)
- ✅ Adapted fullRight positioning: right side for horizontal, bottom banner for vertical cards
- ✅ Refactored both card layouts to use Stack for tip overlays
- ✅ Added proper theming and error handling
- ✅ Verified build success and code quality
**Files Created/Modified:**
- `lib/utils/pack_tip_extension.dart` - PackTipExt extension
- `lib/presentation/widgets/pack_card.dart` - PackTip integration for horizontal cards
- `lib/presentation/widgets/pack_card_vertical.dart` - PackTip integration for vertical cards
**Technical Details:**
- Extension pattern for clean PackTip rendering
- Stack-based overlay system for tip positioning on both card types
- Adaptive positioning logic for horizontal vs vertical layouts
- Full compatibility with mobile PackTip system
- Type-safe implementation with proper error handling
**Next Steps:**
- Test with real backend PackTip data
- Monitor performance with multiple tips
- Consider animation enhancements
---
### BD-2: API v2 Implementation 🔄 IN PROGRESS
**Priority:** HIGH
**Status:** 🟡 In Progress
**Estimated Time:** 34-46 hours for core work
**Description:** Implement API v2 with OAuth2/JWT, RESTful patterns, and versioning
**Current Status:**
- ✅ Backend: AuthApiV2, JwtService, authorizeV2 middleware created
- ✅ Web: ApiConfigV2, HttpRepositoryV2 created
- ✅ AuthService migrated to use v2
- ⚠️ Backend: JWT crypto needs proper implementation
- ⚠️ Backend: Remaining v2 endpoints need implementation
- ⚠️ Web: Remaining services need migration to v2
**See:** `FUTURE_TASKS_PLAN.md` for detailed breakdown
---
## 📊 Progress Summary
**Total Tasks:** 18
**Completed:** 1
**In Progress:** 2
**Not Started:** 13
**Blocked/Deferred:** 2
**Priority Breakdown:**
- 🔴 HIGH: 5 tasks
- 🟡 MEDIUM: 7 tasks
- 🟢 LOW: 5 tasks
---
## 🎯 Recommended Next Steps (See FUTURE_TASKS_PLAN.md for details)
### Immediate Priority (Phase 1 - Backend v2)
1. **Fix JWT Service** - Use proper crypto library for HMAC-SHA256
2. **Complete Auth API v2** - Test and verify Google OAuth flow
3. **Implement Packs API v2** - Complete all pack endpoints
4. **Implement Tests API v2** - Complete test endpoints
5. **Implement remaining v2 APIs** - Games, Purchases, Subscriptions, Promocodes
### Next Priority (Phase 2 - Web Migration)
1. **Complete HttpRepositoryV2** - Add all missing methods
2. **Migrate PackManager** - Update to use v2
3. **Migrate remaining services** - GamesManager, TestManager, etc.
4. **Remove v1 dependencies** - Clean up deprecated code
### After Migration (Phase 3 - Features)
1. **Pack Purchase Flow** - Implement purchase UI and flow
2. **Subscription Management** - Complete subscription UI
3. **Promocode UI** - Add promocode input and application
**For complete detailed plan, see:** `FUTURE_TASKS_PLAN.md`
---
**Note:** Tasks are prioritized based on:
- User impact
- Technical dependencies
- Development effort
- Backend availability

View file

@ -0,0 +1,422 @@
"""
Main orchestrator for AI agent execution.
Manages the development cycle: read tasks -> execute -> test -> commit -> repeat
"""
import sys
import subprocess
import time
from pathlib import Path
from datetime import datetime, timezone
from typing import Optional
from config import AgentConfig, get_test_command, get_lint_command
from task_manager import TaskManager, GlobalLock, Task
from cursor_cli_wrapper import CursorCLI, CursorResultStatus
class AgentOrchestrator:
"""Orchestrates the agent development cycle."""
def __init__(self, config: AgentConfig):
self.config = config
self.task_manager = TaskManager(
config.task_list_path,
config.agent_state_path
)
self.global_lock = GlobalLock(config.global_lock_path)
self.cursor_cli = CursorCLI(
project_root=config.project_root,
api_key=config.cursor_api_key,
model=config.cursor_model,
verbose=True
)
def run(self) -> int:
"""
Main execution loop.
Returns: 0 on success, 1 on error
"""
print(f"🚀 Starting AI Agent for component: {self.config.component}")
print(f"📁 Project root: {self.config.project_root}")
print(f"🎯 Max iterations: {self.config.max_iterations}")
# Check if another agent is running
if self.task_manager.is_agent_running():
print(f"⚠️ Another agent is already running for {self.config.component}")
print("Exiting to avoid conflicts.")
return 0
try:
# Main development loop
while True:
state = self.task_manager.load_state()
# Check iteration limit
if state.iteration_count >= self.config.max_iterations:
print(f"\n⏹️ Reached maximum iterations ({self.config.max_iterations})")
break
# Get next task
next_task = self.task_manager.get_next_task()
if not next_task:
print("\n✅ All tasks completed!")
self.task_manager.reset_state()
break
print(f"\n{'='*80}")
print(f"📋 Task {state.iteration_count + 1}/{self.config.max_iterations}: {next_task.id}")
print(f"📝 {next_task.title}")
print(f"⏱️ Estimated: {next_task.estimated_hours}h")
print(f"{'='*80}\n")
# Start task
self.task_manager.start_task(next_task)
# Check if task needs global lock (modifies common package)
needs_global_lock = self._needs_global_lock(next_task)
if needs_global_lock:
if not self._acquire_global_lock():
print("⚠️ Cannot acquire global lock, skipping task")
self.task_manager.mark_task_skipped(
next_task.id,
"Global lock not available"
)
continue
try:
# Execute task
success = self._execute_task(next_task)
if success:
# Task completed successfully
print(f"\n✅ Task {next_task.id} completed successfully")
self.task_manager.mark_task_completed(next_task.id)
else:
# Task failed
retry_count = self.task_manager.increment_retry()
if retry_count >= self.config.max_retries:
print(f"\n❌ Task {next_task.id} failed after {retry_count} retries")
self.task_manager.mark_task_failed(
next_task.id,
f"Failed after {retry_count} retries"
)
# Create GitHub issue
self._create_issue_for_failed_task(next_task)
else:
print(f"\n⚠️ Task {next_task.id} failed, will retry ({retry_count}/{self.config.max_retries})")
finally:
# Release global lock if held
if needs_global_lock:
self.global_lock.release(self.config.component)
# Small delay between tasks
time.sleep(2)
print("\n🎉 Agent execution completed")
return 0
except KeyboardInterrupt:
print("\n⚠️ Interrupted by user")
self.task_manager.reset_state()
return 1
except Exception as e:
print(f"\n❌ Fatal error: {e}")
import traceback
traceback.print_exc()
return 1
def _needs_global_lock(self, task: Task) -> bool:
"""Check if task needs global lock (modifies shared resources)."""
# Check if any files to modify are in mnemo_cards_common
for file_path in task.files_to_modify:
if "mnemo_cards_common" in file_path:
return True
return False
def _acquire_global_lock(self, timeout_minutes: int = 30) -> bool:
"""Try to acquire global lock with timeout."""
start_time = time.time()
while True:
if self.global_lock.acquire(
self.config.component,
f"Working on {self.config.component}"
):
print("🔒 Acquired global lock")
return True
elapsed_minutes = (time.time() - start_time) / 60
if elapsed_minutes >= timeout_minutes:
return False
print(f"⏳ Waiting for global lock... ({elapsed_minutes:.1f}/{timeout_minutes} min)")
time.sleep(30) # Check every 30 seconds
def _execute_task(self, task: Task) -> bool:
"""
Execute a single task.
Returns: True if successful, False otherwise
"""
# Load prompt template
prompt = self._build_task_prompt(task)
# Run cursor agent
print("🤖 Running Cursor Agent...")
result = self.cursor_cli.run_agent(
task_description=prompt,
force=True,
max_iterations=5
)
if result.status != CursorResultStatus.SUCCESS:
print(f"❌ Cursor agent failed: {result.error}")
return False
print(f"\n📊 Agent completed:")
print(f" - Files created: {len(result.files_created)}")
print(f" - Files modified: {len(result.files_modified)}")
print(f" - Tool calls: {result.tool_calls}")
# Pull latest changes before committing
print("\n🔄 Pulling latest changes...")
self._git_pull()
# Run linter
print("\n🔍 Running linter...")
if not self._run_lint():
print("⚠️ Linter found issues")
# Don't fail, cursor can fix in retry
return False
# Run tests
print("\n🧪 Running tests...")
if not self._run_tests():
print("❌ Tests failed")
return False
# Commit changes
print("\n💾 Committing changes...")
commit_message = self._build_commit_message(task, result)
if not self._git_commit(commit_message):
print("⚠️ No changes to commit")
# Push changes
print("\n📤 Pushing changes...")
self._git_push()
return True
def _build_task_prompt(self, task: Task) -> str:
"""Build comprehensive prompt for the agent."""
# Load development prompt template
prompt_file = self.config.prompts_dir / "development_prompt.md"
if prompt_file.exists():
with open(prompt_file, 'r') as f:
template = f.read()
else:
template = "You are an AI software engineer. Complete the following task:\n\n"
# Add task details
prompt = f"""{template}
## TASK: {task.id} - {task.title}
### Priority: {task.priority.upper()}
### Description:
{task.description}
### Acceptance Criteria:
"""
for i, criterion in enumerate(task.acceptance_criteria, 1):
prompt += f"{i}. {criterion}\n"
prompt += f"""
### Files to Modify:
"""
for file_path in task.files_to_modify:
prompt += f"- {file_path}\n"
prompt += f"""
### Component: {task.component}
### Working Directory: {self.config.component_root}
---
Complete this task following clean architecture principles and project conventions.
Write comprehensive unit tests for all new functionality.
Make sure all existing tests continue to pass.
"""
return prompt
def _build_commit_message(self, task: Task, result) -> str:
"""Build descriptive commit message."""
message = f"""feat({self.config.component}): {task.title}
Task ID: {task.id}
Priority: {task.priority}
Changes:
"""
if result.files_created:
message += f"- Created {len(result.files_created)} file(s)\n"
for file in result.files_created[:5]: # Limit to 5
message += f" - {file}\n"
if result.files_modified:
message += f"- Modified {len(result.files_modified)} file(s)\n"
for file in result.files_modified[:5]: # Limit to 5
message += f" - {file}\n"
message += f"\nCompleted by: AI Agent\n"
message += f"Duration: {result.duration_ms}ms\n"
return message
def _run_lint(self) -> bool:
"""Run linter for component."""
cmd = get_lint_command(self.config.component)
result = subprocess.run(
cmd,
shell=True,
cwd=self.config.project_root,
capture_output=True,
text=True
)
if result.returncode == 0:
print("✅ Linter passed")
return True
else:
print(f"⚠️ Linter output:\n{result.stdout}")
return False
def _run_tests(self) -> bool:
"""Run tests for component."""
cmd = get_test_command(self.config.component)
result = subprocess.run(
cmd,
shell=True,
cwd=self.config.project_root,
capture_output=True,
text=True
)
if result.returncode == 0:
print("✅ All tests passed")
return True
else:
print(f"❌ Test output:\n{result.stdout}")
return False
def _git_pull(self) -> bool:
"""Pull latest changes from remote."""
result = subprocess.run(
["git", "pull", "--rebase"],
cwd=self.config.project_root,
capture_output=True,
text=True
)
return result.returncode == 0
def _git_commit(self, message: str) -> bool:
"""Commit changes."""
# Add all changes
subprocess.run(
["git", "add", "-A"],
cwd=self.config.project_root
)
# Commit
result = subprocess.run(
["git", "commit", "-m", message],
cwd=self.config.project_root,
capture_output=True,
text=True
)
if result.returncode == 0:
# Get commit hash
commit_hash = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=self.config.project_root,
capture_output=True,
text=True
).stdout.strip()
# Update state with commit hash
state = self.task_manager.load_state()
state.last_commit = commit_hash
self.task_manager.save_state(state)
print(f"✅ Committed: {commit_hash[:8]}")
return True
else:
# No changes or error
return False
def _git_push(self) -> bool:
"""Push changes to remote."""
result = subprocess.run(
["git", "push"],
cwd=self.config.project_root,
capture_output=True,
text=True
)
if result.returncode == 0:
print("✅ Pushed to remote")
return True
else:
print(f"⚠️ Push failed: {result.stderr}")
return False
def _create_issue_for_failed_task(self, task: Task) -> None:
"""Create GitHub/Forgejo issue for failed task."""
# TODO: Implement GitHub/Forgejo API integration
print(f"\n📋 TODO: Create issue for failed task {task.id}")
print(f" Title: Failed: {task.title}")
print(f" Description: Task failed after {self.config.max_retries} retries")
def main():
"""Main entry point."""
if len(sys.argv) < 2:
print("Usage: python agent_orchestrator.py <component>")
print("Components: web_v2, backend, common")
sys.exit(1)
component = sys.argv[1]
# Load configuration
config = AgentConfig.from_env(component)
try:
config.validate()
except ValueError as e:
print(f"❌ Configuration error: {e}")
sys.exit(1)
# Create orchestrator and run
orchestrator = AgentOrchestrator(config)
exit_code = orchestrator.run()
sys.exit(exit_code)
if __name__ == "__main__":
main()

123
tools/agent/config.py Normal file
View file

@ -0,0 +1,123 @@
"""
Configuration module for AI Agent system.
"""
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
@dataclass
class AgentConfig:
"""Configuration for AI agent execution."""
# Project paths
project_root: Path
component: str # 'web_v2', 'backend', 'common'
# Agent limits
max_iterations: int = 10
max_retries: int = 3
timeout_hours: int = 6
# Cursor CLI settings
cursor_api_key: Optional[str] = None
cursor_model: str = "claude-3-5-sonnet-20241022"
# State file paths
@property
def agent_dir(self) -> Path:
return self.project_root / "ai_docs" / "agent" / self.component
@property
def task_list_path(self) -> Path:
return self.agent_dir / "task_list.json"
@property
def agent_state_path(self) -> Path:
return self.agent_dir / "agent_state.json"
@property
def global_lock_path(self) -> Path:
return self.project_root / "ai_docs" / "agent" / "global_lock.json"
@property
def prompts_dir(self) -> Path:
return self.project_root / "ai_docs" / "agent" / "prompts"
@property
def component_root(self) -> Path:
"""Root directory of the component being worked on."""
if self.component == "web_v2":
return self.project_root / "mnemo_cards_web_v2"
elif self.component == "backend":
return self.project_root / "mnemo_cards_backend"
elif self.component == "common":
return self.project_root / "mnemo_cards_common"
else:
raise ValueError(f"Unknown component: {self.component}")
@classmethod
def from_env(cls, component: str) -> "AgentConfig":
"""Create configuration from environment variables."""
project_root = Path(os.getenv("PROJECT_ROOT", os.getcwd()))
return cls(
project_root=project_root,
component=component,
max_iterations=int(os.getenv("MAX_ITERATIONS", "10")),
max_retries=int(os.getenv("MAX_RETRIES", "3")),
timeout_hours=int(os.getenv("TIMEOUT_HOURS", "6")),
cursor_api_key=os.getenv("CURSOR_API_KEY"),
cursor_model=os.getenv("CURSOR_MODEL", "claude-3-5-sonnet-20241022"),
)
def validate(self) -> None:
"""Validate configuration."""
if not self.project_root.exists():
raise ValueError(f"Project root does not exist: {self.project_root}")
if not self.cursor_api_key:
raise ValueError("CURSOR_API_KEY environment variable is required")
if self.component not in ["web_v2", "backend", "common"]:
raise ValueError(f"Invalid component: {self.component}")
if not self.component_root.exists():
raise ValueError(f"Component root does not exist: {self.component_root}")
# Component-specific configurations
COMPONENT_CONFIGS = {
"web_v2": {
"test_command": "cd mnemo_cards_web_v2 && flutter test",
"lint_command": "cd mnemo_cards_web_v2 && flutter analyze",
"build_command": "cd mnemo_cards_web_v2 && flutter build web",
},
"backend": {
"test_command": "cd mnemo_cards_backend && dart test",
"lint_command": "cd mnemo_cards_backend && dart analyze",
"build_command": "cd mnemo_cards_backend && dart compile exe bin/server.dart",
},
"common": {
"test_command": "cd mnemo_cards_common && dart test",
"lint_command": "cd mnemo_cards_common && dart analyze",
"build_command": None, # No build for common package
},
}
def get_test_command(component: str) -> str:
"""Get test command for component."""
return COMPONENT_CONFIGS[component]["test_command"]
def get_lint_command(component: str) -> str:
"""Get lint command for component."""
return COMPONENT_CONFIGS[component]["lint_command"]
def get_build_command(component: str) -> Optional[str]:
"""Get build command for component."""
return COMPONENT_CONFIGS[component]["build_command"]

View file

@ -0,0 +1,323 @@
"""
Cursor CLI wrapper with stream-json parsing and progress tracking.
"""
import json
import subprocess
import sys
from pathlib import Path
from typing import Optional, Dict, Any, List, Callable
from dataclasses import dataclass
from enum import Enum
class CursorResultStatus(Enum):
"""Status of cursor CLI execution."""
SUCCESS = "success"
FAILURE = "failure"
TIMEOUT = "timeout"
ERROR = "error"
@dataclass
class CursorResult:
"""Result of cursor CLI execution."""
status: CursorResultStatus
output: str
error: Optional[str]
duration_ms: Optional[int]
files_modified: List[str]
files_created: List[str]
files_read: List[str]
tool_calls: int
class CursorCLI:
"""Wrapper for Cursor CLI with stream-json parsing."""
def __init__(self,
project_root: Path,
api_key: str,
model: str = "claude-3-5-sonnet-20241022",
verbose: bool = True):
self.project_root = project_root
self.api_key = api_key
self.model = model
self.verbose = verbose
def run_agent(self,
task_description: str,
force: bool = True,
max_iterations: int = 5,
progress_callback: Optional[Callable[[str, Any], None]] = None) -> CursorResult:
"""
Run cursor agent on a task.
Args:
task_description: Description of the task for the agent
force: Allow file modifications
max_iterations: Maximum number of agent iterations
progress_callback: Callback function(event_type, data) for progress updates
Returns:
CursorResult with execution details
"""
# Build command
cmd = [
"cursor-agent",
"-p", # Print mode (non-interactive)
"--output-format", "stream-json",
"--stream-partial-output",
]
if force:
cmd.append("--force")
# Add task description
cmd.append(task_description)
# Set environment
env = {
"CURSOR_API_KEY": self.api_key,
"CURSOR_MODEL": self.model,
}
if self.verbose:
print(f"🚀 Running cursor-agent in {self.project_root}")
print(f"📝 Task: {task_description[:100]}...")
# Track execution metrics
accumulated_text = ""
tool_count = 0
files_modified = []
files_created = []
files_read = []
duration_ms = None
error_message = None
try:
# Run cursor-agent with streaming output
process = subprocess.Popen(
cmd,
cwd=self.project_root,
env={**subprocess.os.environ, **env},
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1
)
# Process streaming output line by line
for line in process.stdout:
line = line.strip()
if not line:
continue
try:
event = json.loads(line)
event_type = event.get("type")
subtype = event.get("subtype")
# Call progress callback
if progress_callback:
progress_callback(event_type, event)
# Process different event types
if event_type == "system":
self._handle_system_event(event, subtype)
elif event_type == "assistant":
accumulated_text += self._handle_assistant_event(event)
elif event_type == "tool_call":
tool_result = self._handle_tool_call_event(
event, subtype, files_modified, files_created, files_read
)
if tool_result:
tool_count += 1
elif event_type == "result":
duration_ms = event.get("duration_ms")
if self.verbose:
self._print_summary(
duration_ms, tool_count,
len(accumulated_text),
files_created, files_modified
)
except json.JSONDecodeError:
# Not JSON, might be error message
if self.verbose:
print(f"⚠️ {line}")
error_message = line
# Wait for process to complete
return_code = process.wait()
# Check stderr
stderr = process.stderr.read()
if stderr:
error_message = stderr
if self.verbose:
print(f"❌ Error: {stderr}")
# Determine status
if return_code == 0:
status = CursorResultStatus.SUCCESS
else:
status = CursorResultStatus.FAILURE
return CursorResult(
status=status,
output=accumulated_text,
error=error_message,
duration_ms=duration_ms,
files_modified=files_modified,
files_created=files_created,
files_read=files_read,
tool_calls=tool_count
)
except subprocess.TimeoutExpired:
return CursorResult(
status=CursorResultStatus.TIMEOUT,
output=accumulated_text,
error="Cursor CLI execution timeout",
duration_ms=None,
files_modified=files_modified,
files_created=files_created,
files_read=files_read,
tool_calls=tool_count
)
except Exception as e:
return CursorResult(
status=CursorResultStatus.ERROR,
output=accumulated_text,
error=str(e),
duration_ms=None,
files_modified=files_modified,
files_created=files_created,
files_read=files_read,
tool_calls=tool_count
)
def _handle_system_event(self, event: Dict[str, Any], subtype: Optional[str]) -> None:
"""Handle system event."""
if subtype == "init" and self.verbose:
model = event.get("model", "unknown")
print(f"🤖 Using model: {model}")
def _handle_assistant_event(self, event: Dict[str, Any]) -> str:
"""Handle assistant event and return text delta."""
content = event.get("message", {}).get("content", [])
if content:
text = content[0].get("text", "")
if text and self.verbose:
# Show progress indicator
sys.stdout.write(".")
sys.stdout.flush()
return text
return ""
def _handle_tool_call_event(self,
event: Dict[str, Any],
subtype: Optional[str],
files_modified: List[str],
files_created: List[str],
files_read: List[str]) -> bool:
"""Handle tool call event. Returns True if tool completed."""
tool_call = event.get("tool_call", {})
if subtype == "started":
# Tool started
if "writeToolCall" in tool_call:
path = tool_call["writeToolCall"].get("args", {}).get("path", "unknown")
if self.verbose:
print(f"\n🔧 Writing: {path}")
elif "readToolCall" in tool_call:
path = tool_call["readToolCall"].get("args", {}).get("path", "unknown")
files_read.append(path)
if self.verbose:
print(f"\n📖 Reading: {path}")
return False
elif subtype == "completed":
# Tool completed
if "writeToolCall" in tool_call:
result = tool_call["writeToolCall"].get("result", {})
if "success" in result:
success = result["success"]
path = tool_call["writeToolCall"].get("args", {}).get("path", "unknown")
lines = success.get("linesCreated", 0)
if lines > 0:
files_created.append(path)
if self.verbose:
print(f" ✅ Created {lines} lines")
else:
files_modified.append(path)
if self.verbose:
lines_modified = success.get("linesModified", 0)
print(f" ✅ Modified {lines_modified} lines")
elif "readToolCall" in tool_call:
result = tool_call["readToolCall"].get("result", {})
if "success" in result and self.verbose:
lines = result["success"].get("totalLines", 0)
print(f" ✅ Read {lines} lines")
return True
return False
def _print_summary(self,
duration_ms: Optional[int],
tool_count: int,
chars_generated: int,
files_created: List[str],
files_modified: List[str]) -> None:
"""Print execution summary."""
print(f"\n\n🎯 Completed in {duration_ms}ms")
print(f"📊 Stats: {tool_count} tool calls, {chars_generated} chars generated")
if files_created:
print(f"📝 Created {len(files_created)} file(s)")
if files_modified:
print(f"✏️ Modified {len(files_modified)} file(s)")
def test_cursor_cli():
"""Test cursor CLI wrapper."""
import os
from pathlib import Path
api_key = os.getenv("CURSOR_API_KEY")
if not api_key:
print("❌ CURSOR_API_KEY not set")
return
project_root = Path.cwd()
cli = CursorCLI(project_root, api_key)
task = "List all Python files in tools/agent directory"
def progress_callback(event_type: str, event: Dict[str, Any]):
"""Example progress callback."""
if event_type == "tool_call" and event.get("subtype") == "started":
print(f"🔄 Tool started...")
result = cli.run_agent(
task_description=task,
force=False,
progress_callback=progress_callback
)
print(f"\n📋 Result: {result.status}")
print(f"📄 Output: {result.output[:200]}...")
if __name__ == "__main__":
test_cursor_cli()

View file

@ -0,0 +1,333 @@
"""
Planning Agent - Analyzes project and generates task lists.
"""
import sys
import json
from pathlib import Path
from datetime import datetime, timezone
from typing import Dict, Any, List
from config import AgentConfig
from cursor_cli_wrapper import CursorCLI, CursorResultStatus
class PlanningAgent:
"""Planning agent that generates task lists."""
def __init__(self, config: AgentConfig):
self.config = config
self.cursor_cli = CursorCLI(
project_root=config.project_root,
api_key=config.cursor_api_key,
model=config.cursor_model,
verbose=True
)
def run(self) -> int:
"""
Generate task list for component.
Returns: 0 on success, 1 on error
"""
print(f"🎯 Planning Agent for component: {self.config.component}")
print(f"📁 Project root: {self.config.project_root}")
try:
# Build planning prompt
prompt = self._build_planning_prompt()
# Run cursor agent to analyze and plan
print("\n🤖 Running Cursor Planning Agent...")
result = self.cursor_cli.run_agent(
task_description=prompt,
force=True, # Allow writing task_list.json
max_iterations=3
)
if result.status != CursorResultStatus.SUCCESS:
print(f"❌ Planning agent failed: {result.error}")
return 1
# Verify task list was generated
if not self.config.task_list_path.exists():
print(f"❌ Task list not generated: {self.config.task_list_path}")
return 1
# Validate and summarize task list
task_list = self._load_and_validate_task_list()
if not task_list:
print("❌ Invalid task list generated")
return 1
# Print summary
self._print_summary(task_list)
# Create summary commit
self._commit_task_list()
print("\n✅ Planning completed successfully")
return 0
except Exception as e:
print(f"\n❌ Planning error: {e}")
import traceback
traceback.print_exc()
return 1
def _build_planning_prompt(self) -> str:
"""Build comprehensive planning prompt."""
# Load planning prompt template
prompt_file = self.config.prompts_dir / "planning_prompt.md"
if prompt_file.exists():
with open(prompt_file, 'r') as f:
template = f.read()
else:
template = "You are an AI planning agent. Analyze the project and create a task list.\n\n"
# Add context from existing files
context = self._gather_context()
prompt = f"""{template}
## PLANNING REQUEST
Generate a task list for component: **{self.config.component}**
### Current Context:
{context}
### Task List Requirements:
1. Analyze the current state from tasks.md and workflow_state.md
2. Review what's already completed in agent_state.json
3. Create new tasks or update existing ones
4. Ensure proper dependencies and priorities
5. Write the complete task list to: `{self.config.task_list_path}`
### Output Format:
Write a JSON file at `{self.config.task_list_path}` with this structure:
```json
{{
"project": "mnemo_cards_{self.config.component}",
"component": "{self.config.component}",
"version": "1.0",
"generated_at": "{datetime.now(timezone.utc).isoformat()}",
"generated_by": "planning_agent",
"tasks": [
{{
"id": "TASK-001",
"title": "Clear title",
"priority": "high|medium|low",
"status": "pending",
"estimated_hours": 4.0,
"description": "Detailed description",
"acceptance_criteria": ["Criterion 1", "Criterion 2"],
"dependencies": [],
"files_to_modify": ["path/to/file.dart"],
"component": "{self.config.component}"
}}
]
}}
```
### Guidelines:
- Create 5-10 actionable tasks
- Prioritize based on business value and dependencies
- Each task should be 2-8 hours of work
- Include clear acceptance criteria
- Specify files to modify
- Set proper dependencies
Start planning now!
"""
return prompt
def _gather_context(self) -> str:
"""Gather context from existing files."""
context = ""
# Read tasks.md if exists
tasks_md = self.config.component_root / "tasks.md"
if not tasks_md.exists():
tasks_md = self.config.project_root / "mnemo_cards_web_v2" / "tasks.md"
if tasks_md.exists():
with open(tasks_md, 'r') as f:
content = f.read()
# Truncate if too long
if len(content) > 10000:
content = content[:10000] + "\n... (truncated)"
context += f"### tasks.md:\n```\n{content}\n```\n\n"
# Read workflow_state.md if exists
workflow_state = self.config.component_root / "workflow_state.md"
if not workflow_state.exists():
workflow_state = self.config.project_root / "mnemo_cards_web_v2" / "workflow_state.md"
if workflow_state.exists():
with open(workflow_state, 'r') as f:
content = f.read()
if len(content) > 5000:
content = content[:5000] + "\n... (truncated)"
context += f"### workflow_state.md:\n```\n{content}\n```\n\n"
# Read current task list if exists
if self.config.task_list_path.exists():
with open(self.config.task_list_path, 'r') as f:
current_tasks = json.load(f)
context += f"### Current task_list.json:\n```json\n{json.dumps(current_tasks, indent=2)}\n```\n\n"
# Read agent state
if self.config.agent_state_path.exists():
with open(self.config.agent_state_path, 'r') as f:
state = json.load(f)
context += f"### Current agent_state.json:\n```json\n{json.dumps(state, indent=2)}\n```\n\n"
if not context:
context = "No existing context files found. Create fresh task list based on project structure.\n"
return context
def _load_and_validate_task_list(self) -> Dict[str, Any]:
"""Load and validate generated task list."""
try:
with open(self.config.task_list_path, 'r') as f:
task_list = json.load(f)
# Validate structure
if "tasks" not in task_list:
print("❌ Task list missing 'tasks' field")
return None
tasks = task_list["tasks"]
if not isinstance(tasks, list):
print("'tasks' must be a list")
return None
# Validate each task
required_fields = [
"id", "title", "priority", "status",
"estimated_hours", "description",
"acceptance_criteria", "dependencies",
"files_to_modify", "component"
]
for i, task in enumerate(tasks):
for field in required_fields:
if field not in task:
print(f"❌ Task {i} missing required field: {field}")
return None
return task_list
except json.JSONDecodeError as e:
print(f"❌ Invalid JSON in task list: {e}")
return None
except Exception as e:
print(f"❌ Error loading task list: {e}")
return None
def _print_summary(self, task_list: Dict[str, Any]) -> None:
"""Print task list summary."""
tasks = task_list.get("tasks", [])
print(f"\n{'='*80}")
print(f"📋 Task List Summary for {self.config.component}")
print(f"{'='*80}")
print(f"Total tasks: {len(tasks)}")
# Count by priority
high_count = sum(1 for t in tasks if t.get("priority") == "high")
medium_count = sum(1 for t in tasks if t.get("priority") == "medium")
low_count = sum(1 for t in tasks if t.get("priority") == "low")
print(f"\nPriority breakdown:")
print(f" 🔴 HIGH: {high_count}")
print(f" 🟡 MEDIUM: {medium_count}")
print(f" 🟢 LOW: {low_count}")
# Total estimated hours
total_hours = sum(t.get("estimated_hours", 0) for t in tasks)
print(f"\nTotal estimated: {total_hours:.1f} hours")
# List high priority tasks
print(f"\n🔴 High Priority Tasks:")
for task in tasks:
if task.get("priority") == "high":
print(f" - {task['id']}: {task['title']} ({task['estimated_hours']}h)")
print(f"\n{'='*80}")
def _commit_task_list(self) -> None:
"""Commit the generated task list."""
import subprocess
# Add task list file
subprocess.run(
["git", "add", str(self.config.task_list_path)],
cwd=self.config.project_root
)
# Commit
commit_message = f"""plan({self.config.component}): Update task list
Generated by: Planning Agent
Timestamp: {datetime.now(timezone.utc).isoformat()}
Component: {self.config.component}
"""
result = subprocess.run(
["git", "commit", "-m", commit_message],
cwd=self.config.project_root,
capture_output=True,
text=True
)
if result.returncode == 0:
print("✅ Task list committed")
# Push
subprocess.run(
["git", "push"],
cwd=self.config.project_root
)
print("✅ Pushed to remote")
else:
print("⚠️ No changes to commit (task list unchanged)")
def main():
"""Main entry point."""
if len(sys.argv) < 2:
print("Usage: python planning_agent.py <component>")
print("Components: web_v2, backend, common")
sys.exit(1)
component = sys.argv[1]
# Load configuration
config = AgentConfig.from_env(component)
try:
config.validate()
except ValueError as e:
print(f"❌ Configuration error: {e}")
sys.exit(1)
# Create planning agent and run
agent = PlanningAgent(config)
exit_code = agent.run()
sys.exit(exit_code)
if __name__ == "__main__":
main()

298
tools/agent/task_manager.py Normal file
View file

@ -0,0 +1,298 @@
"""
Task and state management for AI agents.
"""
import json
import fcntl
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Optional, Any
from dataclasses import dataclass, asdict
@dataclass
class Task:
"""Represents a single task."""
id: str
title: str
priority: str # 'high', 'medium', 'low'
status: str # 'pending', 'in_progress', 'completed', 'failed', 'skipped'
estimated_hours: float
description: str
acceptance_criteria: List[str]
dependencies: List[str]
files_to_modify: List[str]
component: str = "web_v2"
def to_dict(self) -> Dict[str, Any]:
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "Task":
return cls(**data)
@dataclass
class AgentState:
"""Represents the current state of an agent."""
component: str
current_task_id: Optional[str]
iteration_count: int
max_iterations: int
started_at: Optional[str]
last_commit: Optional[str]
retry_count: int
max_retries: int
status: str # 'idle', 'in_progress', 'completed', 'error'
errors: List[str]
completed_tasks: List[str]
skipped_tasks: List[str]
def to_dict(self) -> Dict[str, Any]:
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "AgentState":
return cls(**data)
class TaskManager:
"""Manages tasks and agent state."""
def __init__(self, task_list_path: Path, agent_state_path: Path):
self.task_list_path = task_list_path
self.agent_state_path = agent_state_path
def _read_json_file(self, path: Path) -> Dict[str, Any]:
"""Read and parse JSON file with file locking."""
with open(path, 'r') as f:
fcntl.flock(f.fileno(), fcntl.LOCK_SH)
try:
return json.load(f)
finally:
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
def _write_json_file(self, path: Path, data: Dict[str, Any]) -> None:
"""Write JSON file with file locking."""
with open(path, 'w') as f:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
try:
json.dump(data, f, indent=2)
finally:
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
def load_tasks(self) -> List[Task]:
"""Load tasks from task list file."""
data = self._read_json_file(self.task_list_path)
return [Task.from_dict(task_data) for task_data in data.get("tasks", [])]
def save_tasks(self, tasks: List[Task]) -> None:
"""Save tasks to task list file."""
data = self._read_json_file(self.task_list_path)
data["tasks"] = [task.to_dict() for task in tasks]
self._write_json_file(self.task_list_path, data)
def load_state(self) -> AgentState:
"""Load agent state from file."""
data = self._read_json_file(self.agent_state_path)
return AgentState.from_dict(data)
def save_state(self, state: AgentState) -> None:
"""Save agent state to file."""
self._write_json_file(self.agent_state_path, state.to_dict())
def get_next_task(self) -> Optional[Task]:
"""Get next pending task by priority."""
tasks = self.load_tasks()
state = self.load_state()
# Filter pending tasks
pending_tasks = [
task for task in tasks
if task.status == "pending" and task.id not in state.skipped_tasks
]
if not pending_tasks:
return None
# Check dependencies
completed_task_ids = set(state.completed_tasks)
def can_start_task(task: Task) -> bool:
"""Check if task dependencies are satisfied."""
if not task.dependencies:
return True
# Parse dependencies (format: "component:TASK-ID" or "TASK-ID")
for dep in task.dependencies:
if ":" in dep:
dep_component, dep_id = dep.split(":", 1)
# TODO: Check other component's state
# For now, only check current component
if dep_component == state.component and dep_id not in completed_task_ids:
return False
else:
if dep not in completed_task_ids:
return False
return True
# Filter by dependencies
ready_tasks = [task for task in pending_tasks if can_start_task(task)]
if not ready_tasks:
return None
# Sort by priority: high > medium > low
priority_order = {"high": 0, "medium": 1, "low": 2}
ready_tasks.sort(key=lambda t: priority_order.get(t.priority, 3))
return ready_tasks[0]
def update_task_status(self, task_id: str, status: str) -> None:
"""Update task status."""
tasks = self.load_tasks()
for task in tasks:
if task.id == task_id:
task.status = status
break
self.save_tasks(tasks)
def mark_task_completed(self, task_id: str) -> None:
"""Mark task as completed."""
self.update_task_status(task_id, "completed")
state = self.load_state()
if task_id not in state.completed_tasks:
state.completed_tasks.append(task_id)
state.current_task_id = None
state.retry_count = 0
self.save_state(state)
def mark_task_failed(self, task_id: str, error: str) -> None:
"""Mark task as failed."""
self.update_task_status(task_id, "failed")
state = self.load_state()
state.errors.append(f"{task_id}: {error}")
state.current_task_id = None
self.save_state(state)
def mark_task_skipped(self, task_id: str, reason: str) -> None:
"""Mark task as skipped."""
self.update_task_status(task_id, "skipped")
state = self.load_state()
if task_id not in state.skipped_tasks:
state.skipped_tasks.append(task_id)
state.errors.append(f"{task_id} skipped: {reason}")
state.current_task_id = None
self.save_state(state)
def start_task(self, task: Task) -> None:
"""Mark task as started."""
self.update_task_status(task.id, "in_progress")
state = self.load_state()
state.current_task_id = task.id
state.status = "in_progress"
if not state.started_at:
state.started_at = datetime.now(timezone.utc).isoformat()
state.iteration_count += 1
self.save_state(state)
def increment_retry(self) -> int:
"""Increment retry count and return new value."""
state = self.load_state()
state.retry_count += 1
self.save_state(state)
return state.retry_count
def is_agent_running(self) -> bool:
"""Check if agent is currently running."""
state = self.load_state()
if state.status != "in_progress":
return False
# Check if started too long ago (stale lock)
if state.started_at:
started = datetime.fromisoformat(state.started_at)
now = datetime.now(timezone.utc)
hours_elapsed = (now - started).total_seconds() / 3600
if hours_elapsed > 2: # Consider stale after 2 hours
return False
return True
def reset_state(self) -> None:
"""Reset agent state to idle."""
state = self.load_state()
state.status = "idle"
state.current_task_id = None
state.started_at = None
state.iteration_count = 0
state.retry_count = 0
self.save_state(state)
def all_tasks_completed(self) -> bool:
"""Check if all tasks are completed."""
tasks = self.load_tasks()
return all(task.status in ["completed", "skipped"] for task in tasks)
class GlobalLock:
"""Manages global lock for shared resource access."""
def __init__(self, lock_file: Path):
self.lock_file = lock_file
def _read_lock(self) -> Dict[str, Any]:
"""Read lock file."""
with open(self.lock_file, 'r') as f:
fcntl.flock(f.fileno(), fcntl.LOCK_SH)
try:
return json.load(f)
finally:
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
def _write_lock(self, data: Dict[str, Any]) -> None:
"""Write lock file."""
with open(self.lock_file, 'w') as f:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
try:
json.dump(data, f, indent=2)
finally:
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
def acquire(self, component: str, reason: str = "Working on shared resources") -> bool:
"""Acquire global lock."""
lock_data = self._read_lock()
if lock_data.get("locked"):
return False
lock_data["locked"] = True
lock_data["locked_by"] = component
lock_data["locked_at"] = datetime.now(timezone.utc).isoformat()
lock_data["reason"] = reason
self._write_lock(lock_data)
return True
def release(self, component: str) -> None:
"""Release global lock."""
lock_data = self._read_lock()
if lock_data.get("locked_by") == component:
lock_data["locked"] = False
lock_data["locked_by"] = None
lock_data["locked_at"] = None
lock_data["reason"] = None
self._write_lock(lock_data)
def is_locked(self) -> bool:
"""Check if global lock is held."""
lock_data = self._read_lock()
return lock_data.get("locked", False)

View file

@ -0,0 +1,172 @@
# Fail2ban Whitelist Configuration
**Дата**: 19 ноября 2025
**Статус**: ✅ Настроено
## Проблема
Fail2ban блокировал IP администратора (89.19.210.178) при установке Flutter и интенсивном использовании VSCode Server.
## Решение
Добавлен IP администратора в whitelist (ignoreip) для всех VSCode jail'ов.
## Конфигурация
**Файл**: `/etc/fail2ban/jail.d/vscode.conf`
```ini
[vscode]
enabled = true
port = http,https
filter = vscode
logpath = /var/log/nginx/access.log
maxretry = 3
bantime = 3600
findtime = 600
ignoreip = 127.0.0.1/8 ::1 89.19.210.178
[vscode-ddos]
enabled = true
port = http,https
filter = vscode-ddos
logpath = /var/log/nginx/access.log
maxretry = 100
bantime = 600
findtime = 60
ignoreip = 127.0.0.1/8 ::1 89.19.210.178
```
## Что означает ignoreip
- `127.0.0.1/8` - локальные IP адреса (localhost)
- `::1` - IPv6 localhost
- `89.19.210.178` - ваш текущий внешний IP
## Параметры защиты
### vscode jail
- **maxretry**: 3 попытки
- **bantime**: 3600 секунд (1 час)
- **findtime**: 600 секунд (10 минут)
Блокирует за 3 неудачные попытки входа в течение 10 минут на 1 час.
### vscode-ddos jail
- **maxretry**: 100 запросов
- **bantime**: 600 секунд (10 минут)
- **findtime**: 60 секунд (1 минута)
Блокирует за более 100 запросов в минуту на 10 минут (защита от DDoS).
## Проверка
### Текущий статус
```bash
ssh root@147.45.152.129 'fail2ban-client status vscode-ddos'
```
**Результат**:
```
Status for the jail: vscode-ddos
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
```
✅ Нет заблокированных IP
### Проверка whitelist
```bash
ssh root@147.45.152.129 'fail2ban-client get vscode-ddos ignoreip'
```
Должно показать: `127.0.0.1/8 ::1 89.19.210.178`
## Добавление нового IP в whitelist
Если ваш IP изменится или нужно добавить другой IP:
```bash
ssh root@147.45.152.129
# Редактировать конфигурацию
nano /etc/fail2ban/jail.d/vscode.conf
# Добавить новый IP в строку ignoreip через пробел
ignoreip = 127.0.0.1/8 ::1 89.19.210.178 NEW_IP_HERE
# Перезапустить fail2ban
systemctl restart fail2ban
# Проверить статус
systemctl status fail2ban
```
## Разблокировка IP вручную
Если всё же кто-то был заблокирован:
```bash
# Посмотреть заблокированные IP
ssh root@147.45.152.129 'fail2ban-client status vscode-ddos'
# Разблокировать конкретный IP
ssh root@147.45.152.129 'fail2ban-client set vscode-ddos unbanip IP_ADDRESS'
# Пример
ssh root@147.45.152.129 'fail2ban-client set vscode-ddos unbanip 89.19.210.178'
```
## Логи
### Просмотр логов fail2ban
```bash
ssh root@147.45.152.129 'tail -f /var/log/fail2ban.log'
```
### Просмотр nginx access log
```bash
ssh root@147.45.152.129 'tail -f /var/log/nginx/access.log | grep vscode'
```
## Backup
Создан backup оригинальной конфигурации:
```
/etc/fail2ban/jail.d/vscode.conf.backup
```
Для восстановления:
```bash
ssh root@147.45.152.129 'cp /etc/fail2ban/jail.d/vscode.conf.backup /etc/fail2ban/jail.d/vscode.conf && systemctl restart fail2ban'
```
## Безопасность
**Fail2ban продолжает работать** - защита от атак активна
**Ваш IP в whitelist** - вас больше не будет блокировать
**Другие IP защищены** - атаки с других адресов будут блокироваться
## Проверка после изменений
Все сервисы работают:
| Сервис | URL | Статус |
|--------|-----|--------|
| Forgejo | https://code.mnemo-cards.online/ | 🟢 Доступен |
| VSCode | https://vscode.mnemo-cards.online/ | 🟢 Доступен |
| Backend API | https://api.mnemo-cards.online/ | 🟢 Доступен |
## Связанные документы
- [VSCODE_SERVER_SETUP.md](./VSCODE_SERVER_SETUP.md) - Настройка VSCode Server
- [RECOVERY_REPORT_2025-11-19.md](./RECOVERY_REPORT_2025-11-19.md) - Восстановление после перезагрузки
---
**Вывод**: Ваш IP (89.19.210.178) добавлен в whitelist. Fail2ban больше вас не заблокирует, но продолжит защищать сервер от атак с других IP адресов.