Compare commits
3
Commits
2ddf2251d6
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cc8e94863 | ||
|
|
5afea8238c | ||
|
|
940d25435e |
@@ -0,0 +1,78 @@
|
|||||||
|
# Analysis Report
|
||||||
|
|
||||||
|
## Session
|
||||||
|
|
||||||
|
- **Session ID:** `metaagent-002`
|
||||||
|
- **Target repo:** `S:\Git\nifodea`
|
||||||
|
- **Date:** 2026-07-22
|
||||||
|
- **Project type:** `existing`
|
||||||
|
|
||||||
|
## 1. Общая информация
|
||||||
|
|
||||||
|
- **README:** Личная финансовая модель с прогнозом денежных потоков, сценарным анализом и AI-ассистентом. Python + JSON + Excel + AI.
|
||||||
|
- **Лицензия:** не выбрана
|
||||||
|
- **CI/CD:** отсутствует
|
||||||
|
- **Точка входа:** `cli/main.py` (команда `cf`)
|
||||||
|
- **Система сборки:** `pyproject.toml` (setuptools)
|
||||||
|
|
||||||
|
## 2. Стек технологий
|
||||||
|
|
||||||
|
| Компонент | Значение |
|
||||||
|
|---|---|
|
||||||
|
| Язык | Python >= 3.11 |
|
||||||
|
| Фреймворк | Typer (CLI) |
|
||||||
|
| База данных | JSON-файлы |
|
||||||
|
| Тестовый раннер | pytest |
|
||||||
|
| Пакетный менеджер | pip (setuptools) |
|
||||||
|
| Линтер/форматтер | ruff |
|
||||||
|
|
||||||
|
## 3. Архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
cashflow_model/ # Модели данных (dataclass + JSON)
|
||||||
|
engine/ # Вычислительное ядро (forecast + scenarios)
|
||||||
|
sync/ # Excel import/export
|
||||||
|
ai/ # AI-ассистент (заглушка)
|
||||||
|
cli/ # CLI (Typer)
|
||||||
|
tests/ # pytest
|
||||||
|
data/ # JSON-модели
|
||||||
|
exports/ # Экспортированные .xlsx
|
||||||
|
```
|
||||||
|
|
||||||
|
**Паттерн:** Модульный монолит
|
||||||
|
|
||||||
|
**Ключевые модули:**
|
||||||
|
|
||||||
|
| Модуль | Описание |
|
||||||
|
|---|---|
|
||||||
|
| cashflow_model | Модели данных: Account, Transaction, RecurringCashflow, Asset, Liability, ForecastScenario, FinancialModel |
|
||||||
|
| engine | Вычислительное ядро: ForecastService (прогноз), ScenarioService (сценарии + what-if) |
|
||||||
|
| sync | ExcelSync — импорт/экспорт .xlsx |
|
||||||
|
| ai | AssistantService — генерация промптов (заглушка) |
|
||||||
|
| cli | Typer CLI — 8 команд |
|
||||||
|
|
||||||
|
## 4. Конвенции
|
||||||
|
|
||||||
|
- **Стиль:** snake_case для функций/переменных, PascalCase для классов
|
||||||
|
- **Импорты:** стандартные, сгруппированные
|
||||||
|
- **Типизация:** используется (dataclass, type hints)
|
||||||
|
- **Обработка ошибок:** через исключения
|
||||||
|
- **Логирование:** не используется
|
||||||
|
|
||||||
|
## 5. Тесты
|
||||||
|
|
||||||
|
- **Команда запуска:** `pytest`
|
||||||
|
- **Всего тестов:** 26
|
||||||
|
- **Пройдено:** 26
|
||||||
|
- **Упало:** 0
|
||||||
|
- **Пропущено:** 0
|
||||||
|
|
||||||
|
## 6. Базовая проверка
|
||||||
|
|
||||||
|
- **Сборка:** OK (pip install -e .)
|
||||||
|
- **Линтер:** OK (ruff check . — all checks passed)
|
||||||
|
- **Git status:** есть незакоммиченные изменения (checkpoints.json, metaagent-request.md, AGENTS.md, data/model.json, .agent/rules/)
|
||||||
|
|
||||||
|
## 8. Примечания
|
||||||
|
|
||||||
|
Проект полностью функционален: 26 тестов проходят, линтер чист. Требуется только обновление MetaAgent-артефактов до v1.1.0.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"metaagent_version": "1.1.0",
|
||||||
|
"session_id": "metaagent-002",
|
||||||
|
"target_repo": "S:\\Git\\nifodea",
|
||||||
|
"goal": "Обновление metaagent-артефактов до v1.0.0, валидация существующего кода и окружения",
|
||||||
|
"project_type": "existing",
|
||||||
|
"config": {
|
||||||
|
"depth": 4,
|
||||||
|
"design": { "adr": false, "alternative_arch": false },
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": false,
|
||||||
|
"decomposition": { "invariant_tests": false },
|
||||||
|
"handoff": { "layer_structure": false }
|
||||||
|
},
|
||||||
|
"phases": {
|
||||||
|
"analysis": "completed",
|
||||||
|
"design": "skipped",
|
||||||
|
"red_team": "skipped",
|
||||||
|
"decomposition": "completed",
|
||||||
|
"environment": "completed",
|
||||||
|
"handoff": "completed"
|
||||||
|
},
|
||||||
|
"tasks": [
|
||||||
|
{ "id": "T1", "title": "Инициализация проекта и зависимостей", "status": "completed", "depends_on": [], "acceptance_criteria": ["pyproject.toml создан", "Все __init__.py созданы", "ruff проходит", "pytest запускается"] },
|
||||||
|
{ "id": "T2", "title": "Модель данных (dataclass + JSON)", "status": "completed", "depends_on": ["T1"], "acceptance_criteria": ["Все сущности dataclass", "FinancialModel save/load JSON"] },
|
||||||
|
{ "id": "T3", "title": "Forecast Engine", "status": "completed", "depends_on": ["T2"], "acceptance_criteria": ["forecast_cashflow работает", "recurring проецируются", "активы/обязательства учтены"] },
|
||||||
|
{ "id": "T4", "title": "Scenario Analysis", "status": "completed", "depends_on": ["T3"], "acceptance_criteria": ["3 сценария", "what-if модификация", "сравнение сценариев"] },
|
||||||
|
{ "id": "T5", "title": "Excel Sync", "status": "completed", "depends_on": ["T2"], "acceptance_criteria": ["импорт из Excel", "экспорт в Excel", "ошибки невалидного формата"] },
|
||||||
|
{ "id": "T6", "title": "CLI (Typer)", "status": "completed", "depends_on": ["T2","T3","T4","T5","T7"], "acceptance_criteria": ["init/forecast/analyze/import/export/scenario/whatif/compare команды"] },
|
||||||
|
{ "id": "T7", "title": "AI Assistant", "status": "completed", "depends_on": ["T3"], "acceptance_criteria": ["промпты с моделью и прогнозом", "заглушка ответа"] },
|
||||||
|
{ "id": "T8", "title": "Тесты", "status": "completed", "depends_on": ["T2","T3","T4","T5","T6","T7"], "acceptance_criteria": ["pytest проходит", "покрытие всех модулей"] }
|
||||||
|
],
|
||||||
|
"last_updated": "2026-07-22T12:00:00Z"
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
# Design Report
|
||||||
|
|
||||||
|
## Session
|
||||||
|
|
||||||
|
- **Session ID:** `metaagent-001`
|
||||||
|
- **Target repo:** `S:\Git\nifodea`
|
||||||
|
- **Date:** 2026-07-12
|
||||||
|
|
||||||
|
## 1. Технологический стек
|
||||||
|
|
||||||
|
| Компонент | Выбор | Обоснование |
|
||||||
|
|---|---|---|
|
||||||
|
| Язык | Python 3.11+ | Указан в README как вычислительное ядро; широкая экосистема для работы с данными |
|
||||||
|
| Фреймворк | Typer (CLI), openpyxl (Excel) | Typer — современный CLI-фреймворк; openpyxl — стандарт для .xlsx |
|
||||||
|
| База данных | JSON-файлы | README требует JSON как внутреннее представление; для MVP БД не нужна |
|
||||||
|
| Инфраструктура | pip + venv | Минимальная зависимость; .gitignore уже настроен под Python |
|
||||||
|
| Линтер | ruff | Стандарт для Python 2024+; быстрый, уже в .gitignore |
|
||||||
|
| Тесты | pytest | Стандартный тестовый раннер для Python |
|
||||||
|
| AI | Интерфейс через промпты | Для MVP — только промпты и абстракция, без подключения к API |
|
||||||
|
|
||||||
|
## 2. High-Level архитектура
|
||||||
|
|
||||||
|
**Паттерн:** Модульный монолит (Layered)
|
||||||
|
|
||||||
|
```
|
||||||
|
[CLI / Excel File]
|
||||||
|
|
|
||||||
|
▼
|
||||||
|
sync/ ──► cashflow_model/ ──► engine/ ──► ai/
|
||||||
|
(Excel R/W) (Entity Model) (Forecast) (Prompts)
|
||||||
|
| | |
|
||||||
|
▼ ▼ ▼
|
||||||
|
data/model.json data/model.json data/model.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Поток данных:**
|
||||||
|
1. Пользователь редактирует Excel → sync читает и преобразует в JSON
|
||||||
|
2. JSON-модель загружается в Python-объекты (dataclass)
|
||||||
|
3. Forecast Engine вычисляет прогноз на основе модели
|
||||||
|
4. AI Assistant анализирует результаты через промпты
|
||||||
|
5. Результаты экспортируются обратно в Excel
|
||||||
|
|
||||||
|
## 3. Модули
|
||||||
|
|
||||||
|
| Модуль | Ответственность | Ключевые компоненты | Зависит от |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `cashflow_model/` | Определение сущностей (dataclass), сериализация/десериализация JSON | `Account`, `Transaction`, `RecurringCashflow`, `Asset`, `Liability`, `ForecastScenario`, `FinancialModel` | — |
|
||||||
|
| `sync/` | Чтение и запись Excel (.xlsx), конвертация между Excel и JSON | `excel_sync.py` — импорт/экспорт | `cashflow_model` |
|
||||||
|
| `engine/` | Расчёт прогноза, сценарный анализ, what-if | `forecast.py` (прогноз), `scenarios.py` (сценарии) | `cashflow_model` |
|
||||||
|
| `ai/` | Промпты для AI-ассистента, форматирование контекста | `prompts.py` (шаблоны), `assistant.py` (интерфейс) | `cashflow_model`, `engine` |
|
||||||
|
| `cli/` | CLI-интерфейс (Typer) | `main.py` — точки входа | Все модули |
|
||||||
|
|
||||||
|
## 4. Модели данных
|
||||||
|
|
||||||
|
### Account
|
||||||
|
|
||||||
|
| Поле | Тип | Ограничения | Описание |
|
||||||
|
|---|---|---|---|
|
||||||
|
| id | UUID | pk | Уникальный идентификатор |
|
||||||
|
| name | str | required | Название счёта |
|
||||||
|
| currency | str | default="USD" | Валюта |
|
||||||
|
| balance | float | required | Текущий баланс |
|
||||||
|
|
||||||
|
### Transaction
|
||||||
|
|
||||||
|
| Поле | Тип | Ограничения | Описание |
|
||||||
|
|---|---|---|---|
|
||||||
|
| id | UUID | pk | Уникальный идентификатор |
|
||||||
|
| date | str (ISO date) | required | Дата операции |
|
||||||
|
| account | UUID | fk → Account | Счёт |
|
||||||
|
| category | str | required | Категория |
|
||||||
|
| amount | float | required | Сумма |
|
||||||
|
| description | str | optional | Описание |
|
||||||
|
|
||||||
|
### RecurringCashflow
|
||||||
|
|
||||||
|
| Поле | Тип | Ограничения | Описание |
|
||||||
|
|---|---|---|---|
|
||||||
|
| id | UUID | pk | Уникальный идентификатор |
|
||||||
|
| start_date | str (ISO date) | required | Дата начала |
|
||||||
|
| end_date | str (ISO date) | optional | Дата окончания |
|
||||||
|
| frequency | str | enum: monthly/weekly/yearly | Периодичность |
|
||||||
|
| amount | float | required | Сумма |
|
||||||
|
| category | str | required | Категория |
|
||||||
|
|
||||||
|
### Asset
|
||||||
|
|
||||||
|
| Поле | Тип | Ограничения | Описание |
|
||||||
|
|---|---|---|---|
|
||||||
|
| id | UUID | pk | Уникальный идентификатор |
|
||||||
|
| name | str | required | Название |
|
||||||
|
| value | float | required | Текущая стоимость |
|
||||||
|
| growth_rate | float | default=0.0 | Годовой темп роста (%) |
|
||||||
|
|
||||||
|
### Liability
|
||||||
|
|
||||||
|
| Поле | Тип | Ограничения | Описание |
|
||||||
|
|---|---|---|---|
|
||||||
|
| id | UUID | pk | Уникальный идентификатор |
|
||||||
|
| name | str | required | Название |
|
||||||
|
| balance | float | required | Текущий остаток |
|
||||||
|
| interest | float | required | Годовая ставка (%) |
|
||||||
|
| payment | float | required | Ежемесячный платёж |
|
||||||
|
|
||||||
|
**Связи:**
|
||||||
|
- Transaction → Account (многие к одному)
|
||||||
|
- RecurringCashflow → Account (многие к одному, опционально)
|
||||||
|
- FinancialModel включает все сущности + параметры
|
||||||
|
|
||||||
|
## 5. API / Интерфейсы
|
||||||
|
|
||||||
|
### CLI (Typer)
|
||||||
|
|
||||||
|
| Команда | Описание | Пример |
|
||||||
|
|---|---|---|
|
||||||
|
| `import <file.xlsx>` | Импорт данных из Excel в JSON | `cf import data.xlsx` |
|
||||||
|
| `export <file.xlsx>` | Экспорт из JSON в Excel | `cf export report.xlsx` |
|
||||||
|
| `forecast [--months 12]` | Запуск прогноза | `cf forecast --months 12` |
|
||||||
|
| `scenario <name>` | Применить сценарий | `cf scenario optimistic` |
|
||||||
|
| `analyze` | AI-анализ модели | `cf analyze` |
|
||||||
|
| `init` | Инициализация пустой модели | `cf init` |
|
||||||
|
|
||||||
|
## 6. Обработка ошибок
|
||||||
|
|
||||||
|
- **Стратегия:** Исключения Python с кастомными типами (`ModelError`, `SyncError`, `ForecastError`)
|
||||||
|
- **Формат ошибок:** `{ "error": "<message>", "code": "<CODE>", "details": {} }`
|
||||||
|
- **Логирование:** logging с уровнями INFO/ERROR; CLI-вывод через Typer + rich
|
||||||
|
|
||||||
|
## 7. Тестирование
|
||||||
|
|
||||||
|
- **Unit-тесты:** pytest для каждого модуля (cashflow_model, engine, sync)
|
||||||
|
- **Integration-тесты:** чтение/запись Excel, полный цикл import → forecast → export
|
||||||
|
- **Mock-стратегия:** временные файлы для Excel/JSON тестов
|
||||||
|
- **Команда запуска:** `pytest`
|
||||||
|
|
||||||
|
## 8. Предварительная группировка задач
|
||||||
|
|
||||||
|
| Задача | Описание | Тип |
|
||||||
|
|---|---|---|
|
||||||
|
| T1 | Инициализация проекта + scaffold | config |
|
||||||
|
| T2 | Модель данных (dataclass + JSON serialization) | feature |
|
||||||
|
| T3 | Forecast Engine (базовый прогноз) | feature |
|
||||||
|
| T4 | Excel Sync (import/export) | feature |
|
||||||
|
| T5 | CLI (Typer) — все команды | feature |
|
||||||
|
| T6 | AI Assistant (промпты + интерфейс) | feature |
|
||||||
|
| T7 | Тесты на все модули | test |
|
||||||
|
| T8 | Финальная проверка и документация | docs |
|
||||||
|
|
||||||
|
## 9. Примечания
|
||||||
|
|
||||||
|
Для MVP берётся минимальный функционал: модель + forecast + excel sync + cli. AI — только интерфейс (заглушка с промптами). Сценарии — базовая реализация.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# Handoff Summary
|
||||||
|
|
||||||
|
## Session Info
|
||||||
|
|
||||||
|
- **Session ID:** `metaagent-002`
|
||||||
|
- **Target Repo:** `S:\Git\nifodea`
|
||||||
|
- **Goal:** Обновление metaagent-артефактов до v1.1.0, валидация существующего кода и окружения
|
||||||
|
- **Date:** 2026-07-22
|
||||||
|
- **Depth:** 4 (Light)
|
||||||
|
- **Config:** depth=4, design=skipped (existing), red_team=no, risk_register=no, invariant_tests=no, layer_structure=no
|
||||||
|
|
||||||
|
## Repo Summary
|
||||||
|
|
||||||
|
CashFlow Forecast — личная финансовая модель на Python. Модульный монолит: cashflow_model (dataclass), engine (forecast + scenarios), sync (Excel), ai (заглушка), cli (Typer). 26 тестов, ruff lint чист.
|
||||||
|
|
||||||
|
## Project Type
|
||||||
|
|
||||||
|
- **Type:** existing
|
||||||
|
- **Design report:** —
|
||||||
|
|
||||||
|
## Environment Status
|
||||||
|
|
||||||
|
- **Build:** OK
|
||||||
|
- **Tests:** 26/26 passed
|
||||||
|
- **Linter:** ruff — all checks passed
|
||||||
|
- **Dependencies:** установлены (openpyxl, typer, rich)
|
||||||
|
|
||||||
|
## Task Overview
|
||||||
|
|
||||||
|
| Status | Count |
|
||||||
|
|---|---|
|
||||||
|
| Total | 8 |
|
||||||
|
| Pending | 0 |
|
||||||
|
| In Progress | 0 |
|
||||||
|
| Completed | 8 |
|
||||||
|
| Failed/Skipped | 0 |
|
||||||
|
|
||||||
|
**Task by type:**
|
||||||
|
- config: 1
|
||||||
|
- feature: 6
|
||||||
|
- test: 1
|
||||||
|
|
||||||
|
## Tasks (ordered)
|
||||||
|
|
||||||
|
### T1: Инициализация проекта и зависимостей
|
||||||
|
- Type: config
|
||||||
|
- Depends on: —
|
||||||
|
- Files: pyproject.toml, cashflow_model/__init__.py, sync/__init__.py, engine/__init__.py, ai/__init__.py, cli/__init__.py, data/.gitkeep, exports/.gitkeep
|
||||||
|
- Status: completed
|
||||||
|
|
||||||
|
### T2: Модель данных (dataclass + JSON serialization)
|
||||||
|
- Type: feature
|
||||||
|
- Depends on: T1
|
||||||
|
- Files: cashflow_model/__init__.py, cashflow_model/account.py, cashflow_model/transaction.py, cashflow_model/recurring.py, cashflow_model/asset.py, cashflow_model/liability.py, cashflow_model/scenario.py, cashflow_model/model.py
|
||||||
|
- Status: completed
|
||||||
|
|
||||||
|
### T3: Forecast Engine (базовый прогноз)
|
||||||
|
- Type: feature
|
||||||
|
- Depends on: T2
|
||||||
|
- Files: engine/__init__.py, engine/forecast.py
|
||||||
|
- Status: completed
|
||||||
|
|
||||||
|
### T4: Scenario Analysis
|
||||||
|
- Type: feature
|
||||||
|
- Depends on: T3
|
||||||
|
- Files: engine/__init__.py, engine/scenarios.py
|
||||||
|
- Status: completed
|
||||||
|
|
||||||
|
### T5: Excel Sync (import/export)
|
||||||
|
- Type: feature
|
||||||
|
- Depends on: T2
|
||||||
|
- Files: sync/__init__.py, sync/excel_sync.py
|
||||||
|
- Status: completed
|
||||||
|
|
||||||
|
### T6: CLI (Typer) — все команды
|
||||||
|
- Type: feature
|
||||||
|
- Depends on: T2, T3, T4, T5, T7
|
||||||
|
- Files: cli/__init__.py, cli/main.py, pyproject.toml
|
||||||
|
- Status: completed
|
||||||
|
|
||||||
|
### T7: AI Assistant (промпты + интерфейс)
|
||||||
|
- Type: feature
|
||||||
|
- Depends on: T3
|
||||||
|
- Files: ai/__init__.py, ai/prompts.py, ai/assistant.py
|
||||||
|
- Status: completed
|
||||||
|
|
||||||
|
### T8: Тесты на все модули
|
||||||
|
- Type: test
|
||||||
|
- Depends on: T2, T3, T4, T5, T6, T7
|
||||||
|
- Files: tests/test_model.py, tests/test_forecast.py, tests/test_scenarios.py, tests/test_excel_sync.py, tests/test_cli.py, tests/test_ai.py, tests/conftest.py
|
||||||
|
- Status: completed
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
Все 8 задач выполнены. Проект готов к использованию.
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
- AI-ассистент — заглушка (промпты готовы, API не подключено)
|
||||||
|
- База данных — JSON-файлы (не подходит для многопользовательской работы)
|
||||||
|
- Лицензия не выбрана
|
||||||
|
- CI/CD не настроен
|
||||||
|
|
||||||
|
## Checkpoints
|
||||||
|
|
||||||
|
Файл: `.agent/checkpoints.json`
|
||||||
|
Актуальное состояние чекпоинтов прилагается.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# MetaAgent Request
|
||||||
|
# Auto-generated from existing checkpoints.json on 2026-07-22
|
||||||
|
|
||||||
|
## Параметры сессии
|
||||||
|
|
||||||
|
| Функция | Вкл | Аргументы |
|
||||||
|
|---|---|---|
|
||||||
|
| ANALYSIS | ✓ | — |
|
||||||
|
| DESIGN | ✗ | project_type=existing |
|
||||||
|
| RED_TEAM | ✗ | — |
|
||||||
|
| RISK_REGISTER | ✗ | — |
|
||||||
|
| DECOMPOSITION | ✓ | invariant_tests=false |
|
||||||
|
| SETUP | ✓ | — |
|
||||||
|
| HANDOFF | ✓ | layer_structure=false |
|
||||||
|
|
||||||
|
## Глубина проработки
|
||||||
|
|
||||||
|
**Значение:** 4 (Light)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Обновление metaagent-артефактов до v1.1.0, валидация существующего кода и окружения
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Project Rules
|
||||||
|
|
||||||
|
Правила, которым агент обязан следовать во всех фазах.
|
||||||
|
Добавляйте сюда условия, которые должны соблюдаться всегда — они будут прочитаны
|
||||||
|
перед началом каждой фазы и учтены при декомпозиции и реализации.
|
||||||
|
|
||||||
|
## Обязательные правила
|
||||||
|
|
||||||
|
- Всегда читать `.agent/rules/project-rules.md` перед каждой фазой
|
||||||
|
- Следовать протоколам MetaAgent строго последовательно
|
||||||
|
|
||||||
|
## Запреты
|
||||||
|
|
||||||
|
- Не писать production-код (это работа исполнительного агента)
|
||||||
|
- Не удалять файлы
|
||||||
|
- Не коммитить в main/master
|
||||||
|
|
||||||
|
## Конвенции проекта
|
||||||
|
|
||||||
|
- Python-проект: snake_case для функций/переменных, PascalCase для классов
|
||||||
|
- Использовать ruff для линтинга
|
||||||
|
- Тесты через pytest
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# BOUNDARIES — Рамки и границы
|
||||||
|
|
||||||
|
Что мета-агенту **разрешено**, **запрещено** и в каких случаях **нужно остановиться**.
|
||||||
|
|
||||||
|
## Разрешено
|
||||||
|
|
||||||
|
| Действие | Примечание |
|
||||||
|
|---|---|
|
||||||
|
| Читать любые файлы в целевом репозитории | Все файлы, включая .git, конфиги, историю |
|
||||||
|
| Создавать/изменять файлы в `.agent/` | Единственная директория для артефактов |
|
||||||
|
| Устанавливать/обновлять зависимости | Только через штатный пакетный менеджер проекта |
|
||||||
|
| Изменять конфигурационные файлы | Только если это необходимо для сборки/тестов (например, добавить requirements.txt) |
|
||||||
|
| Запускать сборку и тесты | Для верификации окружения |
|
||||||
|
| Читать документацию, issue, PRs | Для понимания контекста |
|
||||||
|
| Запрашивать уточнения у пользователя | Если не хватает информации для декомпозиции |
|
||||||
|
| Копировать исходники MetaAgent в `.agent/src/` целевого проекта | Только на фазе INIT, без перезаписи существующих файлов |
|
||||||
|
| Создавать/обновлять `AGENTS.md` в корне целевого проекта | Только если файла не существует |
|
||||||
|
| **Обязательно:** читать `.agent/rules/project-rules.md` перед каждой фазой | Исполнение правил пользователя — приоритет выше стандартных протоколов |
|
||||||
|
| Перемещать завершённые артефакты в `.agent/archive/` | Только на фазе HANDOFF, только для completed/failed артефактов |
|
||||||
|
|
||||||
|
## Запрещено
|
||||||
|
|
||||||
|
| Действие | Почему |
|
||||||
|
|---|---|
|
||||||
|
| Писать production-код | Это работа исполнительного агента |
|
||||||
|
| Рефакторить существующий код | Мета-агент не меняет логику |
|
||||||
|
| Удалять файлы | Если файл мешает — нужно сообщить пользователю |
|
||||||
|
| Коммитить в main/master | Коммиты делает исполнительный агент по задачам |
|
||||||
|
| Менять удалённые настройки CI/CD | Если CI сломан — сообщить пользователю |
|
||||||
|
| Пул-реквесты | Исполнительный агент создаёт PR после выполнения задач |
|
||||||
|
| Модифицировать код, не связанный с задачей | Только то, что нужно для окружения |
|
||||||
|
|
||||||
|
## Когда остановиться
|
||||||
|
|
||||||
|
1. **Репозиторий не собирается** — сообщить пользователю с логом ошибки, не продолжать
|
||||||
|
2. **Неясна цель** — запросить уточнение, не гадать
|
||||||
|
3. **Обнаружены секреты/токены** — не копировать, сообщить пользователю
|
||||||
|
4. **Цель выходит за рамки одной сессии** — разбить, запросить приоритет
|
||||||
|
5. **Проект не использует известные технолологии** — запросить у пользователя инструкцию по сборке
|
||||||
@@ -0,0 +1,336 @@
|
|||||||
|
# META_AGENT_GUIDE — Главная инструкция
|
||||||
|
|
||||||
|
## Жизненный цикл сессии
|
||||||
|
|
||||||
|
```
|
||||||
|
.agent/metaagent-request.md
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
INIT → ANALYSE → [DESIGN] → [RED_TEAM] → DECOMPOSITION → SETUP → (CHECKPOINT)* → HANDOFF → EXIT
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
ADR (опц.) Invariant Tasks (опц.)
|
||||||
|
Alt.Arch (опц.)
|
||||||
|
Risk Register (опц.)
|
||||||
|
```
|
||||||
|
|
||||||
|
Фазы выполняются **строго последовательно**. Фаза DESIGN — только если project_type = greenfield/scaffold.
|
||||||
|
Фаза RED_TEAM — только если config.red_team = yes.
|
||||||
|
|
||||||
|
Все артефакты размещаются в `.agent/` целевого репозитория (с layer-структурой или плоские, в зависимости от config).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Конфигурация сессии (.agent/metaagent-request.md)
|
||||||
|
|
||||||
|
Перед запуском сессии пользователь заполняет `.agent/metaagent-request.md` (см. `TEMPLATES/metaagent-request.md`). Файл должен находиться в директории `.agent/` целевого репозитория.
|
||||||
|
|
||||||
|
Ключевые параметры:
|
||||||
|
|
||||||
|
### Шкала глубины (depth 1-10)
|
||||||
|
|
||||||
|
| Уровень | Название | Что выполняется |
|
||||||
|
|---|---|---|
|
||||||
|
| 1-2 | Scaffold | INIT → ANALYSIS → SETUP (только структура, без реализации) |
|
||||||
|
| 3-4 | Light | + DESIGN (без ADR/альтернатив), DECOMPOSITION (без инвариантов), HANDOFF — **(default)** |
|
||||||
|
| 5-6 | Standard | полный цикл с базовым DESIGN и DECOMPOSITION |
|
||||||
|
| 7-8 | Deep | + ADR, Alternative Architecture, Risk Register, Invariant Tests |
|
||||||
|
| 9-10 | Maximum | + Red Team Review, Executable Invariants для всех ADR |
|
||||||
|
|
||||||
|
### Функции (таблица вкл/выкл)
|
||||||
|
|
||||||
|
| Функция | Фаза | Глубина | Описание |
|
||||||
|
|---|---|---|---|
|
||||||
|
| adr | DESIGN | >=7 | Создание ADR для каждого ключевого решения |
|
||||||
|
| alternative_arch | DESIGN | >=7 | Обязательное описание альтернативной архитектуры |
|
||||||
|
| red_team | DESIGN (после) | >=9 | Red Team Review — попытка разрушить архитектуру |
|
||||||
|
| risk_register | DESIGN | >=7 | Явный реестр допущений |
|
||||||
|
| invariant_tests | DECOMPOSITION | >=7 | Задачи-инварианты для каждого ADR |
|
||||||
|
| layer_structure | HANDOFF | любая | Организация .agent/ по слоям (layer-0..3) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 0: INIT
|
||||||
|
|
||||||
|
**Вход:** целевой репозиторий + опционально `.agent/metaagent-request.md`.
|
||||||
|
|
||||||
|
**Протокол:** `PROTOCOLS/00_CONFIG.md`
|
||||||
|
|
||||||
|
**Действия:**
|
||||||
|
- Прочитать `VERSION` — текущая версия MetaAgent
|
||||||
|
- Склонировать/открыть целевой репозиторий
|
||||||
|
- Создать директорию `.agent/` в корне целевого репозитория (если нет)
|
||||||
|
- **Установить исходники MetaAgent в `.agent/src/`:**
|
||||||
|
- Скопировать `META_AGENT_GUIDE.md`, `BOUNDARIES.md`, `WORKFLOW.md`, `VERSION` в `.agent/src/`
|
||||||
|
- Скопировать `PROTOCOLS/` и `TEMPLATES/` в `.agent/src/`
|
||||||
|
- Скопировать `install.sh` и `install.ps1` в `.agent/src/` (для возможности обновления)
|
||||||
|
- Если файлы уже существуют — пропустить (не перезаписывать)
|
||||||
|
- **Создать `.agent/rules/`** — директорию для пользовательских правил
|
||||||
|
- Если `.agent/rules/project-rules.md` не существует — создать из шаблона `.agent/src/TEMPLATES/project-rules.md`
|
||||||
|
- **Создать/обновить `AGENTS.md` в корне целевого репозитория** (если нет — создать, если есть — не трогать)
|
||||||
|
- Прочитать `PROTOCOLS/00_CONFIG.md`
|
||||||
|
- Выполнить 00_CONFIG:
|
||||||
|
- Если `.agent/metaagent-request.md` существует — прочитать config из него
|
||||||
|
- Если нет — провести интервью с пользователем (или принять `default`)
|
||||||
|
- Валидировать config относительно depth
|
||||||
|
- Если не было файла — создать `.agent/metaagent-request.md` с пометкой Auto-generated
|
||||||
|
- **Проверить версию:** если `.agent/checkpoints.json` существует → выполнить `PROTOCOLS/00_MIGRATE.md` (сравнить metaagent_version, применить миграцию при необходимости)
|
||||||
|
- Прочитать `PROTOCOLS/01_ANALYSIS.md`
|
||||||
|
- Инициализировать `.agent/checkpoints.json` с `metaagent_version` (если не существовал)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metaagent_version": "1.1.0",
|
||||||
|
"session_id": "<uuid>",
|
||||||
|
"target_repo": "<path>",
|
||||||
|
"goal": "<цель от пользователя>",
|
||||||
|
"project_type": "pending",
|
||||||
|
"config": {
|
||||||
|
"depth": 4,
|
||||||
|
"design": { "adr": false, "alternative_arch": false },
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": false,
|
||||||
|
"decomposition": { "invariant_tests": false },
|
||||||
|
"handoff": { "layer_structure": false }
|
||||||
|
},
|
||||||
|
"phases": {
|
||||||
|
"analysis": "pending",
|
||||||
|
"design": "pending",
|
||||||
|
"red_team": "pending",
|
||||||
|
"decomposition": "pending",
|
||||||
|
"environment": "pending",
|
||||||
|
"handoff": "pending"
|
||||||
|
},
|
||||||
|
"tasks": [],
|
||||||
|
"last_updated": "<timestamp>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Выход:** готовая `.agent/` + checkpoints.json с metaagent_version и config.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 1: ANALYSE
|
||||||
|
|
||||||
|
**Вход:** целевой репозиторий, `.agent/metaagent-request.md` (или auto-generated), checkpoints.json (analysis: pending, config: from INIT).
|
||||||
|
|
||||||
|
**Протокол:** `PROTOCOLS/01_ANALYSIS.md`
|
||||||
|
|
||||||
|
**Действия:**
|
||||||
|
- **Прочитать `.agent/rules/project-rules.md`** — учесть пользовательские правила
|
||||||
|
- Прочитать config из checkpoints.json (уже получен на INIT через 00_CONFIG)
|
||||||
|
- Если config отсутствует — применить default config (depth=4) как fallback
|
||||||
|
- Выполнить анализ репозитория по протоколу (определяет тип проекта)
|
||||||
|
- Записать результат в `.agent/analysis-report.md`
|
||||||
|
- Обновить checkpoints.json: `phases.analysis = "completed"`, `project_type = "existing" | "greenfield" | "scaffold"`
|
||||||
|
|
||||||
|
**Выход:** `.agent/analysis-report.md`
|
||||||
|
|
||||||
|
**Ветвление:**
|
||||||
|
- `project_type = "greenfield"` или `"scaffold"` → далее фаза DESIGN
|
||||||
|
- `project_type = "existing"` → DESIGN пропускается, сразу DECOMPOSITION
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 2: DESIGN (условная)
|
||||||
|
|
||||||
|
**Вход:** analysis-report.md, checkpoints.json (analysis: completed, project_type: greenfield/scaffold).
|
||||||
|
|
||||||
|
**Протокол:** `PROTOCOLS/02_DESIGN.md`
|
||||||
|
|
||||||
|
**Действия:**
|
||||||
|
- **Прочитать `.agent/rules/project-rules.md`** — учесть пользовательские правила
|
||||||
|
- Спроектировать архитектуру, модули, данные, интерфейсы
|
||||||
|
- Если config.design.alternative_arch: описать альтернативную архитектуру
|
||||||
|
- Если config.design.adr: создать ADR для каждого ключевого решения → `.agent/layer-1/adr/`
|
||||||
|
- Если config.risk_register: создать `.agent/layer-1/risk-register.md`
|
||||||
|
- Записать результат в `.agent/design-report.md`
|
||||||
|
- Обновить checkpoints.json: `phases.design = "completed"`
|
||||||
|
|
||||||
|
**Ветвление:**
|
||||||
|
- Если config.red_team = yes → следующая фаза RED_TEAM
|
||||||
|
- Иначе → сразу DECOMPOSITION
|
||||||
|
|
||||||
|
**Выход:** `.agent/design-report.md`, опционально `.agent/layer-1/adr/*.md`, `.agent/layer-1/risk-register.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 2b: RED_TEAM (опциональная)
|
||||||
|
|
||||||
|
**Вход:** design-report.md, ADR (опционально), checkpoints.json (design: completed).
|
||||||
|
|
||||||
|
**Протокол:** `PROTOCOLS/02b_REDTEAM.md`
|
||||||
|
|
||||||
|
**Действия:**
|
||||||
|
- **Прочитать `.agent/rules/project-rules.md`** — учесть пользовательские правила
|
||||||
|
- Выполнить Red Team Review по протоколу
|
||||||
|
- Записать результат в `.agent/layer-1/red-team-report.md`
|
||||||
|
- Дополнить risk-register.md (если существует)
|
||||||
|
- Если найдены критические проблемы — исправить design-report
|
||||||
|
- Обновить checkpoints.json: `phases.red_team = "completed"`
|
||||||
|
|
||||||
|
**Выход:** `.agent/layer-1/red-team-report.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 3: DECOMPOSITION
|
||||||
|
|
||||||
|
**Вход:** analysis-report.md + design-report.md (опционально) + ADR (опционально) + checkpoints.json.
|
||||||
|
|
||||||
|
**Протокол:** `PROTOCOLS/03_DECOMPOSITION.md`
|
||||||
|
|
||||||
|
**Действия:**
|
||||||
|
- **Прочитать `.agent/rules/project-rules.md`** — учесть пользовательские правила
|
||||||
|
- Разбить цель (и дизайн) на атомарные задачи
|
||||||
|
- Если config.decomposition.invariant_tests: создать задачи-инварианты для каждого ADR
|
||||||
|
- Записать манифест в `.agent/task-manifest.json` и `.agent/task-manifest.md`
|
||||||
|
- Обновить checkpoints.json: `phases.decomposition = "completed"`, заполнить `tasks`
|
||||||
|
|
||||||
|
**Выход:** `.agent/task-manifest.json`, `.agent/task-manifest.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 4: SETUP
|
||||||
|
|
||||||
|
**Вход:** analysis-report.md, design-report.md (опционально), task-manifest.json, checkpoints.json (decomposition: completed).
|
||||||
|
|
||||||
|
**Протокол:** `PROTOCOLS/04_ENVIRONMENT_SETUP.md`
|
||||||
|
|
||||||
|
**Действия:**
|
||||||
|
- **Прочитать `.agent/rules/project-rules.md`** — учесть пользовательские правила
|
||||||
|
- Выполнить настройку окружения по протоколу (ветка A для existing, ветка B для greenfield)
|
||||||
|
- Записать результат проверки в `.agent/baseline-test-report.log` и `.agent/setup-report.log`
|
||||||
|
- Обновить checkpoints.json: `phases.environment = "completed"`
|
||||||
|
|
||||||
|
**Выход:** рабочее окружение + `.agent/baseline-test-report.log`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 5: CHECKPOINT (сквозная)
|
||||||
|
|
||||||
|
**Вход:** любая фаза.
|
||||||
|
|
||||||
|
**Протокол:** обновлять checkpoints.json после каждого значимого шага.
|
||||||
|
|
||||||
|
**Архивирование перед сохранением чекпоинта:**
|
||||||
|
- Если checkpoints.json уже существует — сохранить предыдущую версию в `.agent/archive/checkpoints/<last_updated>.json`
|
||||||
|
|
||||||
|
**Формат:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metaagent_version": "1.1.0",
|
||||||
|
"session_id": "<uuid>",
|
||||||
|
"target_repo": "<path>",
|
||||||
|
"goal": "<цель>",
|
||||||
|
"project_type": "existing | greenfield | scaffold",
|
||||||
|
"config": {
|
||||||
|
"depth": 6,
|
||||||
|
"design": { "adr": true, "alternative_arch": true },
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": false,
|
||||||
|
"decomposition": { "invariant_tests": true },
|
||||||
|
"handoff": { "layer_structure": true }
|
||||||
|
},
|
||||||
|
"phases": {
|
||||||
|
"analysis": "completed",
|
||||||
|
"design": "completed",
|
||||||
|
"red_team": "skipped",
|
||||||
|
"decomposition": "in_progress",
|
||||||
|
"environment": "pending",
|
||||||
|
"handoff": "pending"
|
||||||
|
},
|
||||||
|
"tasks": [
|
||||||
|
{ "id": "T1", "title": "...", "status": "completed",
|
||||||
|
"depends_on": [], "acceptance_criteria": ["..."] },
|
||||||
|
{ "id": "T2", "title": "...", "status": "pending",
|
||||||
|
"depends_on": ["T1"], "acceptance_criteria": ["..."] }
|
||||||
|
],
|
||||||
|
"last_updated": "<timestamp>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`status` может быть: `pending`, `in_progress`, `completed`, `failed`, `skipped`.
|
||||||
|
Фаза `red_team` может быть `skipped` если config.red_team = false.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 6: HANDOFF
|
||||||
|
|
||||||
|
**Вход:** все предыдущие фазы completed.
|
||||||
|
|
||||||
|
**Протокол:** `PROTOCOLS/05_HANDOFF.md`
|
||||||
|
|
||||||
|
**Действия:**
|
||||||
|
- **Прочитать `.agent/rules/project-rules.md`** — учесть пользовательские правила
|
||||||
|
- **Архивировать завершённые задачи:**
|
||||||
|
- Для каждой задачи со статусом `completed` в `task-manifest.json`:
|
||||||
|
- Перенести полное описание в `.agent/archive/tasks/<id>.json`
|
||||||
|
- Заменить в манифесте на one-liner: `{ "id": "<id>", "title": "<title>", "status": "archived" }`
|
||||||
|
- Создать `.agent/archive/index.json` со списком архивированных задач
|
||||||
|
- Заархивировать предыдущий `checkpoints.json` в `.agent/archive/checkpoints/`
|
||||||
|
- Выполнить валидацию всех артефактов
|
||||||
|
- Если config.handoff.layer_structure: организовать `.agent/` по слоям
|
||||||
|
- Записать `.agent/handoff-summary.md` (в layer-3 при layer_structure=yes)
|
||||||
|
- Создать `.agent/session-summary.md` (в layer-0 при layer_structure=yes)
|
||||||
|
- Обновить checkpoints.json: `phases.handoff = "completed"`
|
||||||
|
- Сообщить пользователю/оркестратору
|
||||||
|
|
||||||
|
**Выход:** `.agent/handoff-summary.md` — итоговый документ для исполнительного агента.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 7: EXIT
|
||||||
|
|
||||||
|
Мета-агент завершает работу. Управление переходит к исполнительному агенту.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Структура .agent/
|
||||||
|
|
||||||
|
.agent/ всегда содержит служебную директорию `src/` с исходниками MetaAgent (см. фазу INIT).
|
||||||
|
При layer_structure=yes артефакты сессии раскладываются по слоям layer-0..3.
|
||||||
|
|
||||||
|
```
|
||||||
|
.agent/
|
||||||
|
src/ # исходники MetaAgent (всегда)
|
||||||
|
META_AGENT_GUIDE.md # главная инструкция
|
||||||
|
PROTOCOLS/ # протоколы фаз
|
||||||
|
TEMPLATES/ # шаблоны артефактов
|
||||||
|
BOUNDARIES.md # границы
|
||||||
|
WORKFLOW.md # примеры работы
|
||||||
|
VERSION # версия MetaAgent
|
||||||
|
install.sh # скрипт установки/обновления (Unix)
|
||||||
|
install.ps1 # скрипт установки/обновления (Windows)
|
||||||
|
rules/ # пользовательские правила (всегда)
|
||||||
|
project-rules.md # правила проекта — читать перед каждой фазой
|
||||||
|
archive/ # архив завершённых артефактов (создаётся при HANDOFF)
|
||||||
|
index.json # мета-индекс архива
|
||||||
|
tasks/ # детали завершённых задач
|
||||||
|
checkpoints/ # исторические чекпоинты
|
||||||
|
adr/ # заменённые ADR
|
||||||
|
reports/ # устаревшие отчёты
|
||||||
|
layer-0/ # ядро сессии (только при layer_structure=yes)
|
||||||
|
checkpoints.json # всегда (ядро)
|
||||||
|
session-summary.md # краткая сводка сессии
|
||||||
|
layer-1/ # архитектурные решения (справочно)
|
||||||
|
adr/
|
||||||
|
001-технологический-стек.md
|
||||||
|
002-архитектурный-паттерн.md
|
||||||
|
...
|
||||||
|
risk-register.md
|
||||||
|
red-team-report.md
|
||||||
|
layer-2/ # дизайн и анализ (справочно)
|
||||||
|
analysis-report.md
|
||||||
|
design-report.md
|
||||||
|
layer-3/ # состояние исполнения
|
||||||
|
handoff-summary.md
|
||||||
|
task-manifest.json
|
||||||
|
task-manifest.md
|
||||||
|
baseline-test-report.log
|
||||||
|
setup-report.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Исполнительный агент всегда начинает с layer-0 (checkpoints + session-summary),
|
||||||
|
затем при необходимости обращается к layer-1 (ADR для понимания "почему"),
|
||||||
|
layer-2 (детали дизайна), layer-3 (что было сделано).
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
# Протокол 00: Конфигурация сессии (CONFIG)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Определить параметры сессии MetaAgent: глубину проработки, набор функций, тип проекта. Выполняется на фазе INIT.
|
||||||
|
|
||||||
|
## Вход
|
||||||
|
|
||||||
|
- `VERSION` — текущая версия MetaAgent
|
||||||
|
- Запрос пользователя (цель)
|
||||||
|
- Опционально: `.agent/metaagent-request.md` (в директории `.agent/` целевого репозитория)
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
### 0.1. Проверить наличие .agent/metaagent-request.md
|
||||||
|
|
||||||
|
Если файл существует — распарсить, провалидировать и использовать.
|
||||||
|
Если нет — перейти к интервью (шаг 0.2).
|
||||||
|
|
||||||
|
### 0.2. Интервью с пользователем
|
||||||
|
|
||||||
|
Задать пользователю серию вопросов для сбора конфигурации.
|
||||||
|
|
||||||
|
**Сценарий интервью:**
|
||||||
|
|
||||||
|
```
|
||||||
|
MetaAgent: .agent/metaagent-request.md не найден. Давайте настроим сессию.
|
||||||
|
(или ответьте "default" — я выберу depth=4, light)
|
||||||
|
|
||||||
|
Q1: Это новый проект (greenfield) или работа с существующим кодом (existing)?
|
||||||
|
Варианты: new / existing / scaffold / default
|
||||||
|
|
||||||
|
Q2: Глубина проработки?
|
||||||
|
1-2: Scaffold — только структура, пустые модули
|
||||||
|
3-4: Light — быстрый дизайн + задачи, без расширений (рекомендуется default)
|
||||||
|
5-6: Standard — полный цикл с acceptance criteria
|
||||||
|
7-8: Deep — + ADR, risk register, alternative architecture
|
||||||
|
9-10: Maximum — + Red Team review, executable invariants
|
||||||
|
Варианты: число 1-10 / default
|
||||||
|
|
||||||
|
Q3 (если глубина >= 7): Нужны ADR (Architecture Decision Records)?
|
||||||
|
Варианты: yes / no / default
|
||||||
|
|
||||||
|
Q4 (если глубина >= 7): Нужен Risk Register?
|
||||||
|
Варианты: yes / no / default
|
||||||
|
|
||||||
|
Q5 (если глубина >= 9): Нужен Red Team Review?
|
||||||
|
Варианты: yes / no / default
|
||||||
|
```
|
||||||
|
|
||||||
|
**Правила обработки ответов:**
|
||||||
|
- Если пользователь ответил `default` или не ответил — применить значение по умолчанию для этого поля
|
||||||
|
- Если пользователь ответил `new` — `project_type = greenfield`
|
||||||
|
- Если `existing` — `project_type = existing`
|
||||||
|
|
||||||
|
### 0.3. Default config
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"depth": 4,
|
||||||
|
"design": {
|
||||||
|
"adr": false,
|
||||||
|
"alternative_arch": false
|
||||||
|
},
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": false,
|
||||||
|
"decomposition": {
|
||||||
|
"invariant_tests": false
|
||||||
|
},
|
||||||
|
"handoff": {
|
||||||
|
"layer_structure": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Depth=4 (Light) означает:
|
||||||
|
- ANALYSIS — полный (определение типа проекта, извлечение требований)
|
||||||
|
- DESIGN — выполняется (если greenfield), но **без** ADR, Alternative Architecture, Risk Register
|
||||||
|
- DECOMPOSITION — задачи с acceptance criteria, **без** invariant-тестов
|
||||||
|
- SETUP — полный
|
||||||
|
- HANDOFF — плоский `.agent/` (без layer-структуры)
|
||||||
|
|
||||||
|
### 0.4. Запись .agent/metaagent-request.md
|
||||||
|
|
||||||
|
Если файла не было, создать его по результатам интервью с пометкой `Auto-generated`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# MetaAgent Request
|
||||||
|
# Auto-generated from user interview on {{ date }}
|
||||||
|
|
||||||
|
## Параметры сессии
|
||||||
|
|
||||||
|
| Функция | Вкл | Аргументы |
|
||||||
|
|---|---|---|
|
||||||
|
| ANALYSIS | ✓ | — |
|
||||||
|
| DESIGN | ✓ | adr={{ adr }}, alternative_arch={{ alt_arch }} |
|
||||||
|
| RED_TEAM | {{ red_team }} | — |
|
||||||
|
| RISK_REGISTER | {{ risk_register }} | — |
|
||||||
|
| DECOMPOSITION | ✓ | invariant_tests={{ invariant_tests }} |
|
||||||
|
| SETUP | ✓ | — |
|
||||||
|
| HANDOFF | ✓ | layer_structure={{ layer_structure }} |
|
||||||
|
|
||||||
|
## Глубина проработки
|
||||||
|
|
||||||
|
**Значение:** {{ depth }}
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
{{ goal }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 0.5. Создание .agent/rules/
|
||||||
|
|
||||||
|
Создать директорию `.agent/rules/` в корне целевого проекта (если не существует).
|
||||||
|
Если `.agent/rules/project-rules.md` не существует — создать из шаблона `.agent/src/TEMPLATES/project-rules.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Project Rules
|
||||||
|
|
||||||
|
Добавляйте сюда правила, которым агент обязан следовать во всех фазах.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 0.6. Валидация config
|
||||||
|
|
||||||
|
Проверить совместимость параметров с depth:
|
||||||
|
|
||||||
|
```
|
||||||
|
depth < 3 → DESIGN пропускается (даже для greenfield)
|
||||||
|
depth < 7 → adr=false, alternative_arch=false, risk_register=false, invariant_tests=false
|
||||||
|
depth < 9 → red_team=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Если depth несовместим с включёнными функциями — понизить функции до максимума, разрешённого depth.
|
||||||
|
|
||||||
|
## Выход
|
||||||
|
|
||||||
|
- `.agent/metaagent-request.md` (создан или подтверждён)
|
||||||
|
- config — словарь параметров для записи в checkpoints.json
|
||||||
|
|
||||||
|
## Критерии завершения
|
||||||
|
|
||||||
|
- [ ] `.agent/metaagent-request.md` существует (создан или найден)
|
||||||
|
- [ ] Config содержит depth, design.*, red_team, risk_register, decomposition.*, handoff.*
|
||||||
|
- [ ] Config совместим с depth (доп. функции отключены для малых depth)
|
||||||
|
- [ ] При отсутствии файла — проведено интервью, файл создан
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
# Протокол 00b: Миграция артефактов (MIGRATE)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Обеспечить совместимость артефактов `.agent/` при изменении версии MetaAgent.
|
||||||
|
Позволяет обновлять проекты, созданные старой версией, без потери данных.
|
||||||
|
|
||||||
|
## Вход
|
||||||
|
|
||||||
|
- `VERSION` — текущая версия MetaAgent
|
||||||
|
- `.agent/checkpoints.json` — артефакты целевого проекта
|
||||||
|
- `.agent/` — остальные артефакты
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
### M1. Определить версию артефактов
|
||||||
|
|
||||||
|
Прочитать `.agent/checkpoints.json`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
stored_version = checkpoints.get("metaagent_version", None)
|
||||||
|
current_version = read("VERSION").strip()
|
||||||
|
```
|
||||||
|
|
||||||
|
- Если `metaagent_version` отсутствует → артефакт создан **v0.x** (доверсионный)
|
||||||
|
- Если `metaagent_version` == `current_version` → пропустить миграцию
|
||||||
|
- Если `metaagent_version` < `current_version` → требуется миграция
|
||||||
|
|
||||||
|
### M2. Сравнение версий (SemVer)
|
||||||
|
|
||||||
|
Версии сравниваются по семантическому версионированию (`MAJOR.MINOR.PATCH`).
|
||||||
|
|
||||||
|
```python
|
||||||
|
def needs_migration(stored, current):
|
||||||
|
if stored is None:
|
||||||
|
return True
|
||||||
|
return parse_semver(stored) < parse_semver(current)
|
||||||
|
```
|
||||||
|
|
||||||
|
### M3. Матрица миграций
|
||||||
|
|
||||||
|
Каждая строка — набор шагов для перехода с одной версии на следующую.
|
||||||
|
|
||||||
|
| Из версии | В версию | Шаги миграции |
|
||||||
|
|---|---|---|
|
||||||
|
| v0.x (нет поля) | v1.0.0 | M3.1 — M3.4 |
|
||||||
|
| v1.0.0 | v1.1.0 | M3.5 — M3.6 (см. ниже) |
|
||||||
|
|
||||||
|
### M4. Шаги миграции v0.x → v1.0.0
|
||||||
|
|
||||||
|
... (шаги миграции остаются без изменений)
|
||||||
|
|
||||||
|
### M5. Шаги миграции v1.0.0 → v1.1.0
|
||||||
|
|
||||||
|
M3.5: Создать `.agent/rules/` с шаблоном `project-rules.md` (если не существует).
|
||||||
|
M3.6: Создать `.agent/archive/` (если не существует).
|
||||||
|
|
||||||
|
#### M3.1. Добавить metaagent_version
|
||||||
|
|
||||||
|
Записать в checkpoints.json:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"metaagent_version": "1.1.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### M3.2. Добавить config (default)
|
||||||
|
|
||||||
|
Если поля `config` нет в checkpoints.json — добавить config по умолчанию:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"config": {
|
||||||
|
"depth": 4,
|
||||||
|
"design": { "adr": false, "alternative_arch": false },
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": false,
|
||||||
|
"decomposition": { "invariant_tests": false },
|
||||||
|
"handoff": { "layer_structure": false }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### M3.3. Добавить фазу red_team
|
||||||
|
|
||||||
|
Если в `phases` нет ключа `red_team`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"red_team": "skipped"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### M3.4. Создать layer-1/ (опционально, только если config.handoff.layer_structure)
|
||||||
|
|
||||||
|
Если включена layer_structure:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p .agent/layer-1/adr
|
||||||
|
touch .agent/layer-1/adr/.gitkeep
|
||||||
|
```
|
||||||
|
|
||||||
|
Если `risk-register.md` уже существует на верхнем уровне — переместить в `.agent/layer-1/risk-register.md`.
|
||||||
|
|
||||||
|
### M4. После миграции — резюме
|
||||||
|
|
||||||
|
Записать в `.agent/migration-report.log`:
|
||||||
|
|
||||||
|
```
|
||||||
|
[MIGRATE] {{ timestamp }}
|
||||||
|
From: {{ from_version }}
|
||||||
|
To: {{ to_version }}
|
||||||
|
Steps applied: {{ step_list }}
|
||||||
|
Status: OK
|
||||||
|
```
|
||||||
|
|
||||||
|
## Выход
|
||||||
|
|
||||||
|
- Обновлённый `.agent/checkpoints.json` (metaagent_version + config)
|
||||||
|
- Опционально: `.agent/layer-1/` структура
|
||||||
|
- `.agent/migration-report.log`
|
||||||
|
|
||||||
|
## Критерии завершения
|
||||||
|
|
||||||
|
- [ ] metaagent_version в checkpoints.json == текущей версии из VERSION
|
||||||
|
- [ ] config присутствует в checkpoints.json
|
||||||
|
- [ ] phases.red_team присутствует (skipped, если не нужен)
|
||||||
|
- [ ] migration-report.log создан
|
||||||
|
- [ ] Все старые данные сохранены (ничего не удалено)
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
# Протокол 01: Анализ репозитория (ANALYSIS)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Составить полную картину целевого репозитория: тип проекта, архитектура, стек, конвенции, состояние тестов, требования.
|
||||||
|
|
||||||
|
## Вход
|
||||||
|
|
||||||
|
- Целевой репозиторий (локальная копия)
|
||||||
|
- `.agent/metaagent-request.md` (конфигурация сессии: глубина, функции) — или auto-generated
|
||||||
|
- `.agent/checkpoints.json` (фаза analysis: pending)
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
### 0.0. Чтение конфигурации сессии
|
||||||
|
|
||||||
|
Прочитать config из checkpoints.json (установлен на фазе INIT через `00_CONFIG.md`).
|
||||||
|
|
||||||
|
Если config отсутствует или неполный — применить default:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"depth": 4,
|
||||||
|
"design": { "adr": false, "alternative_arch": false },
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": false,
|
||||||
|
"decomposition": { "invariant_tests": false },
|
||||||
|
"handoff": { "layer_structure": false }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Записать (или подтвердить) конфигурацию в checkpoints.json:
|
||||||
|
```json
|
||||||
|
"config": {
|
||||||
|
"depth": 6,
|
||||||
|
"design": { "adr": true, "alternative_arch": true },
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": false,
|
||||||
|
"decomposition": { "invariant_tests": true },
|
||||||
|
"handoff": { "layer_structure": true }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Если `.agent/metaagent-request.md` не найден — использовать значения по умолчанию (depth=6, все базовые функции=true, расширенные=false).
|
||||||
|
|
||||||
|
### 1.0. Определение типа проекта
|
||||||
|
|
||||||
|
Просканировать корень репозитория и определить:
|
||||||
|
|
||||||
|
- **`existing`** — есть исходный код, тесты, система сборки (файлы `.py`, `.js`, `.ts`, `.rs`, `.go` и т.д. помимо конфигов и README)
|
||||||
|
- **`greenfield`** — репозиторий пуст или содержит только README, LICENSE, .gitignore
|
||||||
|
- **`scaffold`** — есть базовая структура (pyproject.toml/package.json), но нет значимого кода
|
||||||
|
|
||||||
|
Записать тип в analysis-report.md.
|
||||||
|
|
||||||
|
**Правило:** если проект `existing` — разделы 1.1–1.6 выполняются полностью. Если `greenfield` — разделы 1.2–1.5 заменяются на 1.7 (извлечение требований из README).
|
||||||
|
|
||||||
|
### 1.1. Общая информация
|
||||||
|
|
||||||
|
Прочитать и зафиксировать:
|
||||||
|
- **README** — описание проекта, how to build/test/run
|
||||||
|
- **Лицензия** — какой LICENSE
|
||||||
|
- **CI/CD** — `.github/workflows/`, `.gitlab-ci.yml`, `Jenkinsfile`, `Makefile` и т.д.
|
||||||
|
- **Главные точки входа** — `main.py`, `index.js`, `cmd/` и т.д.
|
||||||
|
- **Система сборки** — `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `CMakeLists.txt`
|
||||||
|
|
||||||
|
### 1.2. Стек технологий (только для existing/scaffold)
|
||||||
|
|
||||||
|
Определить:
|
||||||
|
- **Язык(и)** — Python, TypeScript, Go, Rust и т.д.
|
||||||
|
- **Фреймворк** — FastAPI, Next.js, React, Actix и т.д.
|
||||||
|
- **База данных** — PostgreSQL, SQLite, MongoDB и т.д.
|
||||||
|
- **Тестовый раннер** — pytest, jest, vitest, go test
|
||||||
|
- **Пакетный менеджер** — pip/poetry, npm/yarn/pnpm, cargo, go modules
|
||||||
|
- **Линтер/форматтер** — ruff, eslint, prettier, rustfmt, gofmt
|
||||||
|
|
||||||
|
### 1.3. Архитектура (только для existing/scaffold)
|
||||||
|
|
||||||
|
- **Структура директорий** — записать схему (можно `tree /F`, но не более 3 уровней глубины)
|
||||||
|
- **Архитектурный паттерн** — MVC, Clean Architecture, модульный монолит, микросервисы
|
||||||
|
- **Ключевые модули/пакеты** — перечислить с кратким описанием
|
||||||
|
- **Внешние зависимости** — основные библиотеки
|
||||||
|
|
||||||
|
### 1.4. Конвенции кода (только для existing/scaffold)
|
||||||
|
|
||||||
|
- **Стиль кода** — судя по линтеру и примерам: именование, импорты, типизация
|
||||||
|
- **Паттерны** — как организованы роуты, хендлеры, модели, тесты
|
||||||
|
- **Обработка ошибок** — как принято обрабатывать ошибки в проекте
|
||||||
|
- **Логирование** — используется ли логгер, какой уровень
|
||||||
|
|
||||||
|
### 1.5. Тесты (только для existing/scaffold)
|
||||||
|
|
||||||
|
- **Какие тесты есть** — unit, integration, e2e
|
||||||
|
- **Где лежат** — `tests/`, `__tests__/`, рядом с модулями
|
||||||
|
- **Запуск** — команда для запуска всех тестов
|
||||||
|
- **Текущее состояние** — запустить тесты, записать результат (сколько всего, сколько пройдено/упало)
|
||||||
|
- **Покрытие** — есть ли метрики покрытия
|
||||||
|
|
||||||
|
### 1.6. Базовая проверка (только для existing/scaffold)
|
||||||
|
|
||||||
|
- **Собирается ли проект?** — запустить сборку
|
||||||
|
- **Запускается ли проект?** — если возможно, проверить старт
|
||||||
|
- **Чистый ли git status?** — нет ли незакоммиченных изменений
|
||||||
|
|
||||||
|
### 1.7. Извлечение требований (только для greenfield/scaffold)
|
||||||
|
|
||||||
|
Если README содержит описание будущего проекта — извлечь и структурировать:
|
||||||
|
|
||||||
|
**Функциональные требования:**
|
||||||
|
- Пользовательские истории (user stories)
|
||||||
|
- Основные сценарии использования
|
||||||
|
- Входные/выходные данные системы
|
||||||
|
|
||||||
|
**Нефункциональные требования:**
|
||||||
|
- Технологические предпочтения (язык, фреймворк, БД)
|
||||||
|
- Требования к производительности, безопасности
|
||||||
|
- Ограничения (сроки, платформа, окружение)
|
||||||
|
|
||||||
|
**Бизнес-контекст:**
|
||||||
|
- Цель системы (зачем)
|
||||||
|
- Целевая аудитория
|
||||||
|
- Ключевые метрики успеха
|
||||||
|
|
||||||
|
**Сомнительные/неясные требования:**
|
||||||
|
- Вопросы, которые нужно задать пользователю перед проектированием
|
||||||
|
- Противоречия в README
|
||||||
|
|
||||||
|
## Выход
|
||||||
|
|
||||||
|
`.agent/analysis-report.md` по шаблону `TEMPLATES/analysis-report.md`.
|
||||||
|
|
||||||
|
Обновить checkpoints.json: `phases.analysis = "completed"`. Если проект `greenfield`, также установить `project_type = "greenfield"`.
|
||||||
|
|
||||||
|
## Критерии завершения фазы
|
||||||
|
|
||||||
|
- [ ] Тип проекта определён (existing / greenfield / scaffold)
|
||||||
|
- [ ] Все соответствующие разделы (1.1–1.7) выполнены
|
||||||
|
- [ ] `.agent/analysis-report.md` создан и заполнен
|
||||||
|
- [ ] checkpoints.json обновлён
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
# Протокол 02: Архитектурное проектирование (DESIGN)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Спроектировать архитектуру, модули, данные и интерфейсы для greenfield/scaffold-проекта на основе требований из analysis-report.
|
||||||
|
|
||||||
|
## Вход
|
||||||
|
|
||||||
|
- `.agent/analysis-report.md` (project_type: greenfield или scaffold)
|
||||||
|
- `.agent/metaagent-request.md` (конфигурация сессии: adr, alternative_arch, risk_register)
|
||||||
|
- `.agent/checkpoints.json` (фаза design: pending)
|
||||||
|
|
||||||
|
## Правила
|
||||||
|
|
||||||
|
1. **Реалистичность** — архитектура должна быть реализуема исполнительным агентом за 1 сессию (до 10 задач)
|
||||||
|
2. **Документируемость** — каждый модуль, модель и интерфейс описывается в design-report.md
|
||||||
|
3. **Тестируемость** — каждый компонент проектируется с учётом того, как его тестировать
|
||||||
|
4. **Итеративность** — первая версия должна быть минимально рабочей (MVP), расширения — отдельными задачами
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
### 2.1. Технологический стек
|
||||||
|
|
||||||
|
Если стек не указан в README — предложить обоснованный выбор. Если указан — зафиксировать.
|
||||||
|
|
||||||
|
Для каждого компонента указать:
|
||||||
|
- Язык и версия
|
||||||
|
- Фреймворк / библиотека
|
||||||
|
- База данных (движок, схема)
|
||||||
|
- Инфраструктура (Docker, CI/CD, хостинг)
|
||||||
|
|
||||||
|
### 2.2. High-level архитектура
|
||||||
|
|
||||||
|
Описать общую структуру системы:
|
||||||
|
|
||||||
|
- **Архитектурный паттерн** — монолит, модульный монолит, микросервисы, слоистая, луковая и т.д.
|
||||||
|
- **Компоненты и их ответственность** — что делает каждый модуль/сервис
|
||||||
|
- **Схема взаимодействия** — текстовое описание потоков данных
|
||||||
|
|
||||||
|
Формат (text diagram):
|
||||||
|
|
||||||
|
```
|
||||||
|
[Client] → HTTP → [API Gateway] → [Auth Service]
|
||||||
|
↓
|
||||||
|
[Core Service] → [Database]
|
||||||
|
↓
|
||||||
|
[External API] → [3rd Party]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3. Модули проекта
|
||||||
|
|
||||||
|
Разбить систему на модули/пакеты. Для каждого:
|
||||||
|
|
||||||
|
| Поле | Описание |
|
||||||
|
|---|---|
|
||||||
|
| **Имя модуля** | `app/services/cashflow.py` |
|
||||||
|
| **Ответственность** | Что делает |
|
||||||
|
| **Ключевые классы/функции** | Только сигнатуры (без реализации) |
|
||||||
|
| **Зависимости** | Какие модули нужны этому |
|
||||||
|
| **Контракт** | Что экспортирует/предоставляет |
|
||||||
|
|
||||||
|
### 2.4. Модели данных
|
||||||
|
|
||||||
|
Описать основные сущности, их поля и связи:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"entity": "Transaction",
|
||||||
|
"fields": [
|
||||||
|
{"name": "id", "type": "UUID", "pk": true},
|
||||||
|
{"name": "amount", "type": "Decimal"},
|
||||||
|
{"name": "date", "type": "datetime"},
|
||||||
|
{"name": "category_id", "type": "UUID", "fk": "Category"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Если используется ORM — указать аннотации/декораторы.
|
||||||
|
Если БД — схему таблиц, индексы, ключи.
|
||||||
|
|
||||||
|
### 2.5. API интерфейсы
|
||||||
|
|
||||||
|
Если проектируется API — описать эндпоинты:
|
||||||
|
|
||||||
|
| Метод | Путь | Описание | Request | Response | Статусы |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| GET | /transactions | Список транзакций | ?page, ?limit | [Transaction] | 200 |
|
||||||
|
| POST | /transactions | Создать транзакцию | CreateTransactionDTO | Transaction | 201, 400 |
|
||||||
|
|
||||||
|
Если GUI — описать ключевые страницы/экраны.
|
||||||
|
Если CLI — описать команды.
|
||||||
|
|
||||||
|
### 2.6. Обработка ошибок
|
||||||
|
|
||||||
|
- Стратегия ошибок: исключения, Result-тип, коды ошибок
|
||||||
|
- Формат ошибок в API: `{ "error": "...", "code": "...", "details": {} }`
|
||||||
|
- Логирование: какой уровень для каких событий
|
||||||
|
|
||||||
|
### 2.7. Стратегия тестирования
|
||||||
|
|
||||||
|
- Какие тесты нужны (unit, integration, e2e)
|
||||||
|
- Как изолировать зависимости (mocks, fakes, testcontainers)
|
||||||
|
- Команда запуска тестов
|
||||||
|
|
||||||
|
### 2.8. Alternative Architecture (если config.alternative_arch = yes)
|
||||||
|
|
||||||
|
Описать **минимум одну принципиально иную архитектуру** и причину отказа:
|
||||||
|
|
||||||
|
| Критерий | Выбранная архитектура | Альтернатива |
|
||||||
|
|---|---|---|
|
||||||
|
| Название | Модульный монолит | Микросервисы / Событийная / и т.д. |
|
||||||
|
| Сложность реализации | Низкая | Высокая (3+ сервиса) |
|
||||||
|
| Масштабирование | Вертикальное | Горизонтальное |
|
||||||
|
| Почему не выбрана | — | Избыточно для MVP |
|
||||||
|
|
||||||
|
Это снижает риск архитектурной инерции: решение становится осознанным, а не единственным возможным.
|
||||||
|
|
||||||
|
### 2.9. ADR (если config.adr = yes)
|
||||||
|
|
||||||
|
Для каждого ключевого архитектурного решения (стек, БД, паттерн, структура модулей) создать отдельный ADR-файл:
|
||||||
|
|
||||||
|
```
|
||||||
|
.agent/layer-1/adr/001-технологический-стек.md
|
||||||
|
.agent/layer-1/adr/002-модульный-монолит.md
|
||||||
|
.agent/layer-1/adr/003-json-хранение.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Формат — по шаблону `TEMPLATES/adr-NNNN.md`.
|
||||||
|
|
||||||
|
### 2.10. Risk Register (если config.risk_register = yes)
|
||||||
|
|
||||||
|
Создать `.agent/layer-1/risk-register.md` по шаблону `TEMPLATES/risk-register.md`:
|
||||||
|
|
||||||
|
| # | Assumption | Impact if wrong | Mitigation | Review trigger |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
|
||||||
|
Задокументировать **неявные допущения**, на которых держится архитектура. Это даёт future-агентам знать, что можно пересматривать в первую очередь.
|
||||||
|
|
||||||
|
### 2.11. Группировка в задачи
|
||||||
|
|
||||||
|
На основе спроектированных модулей и моделей предварительно наметить группировку в задачи (по модулям). Это будет входом для DECOMPOSITION.
|
||||||
|
|
||||||
|
```
|
||||||
|
T1: Инициализация проекта + зависимости
|
||||||
|
T2: Модель данных (сущности, миграции)
|
||||||
|
T3: Cashflow Service (core logic)
|
||||||
|
T4: API endpoints
|
||||||
|
...и т.д.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Выход
|
||||||
|
|
||||||
|
- `.agent/design-report.md` по шаблону `TEMPLATES/design-report.md`
|
||||||
|
- `.agent/layer-1/adr/*.md` (если adr=yes)
|
||||||
|
- `.agent/layer-1/risk-register.md` (если risk_register=yes)
|
||||||
|
- Предварительная группировка задач (для передачи в DECOMPOSITION)
|
||||||
|
|
||||||
|
Обновить checkpoints.json: `phases.design = "completed"`.
|
||||||
|
|
||||||
|
## Критерии завершения фазы
|
||||||
|
|
||||||
|
- [ ] Технологический стек определён
|
||||||
|
- [ ] High-level архитектура описана
|
||||||
|
- [ ] Модули и их ответственность описаны
|
||||||
|
- [ ] Модели данных спроектированы
|
||||||
|
- [ ] API/интерфейсы описаны (если применимо)
|
||||||
|
- [ ] Стратегия тестирования определена
|
||||||
|
- [ ] Alternative Architecture описана (если config требует)
|
||||||
|
- [ ] ADR созданы (если config требует)
|
||||||
|
- [ ] Risk Register создан (если config требует)
|
||||||
|
- [ ] Задачи предварительно сгруппированы
|
||||||
|
- [ ] `.agent/design-report.md` создан
|
||||||
|
- [ ] checkpoints.json обновлён
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# Протокол 02b: Red Team Review (опционально)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Преднамеренно попытаться разрушить спроектированную архитектуру, чтобы найти скрытые проблемы до начала реализации.
|
||||||
|
|
||||||
|
## Вход
|
||||||
|
|
||||||
|
- `.agent/design-report.md`
|
||||||
|
- `.agent/layer-1/adr/*.md` (если созданы)
|
||||||
|
- `.agent/metaagent-request.md` (глубина проработки >= 9)
|
||||||
|
|
||||||
|
## Когда выполняется
|
||||||
|
|
||||||
|
Только если `config.red_team = yes` (глубина 9-10). Выполняется **после** DESIGN, **до** DECOMPOSITION.
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
### RT1. Поиск скрытых зависимостей
|
||||||
|
|
||||||
|
Проверить каждый модуль на наличие неявных связей:
|
||||||
|
|
||||||
|
- Есть ли циклические зависимости между модулями?
|
||||||
|
- Есть ли модуль, который знает слишком много о других?
|
||||||
|
- Есть ли скрытый vendor lock-in (БД, облачный провайдер, внешний API)?
|
||||||
|
|
||||||
|
### RT2. Точки отказа
|
||||||
|
|
||||||
|
Для каждого внешнего интерфейса (API, БД, файловая система):
|
||||||
|
|
||||||
|
- Что произойдёт при отказе компонента?
|
||||||
|
- Есть ли fallback?
|
||||||
|
- Что произойдёт при невалидных входных данных?
|
||||||
|
|
||||||
|
### RT3. Масштабирование
|
||||||
|
|
||||||
|
Оценить поведение системы при:
|
||||||
|
|
||||||
|
- 10x рост данных
|
||||||
|
- 100x рост данных
|
||||||
|
- Добавлении нового пользователя / клиента
|
||||||
|
|
||||||
|
### RT4. Security (если применимо)
|
||||||
|
|
||||||
|
- Какие данные передаются по сети?
|
||||||
|
- Есть ли аутентификация?
|
||||||
|
- Хранятся ли секреты в коде?
|
||||||
|
|
||||||
|
### RT5. Consistency
|
||||||
|
|
||||||
|
Проверить design-report и ADR на противоречия:
|
||||||
|
|
||||||
|
- Одна сущность описана по-разному в двух местах?
|
||||||
|
- API-контракт не соответствует модели данных?
|
||||||
|
- Технологический стек противоречит нефункциональным требованиям?
|
||||||
|
|
||||||
|
## Выход
|
||||||
|
|
||||||
|
`.agent/layer-1/red-team-report.md` с секциями:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Найденные проблемы
|
||||||
|
|
||||||
|
| # | Проблема | Серьёзность | Рекомендация |
|
||||||
|
|---|---|---|---|
|
||||||
|
|
||||||
|
## Отклонённые атаки (что пытались сломать — но не сломалось)
|
||||||
|
|
||||||
|
| # | Гипотеза | Почему не подтвердилась |
|
||||||
|
|---|---|---|
|
||||||
|
```
|
||||||
|
|
||||||
|
Обновить risk-register.md (если существует) новыми рисками.
|
||||||
|
|
||||||
|
## Критерии завершения
|
||||||
|
|
||||||
|
- [ ] Все 5 секций (RT1-RT5) проверены
|
||||||
|
- [ ] Найденные проблемы записаны в red-team-report.md
|
||||||
|
- [ ] Если найдены критические проблемы — design-report должен быть исправлен
|
||||||
|
- [ ] Risk Register дополнен (если существует)
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
# Протокол 03: Декомпозиция задач (DECOMPOSITION)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Разбить цель пользователя (и архитектурный план, если есть) на атомарные, независимо выполнимые задачи и записать их в манифест.
|
||||||
|
|
||||||
|
## Вход
|
||||||
|
|
||||||
|
- `.agent/analysis-report.md`
|
||||||
|
- `.agent/design-report.md` (опционально — для greenfield/scaffold)
|
||||||
|
- `.agent/layer-1/adr/*.md` (опционально)
|
||||||
|
- `.agent/layer-1/risk-register.md` (опционально)
|
||||||
|
- `.agent/metaagent-request.md` (конфигурация сессии)
|
||||||
|
- Цель пользователя (из checkpoints.json)
|
||||||
|
- `.agent/checkpoints.json` (фаза decomposition: pending)
|
||||||
|
|
||||||
|
## Правила декомпозиции
|
||||||
|
|
||||||
|
### 3.1. Принципы
|
||||||
|
|
||||||
|
1. **Атомарность** — одна задача = одна логическая единица работы, которую можно выполнить и проверить за один подход
|
||||||
|
2. **Независимость (макс.)** — минимизировать зависимости между задачами
|
||||||
|
3. **Тестируемость** — каждая задача имеет измеримые acceptance criteria
|
||||||
|
4. **Границы** — задача не должна выходить за пределы, указанные в `BOUNDARIES.md`
|
||||||
|
5. **Порядок** — задачи с зависимостями выполняются строго последовательно
|
||||||
|
|
||||||
|
### 3.2. Размер задачи
|
||||||
|
|
||||||
|
Задача должна укладываться в **1-2 часа работы исполнительного агента**. Если задача крупнее — разбить на подзадачи.
|
||||||
|
|
||||||
|
Признак слишком крупной задачи:
|
||||||
|
- Нельзя сформулировать acceptance criteria одной строкой
|
||||||
|
- Затрагивает 5+ файлов
|
||||||
|
- Содержит союзы "и", "а также", "после чего"
|
||||||
|
|
||||||
|
### 3.3. Структура задачи
|
||||||
|
|
||||||
|
Каждая задача содержит:
|
||||||
|
|
||||||
|
| Поле | Описание | Пример |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | Уникальный идентификатор | `T1`, `T2` |
|
||||||
|
| `title` | Заголовок (что сделать) | "Добавить модель User" |
|
||||||
|
| `description` | Описание (как и зачем) | "Создать SQLAlchemy модель..." |
|
||||||
|
| `type` | Тип задачи | `feature`, `refactor`, `test`, `fix`, `config`, `design`, `docs` |
|
||||||
|
| `status` | Статус задачи | `pending`, `in_progress`, `completed`, `failed`, `archived` |
|
||||||
|
| `files` | Список файлов, которые нужно создать/изменить | `["app/models/user.py"]` |
|
||||||
|
| `depends_on` | ID задач, от которых зависит | `[]` или `["T0"]` |
|
||||||
|
| `acceptance_criteria` | Список критериев приёмки (3-5 пунктов) | `["Модель проходит миграцию"]` |
|
||||||
|
| `context` | Доп. информация (ссылки на доки, примеры, релевантные секции из design-report) | `"Смотри app/models/base.py"` |
|
||||||
|
|
||||||
|
### 3.4. Типы задач
|
||||||
|
|
||||||
|
| Тип | Описание |
|
||||||
|
|---|---|
|
||||||
|
| `config` | Настройка окружения, зависимостей, CI, инициализация проекта |
|
||||||
|
| `design` | Архитектурное/дизайнерское решение без кода |
|
||||||
|
| `feature` | Новая функциональность |
|
||||||
|
| `refactor` | Изменение структуры без изменения поведения |
|
||||||
|
| `test` | Добавление/исправление тестов |
|
||||||
|
| `fix` | Исправление бага |
|
||||||
|
| `docs` | Документация |
|
||||||
|
| `invariant` | Тест, проверяющий архитектурный инвариант (см. 3.7) |
|
||||||
|
|
||||||
|
### 3.5. Зелёная декомпозиция (для greenfield/scaffold)
|
||||||
|
|
||||||
|
Если есть `.agent/design-report.md` — задачи формируются на основе группировки из дизайна:
|
||||||
|
|
||||||
|
1. **T1: init** — инициализация проекта, зависимости, конфиги, scaffold
|
||||||
|
2. **T2..Tn: features** — модули/функциональность по одному
|
||||||
|
3. **Tn+1: tests** — тесты на каждый модуль (можно в составе feature-задачи)
|
||||||
|
4. **Tn+2: polish** — документация, форматирование, финальная проверка
|
||||||
|
|
||||||
|
### 3.6. Сортировка
|
||||||
|
|
||||||
|
Задачи в манифесте располагаются в порядке выполнения:
|
||||||
|
1. Сначала задачи без зависимостей
|
||||||
|
2. Потом те, чьи зависимости уже выполнены
|
||||||
|
3. Последними — задачи с наибольшим числом зависимостей
|
||||||
|
|
||||||
|
### 3.7. Executable Invariants (если config.invariant_tests = yes)
|
||||||
|
|
||||||
|
Для каждого ADR (из layer-1/adr/) создать задачу типа `invariant` — тест, проверяющий архитектурное правило.
|
||||||
|
|
||||||
|
**Правила превращения ADR в инварианты:**
|
||||||
|
|
||||||
|
| ADR | Инвариант-тест |
|
||||||
|
|---|---|
|
||||||
|
| "Модуль X не зависит от Y" | `test_x_does_not_import_y.py` — import test |
|
||||||
|
| "Слой Model не знает о CLI" | `test_model_layer_imports.py` — проверка import graph |
|
||||||
|
| "Все исключения кастомные" | `test_custom_exceptions.py` — проверка hierarchy |
|
||||||
|
| "Интерфейс репозитория не泄漏 implementation details" | `test_repository_interface.py` — ABC check |
|
||||||
|
|
||||||
|
**Формат задачи-инварианта:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "I1",
|
||||||
|
"title": "Инвариант: model не импортирует cli",
|
||||||
|
"type": "invariant",
|
||||||
|
"files": ["tests/invariants/test_layer_imports.py"],
|
||||||
|
"depends_on": ["T2"],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"Тест проверяет, что cashflow_model не импортирует cli, sync, engine",
|
||||||
|
"Тест проходит на пустом проекте (до реализации функциональности)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Инварианты размещаются в `tests/invariants/` и запускаются вместе с основными тестами.
|
||||||
|
|
||||||
|
## Выход
|
||||||
|
|
||||||
|
- `.agent/task-manifest.json` — по схеме `TEMPLATES/task-manifest.json`
|
||||||
|
- `.agent/task-manifest.md` — по шаблону `TEMPLATES/task-manifest.md`
|
||||||
|
|
||||||
|
Обновить checkpoints.json:
|
||||||
|
- `phases.decomposition = "completed"`
|
||||||
|
- `tasks` = полный массив задач со статусом `pending`
|
||||||
|
|
||||||
|
> **Примечание:** после HANDOFF завершённые задачи будут архивированы —
|
||||||
|
> полное описание уходит в `.agent/archive/tasks/`, в манифесте остаётся
|
||||||
|
> one-liner с `"status": "archived"`.
|
||||||
|
|
||||||
|
## Критерии завершения фазы
|
||||||
|
|
||||||
|
- [ ] Цель разбита на атомарные задачи
|
||||||
|
- [ ] Для каждой задачи указаны acceptance criteria
|
||||||
|
- [ ] Для каждой задачи указаны affected files
|
||||||
|
- [ ] Зависимости между задачами корректны (нет циклов)
|
||||||
|
- [ ] Invariant-задачи созданы для каждого ADR (если config требует)
|
||||||
|
- [ ] `.agent/task-manifest.json` и `.agent/task-manifest.md` созданы
|
||||||
|
- [ ] checkpoints.json обновлён
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# Протокол 04: Настройка окружения (SETUP)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Обеспечить рабочее окружение, в котором исполнительный агент может сразу выполнять задачи.
|
||||||
|
|
||||||
|
## Вход
|
||||||
|
|
||||||
|
- `.agent/analysis-report.md`
|
||||||
|
- `.agent/design-report.md` (опционально, для greenfield)
|
||||||
|
- `.agent/task-manifest.json`
|
||||||
|
- `.agent/checkpoints.json` (фаза environment: pending)
|
||||||
|
|
||||||
|
## Поведение в зависимости от типа проекта
|
||||||
|
|
||||||
|
Фаза SETUP работает по-разному для `existing` и `greenfield/scaffold` проектов.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ветка A: existing/scaffold проект
|
||||||
|
|
||||||
|
### 4A.1. Зависимости
|
||||||
|
|
||||||
|
- Установить все зависимости согласно документации проекта
|
||||||
|
- Если есть `requirements.txt`, `pyproject.toml`, `package.json`, `Cargo.toml` и т.д. — выполнить установку
|
||||||
|
- Если в проекте используется виртуальное окружение (venv, .venv, conda) — активировать или создать
|
||||||
|
- Если в проекте используется Docker — проверить, что образ собирается
|
||||||
|
|
||||||
|
**Правило:** если установка зависимостей требует нестандартных шагов, описанных в README — строго следовать им. Если шаги не описаны — запросить у пользователя.
|
||||||
|
|
||||||
|
### 4A.2. Конфигурация
|
||||||
|
|
||||||
|
- Проверить наличие конфигурационных файлов (`.env.example`, `.env`, `config.yaml`)
|
||||||
|
- Если есть `.env.example`, скопировать в `.env` с настройками по умолчанию
|
||||||
|
- Если проекту требуется БД — проверить строку подключения, при необходимости создать БД или использовать SQLite для разработки
|
||||||
|
- Настроить pre-commit хуки, если они есть в проекте
|
||||||
|
|
||||||
|
### 4A.3. Линтеры и форматтеры
|
||||||
|
|
||||||
|
- Запустить линтер на всём проекте: записать результат
|
||||||
|
- Если линтер выдаёт ошибки — не исправлять, только зафиксировать в отчёте
|
||||||
|
- Убедиться, что исполнительный агент может запускать линтер (записать команду)
|
||||||
|
|
||||||
|
### 4A.4. Baseline-тесты
|
||||||
|
|
||||||
|
- Запустить все тесты проекта
|
||||||
|
- Записать в `.agent/baseline-test-report.log`:
|
||||||
|
- Команда запуска
|
||||||
|
- Общее количество тестов
|
||||||
|
- Пройдено / упало / пропущено
|
||||||
|
- Время выполнения
|
||||||
|
- Список упавших тестов (если есть)
|
||||||
|
- Если тесты не проходят — указать это в отчёте, но **не исправлять**
|
||||||
|
|
||||||
|
### 4A.5. Сборка проекта
|
||||||
|
|
||||||
|
- Выполнить полную сборку/компиляцию проекта
|
||||||
|
- Записать результат (успех/ошибка с логом)
|
||||||
|
- Сборка должна проходить без ошибок. Если не собирается — остановиться, сообщить пользователю.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ветка B: greenfield проект
|
||||||
|
|
||||||
|
### 4B.1. Инициализация проекта
|
||||||
|
|
||||||
|
- Создать базовую структуру директорий согласно design-report.md
|
||||||
|
- Инициализировать пакетный менеджер:
|
||||||
|
- Python: `pyproject.toml` (poetry, pdm, hatch) или `requirements.txt`
|
||||||
|
- Node: `package.json` и `npm init` / `yarn init`
|
||||||
|
- Go: `go mod init`
|
||||||
|
- Rust: `cargo init`
|
||||||
|
- Настроить базовый конфиг: `.env.example`, `config/` и т.д.
|
||||||
|
- Настроить линтер/форматтер: `ruff`, `eslint`, `gofmt` и т.д.
|
||||||
|
|
||||||
|
### 4B.2. Scaffold-код
|
||||||
|
|
||||||
|
Создать пустые заглушки для модулей, описанных в design-report:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# app/services/cashflow.py — заглушка
|
||||||
|
class CashflowService:
|
||||||
|
"""TBD — реализация в задаче T3"""
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
Назначение: фиксировать структуру, чтобы исполнительный агент не думал о ней, а сразу писал реализацию.
|
||||||
|
|
||||||
|
### 4B.3. Установка зависимостей
|
||||||
|
|
||||||
|
- Установить базовые зависимости согласно стеку из design-report
|
||||||
|
- Если проект использует БД — установить драйвер/ORM
|
||||||
|
- Если проект использует API — установить фреймворк (FastAPI, Express и т.д.)
|
||||||
|
- Установить dev-зависимости: линтер, тестовый раннер, type stubs
|
||||||
|
|
||||||
|
### 4B.4. Базовые тесты (scaffold)
|
||||||
|
|
||||||
|
- Создать пустой тестовый файл для каждого модуля
|
||||||
|
- Настроить тестовый раннер (pytest, jest и т.д.)
|
||||||
|
- Записать в `.agent/baseline-test-report.log`: "0 tests — greenfield, scaffold готов"
|
||||||
|
|
||||||
|
### 4B.5. Проверка сборки
|
||||||
|
|
||||||
|
- Убедиться, что проект импортируется без ошибок
|
||||||
|
- Убедиться, что линтер проходит (без кода он должен проходить)
|
||||||
|
- Убедиться, что тестовый раннер запускается (0 tests, exit code 0)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Выход
|
||||||
|
|
||||||
|
- Работоспособное окружение / инициализированный проект
|
||||||
|
- `.agent/baseline-test-report.log` — результат прогона тестов
|
||||||
|
- `.agent/setup-report.log` — лог установки зависимостей и сборки
|
||||||
|
|
||||||
|
Обновить checkpoints.json: `phases.environment = "completed"`.
|
||||||
|
|
||||||
|
## Критерии завершения фазы
|
||||||
|
|
||||||
|
- [ ] Зависимости установлены / проект инициализирован
|
||||||
|
- [ ] Проект собирается / импортируется без ошибок
|
||||||
|
- [ ] Baseline-тесты запущены, результат записан
|
||||||
|
- [ ] `.agent/baseline-test-report.log` и `.agent/setup-report.log` созданы
|
||||||
|
- [ ] checkpoints.json обновлён
|
||||||
|
|
||||||
|
Если проект не собирается — **фаза считается проваленной**, checkpoints.json отмечает `phases.environment = "failed"`, управление возвращается пользователю.
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
# Протокол 05: Передача исполнительному агенту (HANDOFF)
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Подготовить и передать исполнительному агенту полный контекст для работы: задачи, окружение, правила.
|
||||||
|
|
||||||
|
## Вход
|
||||||
|
|
||||||
|
- `.agent/analysis-report.md`
|
||||||
|
- `.agent/design-report.md` (опционально, для greenfield)
|
||||||
|
- `.agent/layer-1/adr/*.md` (опционально)
|
||||||
|
- `.agent/layer-1/risk-register.md` (опционально)
|
||||||
|
- `.agent/layer-1/red-team-report.md` (опционально)
|
||||||
|
- `.agent/task-manifest.json`
|
||||||
|
- `.agent/task-manifest.md`
|
||||||
|
- `.agent/baseline-test-report.log`
|
||||||
|
- `.agent/checkpoints.json` (все предыдущие фазы: completed)
|
||||||
|
|
||||||
|
## Шаги
|
||||||
|
|
||||||
|
### 5.1. Архивация завершённых артефактов
|
||||||
|
|
||||||
|
Перед валидацией и передачей выполнить архивирование.
|
||||||
|
|
||||||
|
**Архивировать завершённые задачи:**
|
||||||
|
|
||||||
|
Для каждой задачи в `task-manifest.json` со статусом `completed`:
|
||||||
|
1. Создать `.agent/archive/tasks/<id>.json` — перенести полное описание задачи (все поля)
|
||||||
|
2. В `task-manifest.json` заменить задачу на one-liner:
|
||||||
|
```json
|
||||||
|
{ "id": "<id>", "title": "<title>", "status": "archived" }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Архивировать чекпоинты:**
|
||||||
|
|
||||||
|
Если `checkpoints.json` уже существует — сохранить предыдущую версию в `.agent/archive/checkpoints/<last_updated>.json`.
|
||||||
|
|
||||||
|
**Создать индекс архива:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "1.1.0",
|
||||||
|
"archived_at": "<timestamp>",
|
||||||
|
"tasks": [
|
||||||
|
{ "id": "T1", "title": "...", "archived_at": "<timestamp>" }
|
||||||
|
],
|
||||||
|
"checkpoints": [
|
||||||
|
{ "file": "checkpoints/2026-07-15T10-00-00.json", "archived_at": "<timestamp>" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2. Валидация
|
||||||
|
|
||||||
|
Перед передачей проверить:
|
||||||
|
|
||||||
|
- [ ] Все фазы отмечены как `completed` в checkpoints.json
|
||||||
|
- [ ] `.agent/` содержит все обязательные файлы:
|
||||||
|
- `checkpoints.json`
|
||||||
|
- `analysis-report.md`
|
||||||
|
- `task-manifest.json` + `task-manifest.md`
|
||||||
|
- `baseline-test-report.log`
|
||||||
|
- `setup-report.log`
|
||||||
|
- `src/META_AGENT_GUIDE.md`
|
||||||
|
- `src/BOUNDARIES.md`
|
||||||
|
- `src/VERSION`
|
||||||
|
- `src/PROTOCOLS/`
|
||||||
|
- `src/TEMPLATES/`
|
||||||
|
- `rules/project-rules.md`
|
||||||
|
- `archive/index.json`
|
||||||
|
- [ ] Для greenfield: `design-report.md` присутствует
|
||||||
|
- [ ] В task-manifest.json нет циклических зависимостей
|
||||||
|
- [ ] Все acceptance criteria сформулированы измеримо
|
||||||
|
- [ ] Для каждой задачи указаны affected files
|
||||||
|
- [ ] В репозитории нет незакоммиченных изменений (кроме `.agent/`)
|
||||||
|
- [ ] `.agent/src/` содержит актуальные исходники MetaAgent (META_AGENT_GUIDE.md, PROTOCOLS/, TEMPLATES/, BOUNDARIES.md, VERSION)
|
||||||
|
- [ ] `AGENTS.md` присутствует в корне репозитория
|
||||||
|
- [ ] `.agent/rules/` содержит `project-rules.md`
|
||||||
|
|
||||||
|
**Дополнительные проверки (если config включает):**
|
||||||
|
- [ ] ADR присутствуют (если adr=yes)
|
||||||
|
- [ ] Risk Register заполнен (если risk_register=yes)
|
||||||
|
- [ ] Red Team Report есть (если red_team=yes)
|
||||||
|
- [ ] Invariant-задачи в манифесте (если invariant_tests=yes)
|
||||||
|
|
||||||
|
### 5.3. Layer-структура .agent/
|
||||||
|
|
||||||
|
Если `config.layer_structure = yes`, организовать артефакты по слоям:
|
||||||
|
|
||||||
|
```
|
||||||
|
.agent/
|
||||||
|
layer-0/
|
||||||
|
checkpoints.json # всегда (ядро)
|
||||||
|
session-summary.md # краткая сводка сессии (создаётся здесь)
|
||||||
|
layer-1/
|
||||||
|
adr/ # ADR (опционально)
|
||||||
|
risk-register.md # (опционально)
|
||||||
|
red-team-report.md # (опционально)
|
||||||
|
layer-2/
|
||||||
|
analysis-report.md
|
||||||
|
design-report.md
|
||||||
|
design-report.md
|
||||||
|
layer-3/
|
||||||
|
handoff-summary.md
|
||||||
|
task-manifest.json
|
||||||
|
task-manifest.md
|
||||||
|
baseline-test-report.log
|
||||||
|
setup-report.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Если `layer_structure = no` — артефакты остаются плоскими в `.agent/`, как раньше.
|
||||||
|
|
||||||
|
### 5.4. Создать handoff-summary.md
|
||||||
|
|
||||||
|
Заполнить по шаблону `TEMPLATES/handoff-summary.md`:
|
||||||
|
|
||||||
|
- **Session Info** — ID, цель, дата
|
||||||
|
- **Configuration** — какие функции были включены, глубина
|
||||||
|
- **Repo Summary** — краткая выжимка из analysis-report
|
||||||
|
- **Environment Status** — результат сборки и тестов
|
||||||
|
- **Design Summary** (если есть design-report) — ключевые архитектурные решения
|
||||||
|
- **ADR Summary** (если adr=yes) — какие решения задокументированы
|
||||||
|
- **Risk Register** (если risk_register=yes) — основные допущения
|
||||||
|
- **Task Overview** — количество задач, типы, список
|
||||||
|
- **Next Steps** — с какой задачи начинать исполнительному агенту
|
||||||
|
- **Project Rules** — ссылка на `.agent/rules/project-rules.md` (передаётся exec-агенту)
|
||||||
|
- **Archive** — ссылка на `.agent/archive/index.json` (история завершённых задач)
|
||||||
|
- **Caveats** — известные проблемы, ограничения, неясные моменты
|
||||||
|
- **Checkpoints** — актуальное состояние чекпоинтов
|
||||||
|
|
||||||
|
### 5.5. Финализировать checkpoints
|
||||||
|
|
||||||
|
- Отметить `phases.handoff = "completed"`
|
||||||
|
- Записать финальный `last_updated`
|
||||||
|
|
||||||
|
### 5.6. Сигнал
|
||||||
|
|
||||||
|
Сообщить пользователю/оркестратору:
|
||||||
|
|
||||||
|
```
|
||||||
|
HANDOFF COMPLETE
|
||||||
|
|
||||||
|
Session: <session_id>
|
||||||
|
Target: <target_repo>
|
||||||
|
Type: <existing | greenfield | scaffold>
|
||||||
|
Config: depth=<N>, adr=<yes|no>, red_team=<yes|no>, ...
|
||||||
|
Tasks: <count> tasks ready
|
||||||
|
|
||||||
|
Исполнительный агент может начинать с задачи <T1>.
|
||||||
|
Контекст: .agent/handoff-summary.md
|
||||||
|
Манифест: .agent/task-manifest.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Что получает исполнительный агент
|
||||||
|
|
||||||
|
1. **Целевой репозиторий** — полностью настроенный, с установленными зависимостями
|
||||||
|
2. **`.agent/`** — директория со всеми артефактами (layer-структура или плоская)
|
||||||
|
3. **`task-manifest.json`** — машиночитаемый список задач
|
||||||
|
4. **`task-manifest.md`** — человекочитаемый список задач
|
||||||
|
5. **`handoff-summary.md`** — итоговая сводка
|
||||||
|
6. **`checkpoints.json`** — актуальное состояние (исполнительный агент будет его обновлять)
|
||||||
|
7. **`layer-1/adr/*.md`** (опционально) — ключевые решения
|
||||||
|
8. **`layer-1/risk-register.md`** (опционально) — допущения
|
||||||
|
9. **`layer-2/analysis-report.md`** — полный анализ репозитория (справочно)
|
||||||
|
10. **`layer-2/design-report.md`** (только для greenfield) — архитектурный план
|
||||||
|
11. **`layer-3/baseline-test-report.log`** — baseline тестов (чтобы не сломать существующее)
|
||||||
|
12. **`.agent/src/`** — полные исходники MetaAgent (справочно, всегда присутствуют)
|
||||||
|
13. **`.agent/rules/`** — пользовательские правила проекта
|
||||||
|
14. **`AGENTS.md`** — инструкция для AI-агента в корне проекта (всегда присутствует)
|
||||||
|
15. **`.agent/archive/`** — архив завершённых задач, чекпоинтов и устаревших артефактов
|
||||||
|
|
||||||
|
## Выход
|
||||||
|
|
||||||
|
- `.agent/layer-0/session-summary.md` (если layer_structure=yes)
|
||||||
|
- `.agent/layer-3/handoff-summary.md`
|
||||||
|
- `.agent/layer-0/checkpoints.json` (финальный)
|
||||||
|
- `.agent/archive/index.json` (создаётся при архивации)
|
||||||
|
|
||||||
|
## Критерии завершения
|
||||||
|
|
||||||
|
- [ ] Все артефакты на месте (с учётом layer-структуры)
|
||||||
|
- [ ] `.agent/src/` содержит актуальные исходники MetaAgent
|
||||||
|
- [ ] `.agent/rules/` содержит `project-rules.md`
|
||||||
|
- [ ] `AGENTS.md` присутствует в корне репозитория
|
||||||
|
- [ ] `.agent/archive/index.json` создан, завершённые задачи архивированы
|
||||||
|
- [ ] handoff-summary.md заполнен (включая config, design summary, ADR summary, archive)
|
||||||
|
- [ ] checkpoints.json финализирован
|
||||||
|
- [ ] Сигнал отправлен пользователю/оркестратору
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# ADR-NNNN: <Заголовок решения>
|
||||||
|
|
||||||
|
**Статус:** proposed | accepted | deprecated | superseded
|
||||||
|
|
||||||
|
**Дата:** {{ date }}
|
||||||
|
|
||||||
|
**Контекст:** почему возникла необходимость в решении, какая проблема решается.
|
||||||
|
|
||||||
|
**Рассматриваемые альтернативы:**
|
||||||
|
1. Вариант A — описание
|
||||||
|
2. Вариант B — описание
|
||||||
|
3. Вариант C — описание
|
||||||
|
|
||||||
|
**Решение:** выбран вариант <A/B/C>.
|
||||||
|
|
||||||
|
**Обоснование:** почему выбран именно этот вариант (критерии: сложность, поддерживаемость, производительность, совместимость).
|
||||||
|
|
||||||
|
**Последствия:**
|
||||||
|
- Позитивные: ...
|
||||||
|
- Негативные: ...
|
||||||
|
- Риски: ...
|
||||||
|
|
||||||
|
**Invariant (если применимо):** ключевое правило, которое не должен нарушать исполнительный агент. Если можно — ссылка на тест, проверяющий invariant.
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# Analysis Report
|
||||||
|
|
||||||
|
## Session
|
||||||
|
|
||||||
|
- **Session ID:** `{{ session_id }}`
|
||||||
|
- **Target repo:** `{{ target_repo }}`
|
||||||
|
- **Date:** {{ date }}
|
||||||
|
- **Project type:** `{{ project_type }}` (existing / greenfield / scaffold)
|
||||||
|
|
||||||
|
## 1. Общая информация
|
||||||
|
|
||||||
|
- **README:** {{ readme_summary }}
|
||||||
|
- **Лицензия:** {{ license }}
|
||||||
|
- **CI/CD:** {{ ci_cd }}
|
||||||
|
- **Точка входа:** {{ entry_point }}
|
||||||
|
- **Система сборки:** {{ build_system }}
|
||||||
|
|
||||||
|
{% if project_type == "existing" or project_type == "scaffold" %}
|
||||||
|
## 2. Стек технологий
|
||||||
|
|
||||||
|
| Компонент | Значение |
|
||||||
|
|---|---|
|
||||||
|
| Язык | {{ language }} |
|
||||||
|
| Фреймворк | {{ framework }} |
|
||||||
|
| База данных | {{ database }} |
|
||||||
|
| Тестовый раннер | {{ test_runner }} |
|
||||||
|
| Пакетный менеджер | {{ package_manager }} |
|
||||||
|
| Линтер/форматтер | {{ linter }} |
|
||||||
|
|
||||||
|
## 3. Архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
{{ directory_tree }}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Паттерн:** {{ architecture_pattern }}
|
||||||
|
|
||||||
|
**Ключевые модули:**
|
||||||
|
|
||||||
|
| Модуль | Описание |
|
||||||
|
|---|---|
|
||||||
|
| {{ module }} | {{ description }} |
|
||||||
|
|
||||||
|
## 4. Конвенции
|
||||||
|
|
||||||
|
- **Стиль:** {{ code_style }}
|
||||||
|
- **Импорты:** {{ import_style }}
|
||||||
|
- **Типизация:** {{ typing_usage }}
|
||||||
|
- **Обработка ошибок:** {{ error_handling }}
|
||||||
|
- **Логирование:** {{ logging }}
|
||||||
|
|
||||||
|
## 5. Тесты
|
||||||
|
|
||||||
|
- **Команда запуска:** `{{ test_command }}`
|
||||||
|
- **Всего тестов:** {{ total_tests }}
|
||||||
|
- **Пройдено:** {{ passed }}
|
||||||
|
- **Упало:** {{ failed }}
|
||||||
|
- **Пропущено:** {{ skipped }}
|
||||||
|
- **Упавшие тесты:**
|
||||||
|
{% for test in failed_tests %}
|
||||||
|
- `{{ test }}`
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
## 6. Базовая проверка
|
||||||
|
|
||||||
|
- **Сборка:** {{ build_status }}
|
||||||
|
- **Запуск:** {{ run_status }}
|
||||||
|
- **Git status:** {{ git_status }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if project_type == "greenfield" or project_type == "scaffold" %}
|
||||||
|
## 7. Требования (из README)
|
||||||
|
|
||||||
|
### Функциональные требования
|
||||||
|
|
||||||
|
{% for req in functional_requirements %}
|
||||||
|
- {{ req }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
### Нефункциональные требования
|
||||||
|
|
||||||
|
{% for req in non_functional_requirements %}
|
||||||
|
- {{ req }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
### Бизнес-контекст
|
||||||
|
|
||||||
|
{% for item in business_context %}
|
||||||
|
- {{ item }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
### Неясные моменты / Вопросы
|
||||||
|
|
||||||
|
{% for question in open_questions %}
|
||||||
|
- {{ question }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
## 8. Примечания
|
||||||
|
|
||||||
|
{{ notes }}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# Design Report
|
||||||
|
|
||||||
|
## Session
|
||||||
|
|
||||||
|
- **Session ID:** `{{ session_id }}`
|
||||||
|
- **Target repo:** `{{ target_repo }}`
|
||||||
|
- **Date:** {{ date }}
|
||||||
|
|
||||||
|
## 1. Технологический стек
|
||||||
|
|
||||||
|
| Компонент | Выбор | Обоснование |
|
||||||
|
|---|---|---|
|
||||||
|
| Язык | {{ language }} | {{ language_rationale }} |
|
||||||
|
| Фреймворк | {{ framework }} | {{ framework_rationale }} |
|
||||||
|
| База данных | {{ database }} | {{ database_rationale }} |
|
||||||
|
| Инфраструктура | {{ infrastructure }} | {{ infrastructure_rationale }} |
|
||||||
|
|
||||||
|
## 2. High-Level архитектура
|
||||||
|
|
||||||
|
**Паттерн:** {{ architecture_pattern }}
|
||||||
|
|
||||||
|
```
|
||||||
|
{{ architecture_diagram }}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Поток данных:**
|
||||||
|
1. {{ data_flow_step_1 }}
|
||||||
|
2. {{ data_flow_step_2 }}
|
||||||
|
3. {{ data_flow_step_3 }}
|
||||||
|
|
||||||
|
## 3. Модули
|
||||||
|
|
||||||
|
| Модуль | Ответственность | Ключевые компоненты | Зависит от |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `{{ module_path }}` | {{ responsibility }} | {{ components }} | {{ dependencies }} |
|
||||||
|
|
||||||
|
## 4. Модели данных
|
||||||
|
|
||||||
|
### Сущности
|
||||||
|
|
||||||
|
{% for entity in entities %}
|
||||||
|
### {{ entity.name }}
|
||||||
|
|
||||||
|
| Поле | Тип | Ограничения | Описание |
|
||||||
|
|---|---|---|---|
|
||||||
|
{% for field in entity.fields %}
|
||||||
|
| {{ field.name }} | {{ field.type }} | {{ field.constraints }} | {{ field.description }} |
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
**Связи:** {{ entity.relationships }}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
## 5. API / Интерфейсы
|
||||||
|
|
||||||
|
{% if has_api %}
|
||||||
|
| Метод | Путь | Описание | Request | Response |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
{% for endpoint in api_endpoints %}
|
||||||
|
| {{ endpoint.method }} | {{ endpoint.path }} | {{ endpoint.description }} | {{ endpoint.request }} | {{ endpoint.response }} |
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if has_gui %}
|
||||||
|
**Экраны:** {{ gui_screens }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if has_cli %}
|
||||||
|
**Команды:** {{ cli_commands }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
## 6. Обработка ошибок
|
||||||
|
|
||||||
|
- **Стратегия:** {{ error_strategy }}
|
||||||
|
- **Формат ошибок:** {{ error_format }}
|
||||||
|
- **Логирование:** {{ logging_strategy }}
|
||||||
|
|
||||||
|
## 7. Тестирование
|
||||||
|
|
||||||
|
- **Unit-тесты:** {{ unit_test_strategy }}
|
||||||
|
- **Integration-тесты:** {{ integration_test_strategy }}
|
||||||
|
- **Mock-стратегия:** {{ mock_strategy }}
|
||||||
|
- **Команда запуска:** `{{ test_command }}`
|
||||||
|
|
||||||
|
## 8. Alternative Architecture (если применимо)
|
||||||
|
|
||||||
|
| Критерий | Выбранная архитектура | Альтернатива |
|
||||||
|
|---|---|---|
|
||||||
|
| Название | {{ chosen_arch }} | {{ alt_arch }} |
|
||||||
|
| Сложность | {{ chosen_complexity }} | {{ alt_complexity }} |
|
||||||
|
| Почему не выбрана | — | {{ alt_rejection_reason }} |
|
||||||
|
|
||||||
|
## 9. ADR Reference (если применимо)
|
||||||
|
|
||||||
|
| ID | Решение | Файл |
|
||||||
|
|---|---|---|
|
||||||
|
{% for adr in adr_list %}
|
||||||
|
| {{ adr.id }} | {{ adr.title }} | `{{ adr.path }}` |
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
## 10. Risk Register (если применимо)
|
||||||
|
|
||||||
|
| # | Assumption | Impact | Mitigation |
|
||||||
|
|---|---|---|---|
|
||||||
|
{% for risk in risk_list %}
|
||||||
|
| {{ risk.id }} | {{ risk.assumption }} | {{ risk.impact }} | {{ risk.mitigation }} |
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
## 11. Предварительная группировка задач
|
||||||
|
|
||||||
|
| Задача | Описание | Тип |
|
||||||
|
|---|---|---|
|
||||||
|
| T1 | {{ task_1 }} | config |
|
||||||
|
| T2 | {{ task_2 }} | feature |
|
||||||
|
| T3 | {{ task_3 }} | feature |
|
||||||
|
| T4 | {{ task_4 }} | test |
|
||||||
|
|
||||||
|
## 12. Примечания
|
||||||
|
|
||||||
|
{{ notes }}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Handoff Summary
|
||||||
|
|
||||||
|
## Session Info
|
||||||
|
|
||||||
|
- **Session ID:** `{{ session_id }}`
|
||||||
|
- **Target Repo:** `{{ target_repo }}`
|
||||||
|
- **Goal:** {{ goal }}
|
||||||
|
- **Date:** {{ date }}
|
||||||
|
- **Duration:** {{ duration }}
|
||||||
|
- **Depth:** {{ depth }}
|
||||||
|
- **Config:** {{ config_summary }}
|
||||||
|
|
||||||
|
## Repo Summary
|
||||||
|
|
||||||
|
{{ repo_summary }}
|
||||||
|
|
||||||
|
## Project Type
|
||||||
|
|
||||||
|
- **Type:** {{ project_type }}
|
||||||
|
- **Design report:** {% if project_type == "greenfield" or project_type == "scaffold" %}`.agent/design-report.md`{% else %}—{% endif %}
|
||||||
|
|
||||||
|
## ADR Summary (если применимо)
|
||||||
|
|
||||||
|
{% if adr_count > 0 %}
|
||||||
|
Создано ADR: {{ adr_count }}
|
||||||
|
{% for adr in adr_list %}
|
||||||
|
- `{{ adr.path }}` — {{ adr.title }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
## Risk Register (если применимо)
|
||||||
|
|
||||||
|
{% if risk_count > 0 %}
|
||||||
|
Задокументировано допущений: {{ risk_count }}
|
||||||
|
Наиболее критичное: {{ top_risk }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
## Environment Status
|
||||||
|
|
||||||
|
- **Build:** {{ build_status }}
|
||||||
|
- **Tests:** {{ tests_passed }}/{{ tests_total }} passed
|
||||||
|
- **Baseline log:** `.agent/baseline-test-report.log`
|
||||||
|
- **Dependencies:** {{ deps_status }}
|
||||||
|
|
||||||
|
## Task Overview
|
||||||
|
|
||||||
|
| Status | Count |
|
||||||
|
|---|---|
|
||||||
|
| Total | {{ total }} |
|
||||||
|
| Pending | {{ pending }} |
|
||||||
|
| In Progress | {{ in_progress }} |
|
||||||
|
| Completed | {{ completed }} |
|
||||||
|
| Failed/Skipped | {{ failed }} |
|
||||||
|
|
||||||
|
**Task by type:**
|
||||||
|
{% for type, count in tasks_by_type %}
|
||||||
|
- {{ type }}: {{ count }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
## Tasks (ordered)
|
||||||
|
|
||||||
|
{% for task in tasks %}
|
||||||
|
### {{ task.id }}: {{ task.title }}
|
||||||
|
- Type: {{ task.type }}
|
||||||
|
- Depends on: {{ task.depends_on | default("—") }}
|
||||||
|
- Files: {{ task.files | join(", ") }}
|
||||||
|
- Status: {{ task.status }}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
Исполнительный агент начинает с задачи **{{ first_task }}**.
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
{% for caveat in caveats %}
|
||||||
|
- {{ caveat }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
## Checkpoints
|
||||||
|
|
||||||
|
Файл: `.agent/checkpoints.json`
|
||||||
|
Актуальное состояние чекпоинтов прилагается.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# MetaAgent Request
|
||||||
|
# Для ручного заполнения перед запуском MetaAgent.
|
||||||
|
# Поместите этот файл в .agent/metaagent-request.md целевого репозитория.
|
||||||
|
# Если файл отсутствует — MetaAgent проведёт интервью (PROTOCOLS/00_CONFIG.md).
|
||||||
|
# Ответьте "default" на любой вопрос — будет использовано значение по умолчанию.
|
||||||
|
|
||||||
|
## Параметры сессии
|
||||||
|
|
||||||
|
| Функция | Вкл | Аргументы |
|
||||||
|
|---|---|---|
|
||||||
|
| ANALYSIS | ✓ | — |
|
||||||
|
| DESIGN | ✓ | adr=yes, alternative_arch=yes |
|
||||||
|
| RED_TEAM | ✗ | — |
|
||||||
|
| RISK_REGISTER | ✗ | — |
|
||||||
|
| DECOMPOSITION | ✓ | invariant_tests=yes |
|
||||||
|
| SETUP | ✓ | — |
|
||||||
|
| HANDOFF | ✓ | layer_structure=yes |
|
||||||
|
|
||||||
|
## Глубина проработки
|
||||||
|
|
||||||
|
**Значение:** 6 (1-10)
|
||||||
|
|
||||||
|
| Уровень | Название | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| 1-2 | Scaffold | Только структура проекта + пустые модули |
|
||||||
|
| 3-4 | Light | (default) Быстрый дизайн + задачи без расширений |
|
||||||
|
| 5-6 | Standard | Полный ANALYSIS→DESIGN→DECOMP→SETUP→HANDOFF |
|
||||||
|
| 7-8 | Deep | Standard + ADR, Risk Register, Alternative Architecture |
|
||||||
|
| 9-10 | Maximum | Deep + Red Team Review, Executable Invariants |
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Сформулируйте задачу для MetaAgent.
|
||||||
|
|
||||||
|
## Дополнительно
|
||||||
|
|
||||||
|
- **Boundaries:** (опционально) ограничения, которые нельзя нарушать
|
||||||
|
- **Target:** путь к репозиторию или URL
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Project Rules
|
||||||
|
|
||||||
|
Правила, которым агент обязан следовать во всех фазах.
|
||||||
|
Добавляйте сюда условия, которые должны соблюдаться всегда — они будут прочитаны
|
||||||
|
перед началом каждой фазы и учтены при декомпозиции и реализации.
|
||||||
|
|
||||||
|
## Обязательные правила
|
||||||
|
|
||||||
|
- (укажите правила, например: «Всегда использовать tabs для отступов»)
|
||||||
|
|
||||||
|
## Запреты
|
||||||
|
|
||||||
|
- (укажите запреты, например: «Не трогать CI/CD конфигурацию»)
|
||||||
|
|
||||||
|
## Конвенции проекта
|
||||||
|
|
||||||
|
- (укажите конвенции, например: «Имена классов в PascalCase, функции в snake_case»)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Risk Register
|
||||||
|
|
||||||
|
| # | Assumption | Impact if wrong | Mitigation | Review trigger |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| R1 | Пользователи имеют Python 3.11+ | Проект не запускается на старых версиях | Указать требование в README, CI-проверка | При жалобе на установку |
|
||||||
|
| R2 | JSON-файлы не превышают 10MB | Деградация производительности | Добавить лимит в model.py | При первом замедлении |
|
||||||
|
| R3 | ... | ... | ... | ... |
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Session Summary
|
||||||
|
|
||||||
|
**Session:** {{ session_id }}
|
||||||
|
**Target:** {{ target_repo }}
|
||||||
|
**Depth:** {{ depth }}
|
||||||
|
**Date:** {{ date }}
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
| Функция | Статус |
|
||||||
|
|---|---|
|
||||||
|
| ADR | {{ adr_enabled }} |
|
||||||
|
| Alternative Architecture | {{ alt_arch_enabled }} |
|
||||||
|
| Red Team | {{ red_team_enabled }} |
|
||||||
|
| Risk Register | {{ risk_register_enabled }} |
|
||||||
|
| Invariant Tests | {{ invariant_tests_enabled }} |
|
||||||
|
| Layer Structure | {{ layer_structure_enabled }} |
|
||||||
|
|
||||||
|
## Phase Status
|
||||||
|
|
||||||
|
| Phase | Status |
|
||||||
|
|---|---|
|
||||||
|
| ANALYSIS | {{ analysis_status }} |
|
||||||
|
| DESIGN | {{ design_status }} |
|
||||||
|
| RED_TEAM | {{ red_team_status }} |
|
||||||
|
| DECOMPOSITION | {{ decomposition_status }} |
|
||||||
|
| SETUP | {{ setup_status }} |
|
||||||
|
| HANDOFF | {{ handoff_status }} |
|
||||||
|
|
||||||
|
## Quick Links
|
||||||
|
|
||||||
|
- Task Manifest: `.agent/task-manifest.json`
|
||||||
|
- Handoff Summary: `.agent/handoff-summary.md`
|
||||||
|
- Design Report: `.agent/analysis-report.md`
|
||||||
|
- ADR: `.agent/layer-1/adr/` (если есть)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "metaagent-task-manifest",
|
||||||
|
"version": "1.0",
|
||||||
|
"session_id": "{{ session_id }}",
|
||||||
|
"goal": "{{ goal }}",
|
||||||
|
"created_at": "{{ timestamp }}",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"id": "T1",
|
||||||
|
"title": "{{ task_title }}",
|
||||||
|
"description": "{{ task_description }}",
|
||||||
|
"type": "feature|refactor|test|fix|config|docs",
|
||||||
|
"files": ["path/to/file1.py", "path/to/file2.py"],
|
||||||
|
"depends_on": [],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"Критерий 1: ...",
|
||||||
|
"Критерий 2: ..."
|
||||||
|
],
|
||||||
|
"context": "Дополнительная информация",
|
||||||
|
"status": "pending"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Task Manifest
|
||||||
|
|
||||||
|
**Session:** {{ session_id }}
|
||||||
|
**Goal:** {{ goal }}
|
||||||
|
**Date:** {{ timestamp }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task Overview
|
||||||
|
|
||||||
|
| ID | Title | Type | Depends On | Status |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| T1 | {{ title }} | {{ type }} | — | pending |
|
||||||
|
| T2 | {{ title }} | {{ type }} | T1 | pending |
|
||||||
|
|
||||||
|
**Total tasks:** {{ count }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task Details
|
||||||
|
|
||||||
|
### T1: {{ title }}
|
||||||
|
|
||||||
|
**Type:** {{ type }}
|
||||||
|
**Description:** {{ description }}
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `{{ file_path }}`
|
||||||
|
|
||||||
|
**Depends on:** —
|
||||||
|
|
||||||
|
**Acceptance Criteria:**
|
||||||
|
- [ ] {{ criterion }}
|
||||||
|
- [ ] {{ criterion }}
|
||||||
|
|
||||||
|
**Context:** {{ context }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T2: {{ title }}
|
||||||
|
|
||||||
|
...
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1.1.0
|
||||||
@@ -0,0 +1,356 @@
|
|||||||
|
# WORKFLOW — Сквозной пример сессии
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сценарий A: Existing проект
|
||||||
|
|
||||||
|
**Цель:** Добавить в существующий FastAPI-проект ручку GET /health с тестами.
|
||||||
|
|
||||||
|
**Целевой репозиторий:** `github.com/example/fastapi-app`
|
||||||
|
|
||||||
|
**Пользователь:** "Добавь health-check endpoint и тесты к нему"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза INIT
|
||||||
|
|
||||||
|
Мета-агент читает `.agent/metaagent-request.md`, клонирует репозиторий, создаёт `.agent/`, пишет начальный чекпоинт:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metaagent_version": "1.1.0",
|
||||||
|
"session_id": "ses_abc123",
|
||||||
|
"target_repo": "/tmp/fastapi-app",
|
||||||
|
"goal": "Добавить GET /health с тестами",
|
||||||
|
"project_type": "existing",
|
||||||
|
"config": {
|
||||||
|
"depth": 6,
|
||||||
|
"design": { "adr": false, "alternative_arch": false },
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": false,
|
||||||
|
"decomposition": { "invariant_tests": false },
|
||||||
|
"handoff": { "layer_structure": false }
|
||||||
|
},
|
||||||
|
"phases": {
|
||||||
|
"analysis": "pending",
|
||||||
|
"design": "pending",
|
||||||
|
"red_team": "pending",
|
||||||
|
"decomposition": "pending",
|
||||||
|
"environment": "pending",
|
||||||
|
"handoff": "pending"
|
||||||
|
},
|
||||||
|
"tasks": [],
|
||||||
|
"last_updated": "2026-07-12T15:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза ANALYSE
|
||||||
|
|
||||||
|
Мета-агент выполняет `PROTOCOLS/01_ANALYSIS.md`. Определяет тип проекта: `existing`.
|
||||||
|
|
||||||
|
Результат `.agent/analysis-report.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 2. Стек технологий
|
||||||
|
| Язык | Python 3.12 |
|
||||||
|
| Фреймворк | FastAPI |
|
||||||
|
| Тестовый раннер | pytest + httpx |
|
||||||
|
| Пакетный менеджер | pip + requirements.txt |
|
||||||
|
|
||||||
|
## 3. Архитектура
|
||||||
|
├── app/
|
||||||
|
│ ├── main.py
|
||||||
|
│ ├── routers/
|
||||||
|
│ │ └── users.py
|
||||||
|
│ ├── models/
|
||||||
|
│ │ └── user.py
|
||||||
|
│ └── schemas/
|
||||||
|
│ └── user.py
|
||||||
|
├── tests/
|
||||||
|
│ └── test_users.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Тесты запущены: **12 passed, 0 failed**.
|
||||||
|
|
||||||
|
Чекпоинт обновлён: `analysis = "completed"`, `project_type = "existing"`.
|
||||||
|
Фаза DESIGN пропускается.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза DECOMPOSITION
|
||||||
|
|
||||||
|
Мета-агент выполняет `PROTOCOLS/03_DECOMPOSITION.md`.
|
||||||
|
|
||||||
|
Декомпозиция цели "Добавить GET /health с тестами":
|
||||||
|
|
||||||
|
| ID | Задача | Тип | Зависит от | AC |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| T1 | Создать health-check router | feature | — | Ручка возвращает 200 + {"status":"ok"} |
|
||||||
|
| T2 | Подключить router в main.py | config | T1 | Ручка доступна по /health |
|
||||||
|
| T3 | Написать тесты для /health | test | T2 | Тесты проверяют 200 и структуру ответа |
|
||||||
|
|
||||||
|
Создан `.agent/task-manifest.json` и `.agent/task-manifest.md`.
|
||||||
|
|
||||||
|
Чекпоинт обновлён: `decomposition = "completed"`. Tasks: T1-T3 со статусом `pending`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза SETUP
|
||||||
|
|
||||||
|
Мета-агент выполняет `PROTOCOLS/04_ENVIRONMENT_SETUP.md` (ветка A: existing).
|
||||||
|
|
||||||
|
- `pip install -r requirements.txt` — OK
|
||||||
|
- Запуск pytest — OK, 12 passed (базовый тест)
|
||||||
|
- Результат в `.agent/baseline-test-report.log`
|
||||||
|
|
||||||
|
Чекпоинт обновлён: `environment = "completed"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза HANDOFF
|
||||||
|
|
||||||
|
Мета-агент выполняет `PROTOCOLS/05_HANDOFF.md`.
|
||||||
|
|
||||||
|
Создан `.agent/handoff-summary.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Next Steps
|
||||||
|
Исполнительный агент начинает с задачи T1: "Создать health-check router".
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
- Придерживаться стиля существующего роутера users.py
|
||||||
|
- Не менять существующие тесты
|
||||||
|
- Убедиться, что response model соответствует JSON: {"status": "ok"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Чекпоинт финализирован:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metaagent_version": "1.1.0",
|
||||||
|
"session_id": "ses_abc123",
|
||||||
|
"goal": "Добавить GET /health с тестами",
|
||||||
|
"project_type": "existing",
|
||||||
|
"config": {
|
||||||
|
"depth": 6,
|
||||||
|
"design": { "adr": false, "alternative_arch": false },
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": false,
|
||||||
|
"decomposition": { "invariant_tests": false },
|
||||||
|
"handoff": { "layer_structure": false }
|
||||||
|
},
|
||||||
|
"phases": {
|
||||||
|
"analysis": "completed",
|
||||||
|
"design": "skipped",
|
||||||
|
"red_team": "skipped",
|
||||||
|
"decomposition": "completed",
|
||||||
|
"environment": "completed",
|
||||||
|
"handoff": "completed"
|
||||||
|
},
|
||||||
|
"tasks": [
|
||||||
|
{ "id": "T1", "title": "Создать health-check router", "status": "pending" },
|
||||||
|
{ "id": "T2", "title": "Подключить router в main.py", "status": "pending" },
|
||||||
|
{ "id": "T3", "title": "Написать тесты для /health", "status": "pending" }
|
||||||
|
],
|
||||||
|
"last_updated": "2026-07-12T15:15:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Сигнал пользователю:
|
||||||
|
|
||||||
|
```
|
||||||
|
HANDOFF COMPLETE
|
||||||
|
Session: ses_abc123
|
||||||
|
Target: /tmp/fastapi-app
|
||||||
|
Type: existing
|
||||||
|
Tasks: 3 tasks ready
|
||||||
|
|
||||||
|
Исполнительный агент может начинать с задачи T1.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сценарий B: Greenfield проект (Cashflow Forecasting)
|
||||||
|
|
||||||
|
**Цель:** Спроектировать и реализовать MVP сервиса прогнозирования денежных потоков.
|
||||||
|
|
||||||
|
**Целевой репозиторий:** `github.com/example/cashflow-app`
|
||||||
|
|
||||||
|
**README:** README содержит описание:
|
||||||
|
> Сервис для прогнозирования движения денежных средств (cashflow forecasting).
|
||||||
|
> Пользователь загружает CSV с транзакциями, сервис строит прогноз на N дней вперёд.
|
||||||
|
> Стек: Python, FastAPI, SQLite, matplotlib для графиков.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза INIT
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"metaagent_version": "1.1.0",
|
||||||
|
"session_id": "ses_def456",
|
||||||
|
"target_repo": "/tmp/cashflow-app",
|
||||||
|
"goal": "Спроектировать и реализовать MVP сервиса прогнозирования денежных потоков",
|
||||||
|
"project_type": "greenfield",
|
||||||
|
"config": {
|
||||||
|
"depth": 7,
|
||||||
|
"design": { "adr": true, "alternative_arch": true },
|
||||||
|
"red_team": false,
|
||||||
|
"risk_register": true,
|
||||||
|
"decomposition": { "invariant_tests": true },
|
||||||
|
"handoff": { "layer_structure": true }
|
||||||
|
},
|
||||||
|
"phases": {
|
||||||
|
"analysis": "pending",
|
||||||
|
"design": "pending",
|
||||||
|
"red_team": "pending",
|
||||||
|
"decomposition": "pending",
|
||||||
|
"environment": "pending",
|
||||||
|
"handoff": "pending"
|
||||||
|
},
|
||||||
|
"tasks": [],
|
||||||
|
"last_updated": "2026-07-12T16:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза ANALYSE
|
||||||
|
|
||||||
|
Мета-агент выполняет `PROTOCOLS/01_ANALYSIS.md`. Определяет тип проекта: `greenfield`.
|
||||||
|
|
||||||
|
Сканирование корня: пусто (кроме README.md, LICENSE, .gitignore).
|
||||||
|
|
||||||
|
Извлечение требований из README:
|
||||||
|
|
||||||
|
| Тип | Требование |
|
||||||
|
|---|---|
|
||||||
|
| Функциональное | Загрузка CSV с транзакциями |
|
||||||
|
| Функциональное | Прогноз на N дней вперёд |
|
||||||
|
| Нефункциональное | Python, FastAPI |
|
||||||
|
| Нефункциональное | SQLite |
|
||||||
|
| Нефункциональное | matplotlib для графиков |
|
||||||
|
|
||||||
|
Чекпоинт: `analysis = "completed"`, `project_type = "greenfield"`.
|
||||||
|
|
||||||
|
Так как проект greenfield — мета-агент переходит к фазе DESIGN.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза DESIGN
|
||||||
|
|
||||||
|
Мета-агент выполняет `PROTOCOLS/02_DESIGN.md`.
|
||||||
|
|
||||||
|
Результат `.agent/design-report.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 1. Технологический стек
|
||||||
|
| Язык | Python 3.12 |
|
||||||
|
| Фреймворк | FastAPI + Pydantic |
|
||||||
|
| БД | SQLite + SQLAlchemy |
|
||||||
|
| Визуализация | matplotlib |
|
||||||
|
| Тесты | pytest |
|
||||||
|
|
||||||
|
## 2. Архитектура
|
||||||
|
[Client] → HTTP → [FastAPI] → [CashflowService] → [SQLite]
|
||||||
|
↓
|
||||||
|
[ForecastEngine] → [matplotlib]
|
||||||
|
|
||||||
|
## 3. Модули
|
||||||
|
| Модуль | Ответственность |
|
||||||
|
|---|---|
|
||||||
|
| app/main.py | Точка входа, роуты |
|
||||||
|
| app/models/transaction.py | Модель транзакции |
|
||||||
|
| app/services/cashflow.py | Бизнес-логика |
|
||||||
|
| app/services/forecast.py | Алгоритм прогноза |
|
||||||
|
| app/services/upload.py | Парсинг CSV |
|
||||||
|
| app/schemas/ | Pydantic схемы |
|
||||||
|
|
||||||
|
## 4. Модели
|
||||||
|
Transaction: id, date, amount, category, description
|
||||||
|
|
||||||
|
## 5. API
|
||||||
|
POST /upload — загрузить CSV
|
||||||
|
GET /forecast?days=30 — прогноз + график
|
||||||
|
|
||||||
|
## 6. Задачи (pre-grouped)
|
||||||
|
T1: init — проект, зависимости, scaffold
|
||||||
|
T2: models — модели + миграции
|
||||||
|
T3: upload — загрузка CSV
|
||||||
|
T4: forecast — алгоритм прогноза
|
||||||
|
T5: API — endpoints
|
||||||
|
T6: tests — тесты
|
||||||
|
```
|
||||||
|
|
||||||
|
Чекпоинт: `design = "completed"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза DECOMPOSITION
|
||||||
|
|
||||||
|
Мета-агент выполняет `PROTOCOLS/03_DECOMPOSITION.md`, используя design-report.
|
||||||
|
|
||||||
|
Итоговые задачи:
|
||||||
|
|
||||||
|
| ID | Задача | Тип | Зависит от |
|
||||||
|
|---|---|---|---|
|
||||||
|
| T1 | Инициализация проекта + зависимости | config | — |
|
||||||
|
| T2 | Модель Transaction + SQLAlchemy + SQLite | feature | T1 |
|
||||||
|
| T3 | Сервис загрузки и парсинга CSV | feature | T2 |
|
||||||
|
| T4 | ForecastEngine — алгоритм прогноза | feature | T2 |
|
||||||
|
| T5 | API endpoints + документация | feature | T3, T4 |
|
||||||
|
| T6 | Тесты (unit + integration) | test | T5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза SETUP
|
||||||
|
|
||||||
|
Мета-агент выполняет `PROTOCOLS/04_ENVIRONMENT_SETUP.md` (ветка B: greenfield).
|
||||||
|
|
||||||
|
- `poetry init` + создание pyproject.toml
|
||||||
|
- Установка fastapi, uvicorn, sqlalchemy, matplotlib, pytest
|
||||||
|
- Создание scaffold-структуры: `app/models/`, `app/services/`, `app/schemas/`, `tests/`
|
||||||
|
- Пустые заглушки модулей
|
||||||
|
- `.agent/baseline-test-report.log`: "0 tests — greenfield, scaffold готов"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Фаза HANDOFF
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
HANDOFF COMPLETE
|
||||||
|
Session: ses_def456
|
||||||
|
Target: /tmp/cashflow-app
|
||||||
|
Type: greenfield
|
||||||
|
Config: depth=7, adr=yes, risk_register=yes, invariant_tests=yes
|
||||||
|
Tasks: 6 tasks ready
|
||||||
|
|
||||||
|
Исполнительный агент может начинать с задачи T1 (init).
|
||||||
|
Архитектурный план: .agent/design-report.md
|
||||||
|
ADR: .agent/layer-1/adr/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## После HANDOFF: работа исполнительного агента
|
||||||
|
|
||||||
|
Исполнительный агент читает `.agent/handoff-summary.md`, `.agent/task-manifest.json`, выполняет задачи по порядку, обновляя checkpoints.json после каждой.
|
||||||
|
|
||||||
|
После завершения всех задач:
|
||||||
|
|
||||||
|
```
|
||||||
|
ALL TASKS COMPLETE
|
||||||
|
Session: ses_def456
|
||||||
|
Tasks: 6/6 completed
|
||||||
|
|
||||||
|
T1: Инициализация проекта ✓
|
||||||
|
T2: Модель Transaction ✓
|
||||||
|
T3: Сервис загрузки CSV ✓
|
||||||
|
T4: ForecastEngine ✓
|
||||||
|
T5: API endpoints ✓
|
||||||
|
T6: Тесты ✓
|
||||||
|
|
||||||
|
Все тесты проходят: 24/24 passed.
|
||||||
|
```
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
# MetaAgent — установка исходников в целевой проект
|
||||||
|
# Usage: .\install.ps1 [[-Path] target_path] [-Update]
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Path = "",
|
||||||
|
[switch]$Update,
|
||||||
|
[switch]$Help
|
||||||
|
)
|
||||||
|
|
||||||
|
$MetaAgentSrc = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
|
||||||
|
function Show-Usage {
|
||||||
|
@"
|
||||||
|
Usage: install.ps1 [[-Path] target_path] [-Update] [-Help]
|
||||||
|
|
||||||
|
Install MetaAgent sources into <target>/.agent/src/
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-Path Path to target project (default: interactive prompt)
|
||||||
|
-Update Overwrite existing files in .agent/src/
|
||||||
|
-Help Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
.\install.ps1
|
||||||
|
.\install.ps1 -Path C:\Projects\MyApp
|
||||||
|
.\install.ps1 -Path C:\Projects\MyApp -Update
|
||||||
|
"@
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Help) { Show-Usage }
|
||||||
|
|
||||||
|
$TargetPath = $Path
|
||||||
|
if (-not $TargetPath) {
|
||||||
|
$TargetPath = Read-Host "Enter path to target project"
|
||||||
|
}
|
||||||
|
|
||||||
|
$TargetPath = $TargetPath.Trim()
|
||||||
|
if (-not (Test-Path $TargetPath -PathType Container)) {
|
||||||
|
Write-Error "Directory '$TargetPath' does not exist."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$TargetPath = (Resolve-Path $TargetPath).Path
|
||||||
|
|
||||||
|
$AgentDir = Join-Path $TargetPath ".agent"
|
||||||
|
$SrcDir = Join-Path $AgentDir "src"
|
||||||
|
$RulesDir = Join-Path $AgentDir "rules"
|
||||||
|
$ArchiveDir = Join-Path $AgentDir "archive"
|
||||||
|
$VersionFile = Join-Path $MetaAgentSrc "VERSION"
|
||||||
|
$Version = if (Test-Path $VersionFile) { Get-Content $VersionFile -Raw | ForEach-Object { $_.Trim() } } else { "?" }
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path $SrcDir -Force | Out-Null
|
||||||
|
New-Item -ItemType Directory -Path $RulesDir -Force | Out-Null
|
||||||
|
New-Item -ItemType Directory -Path $ArchiveDir -Force | Out-Null
|
||||||
|
Write-Host "Installing MetaAgent v$Version → $SrcDir"
|
||||||
|
|
||||||
|
# --- copy files ---
|
||||||
|
function Copy-File {
|
||||||
|
param([string]$Src, [string]$DstDir)
|
||||||
|
$name = Split-Path $Src -Leaf
|
||||||
|
if (-not (Test-Path $Src -PathType Leaf)) {
|
||||||
|
Write-Host " [skip] $name (not found)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$dst = Join-Path $DstDir $name
|
||||||
|
if ($Update -or -not (Test-Path $dst)) {
|
||||||
|
Copy-Item $Src $dst -Force
|
||||||
|
Write-Host " [copy] $name"
|
||||||
|
} else {
|
||||||
|
Write-Host " [skip] $name (exists, use -Update to overwrite)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-Dir {
|
||||||
|
param([string]$Src, [string]$DstDir)
|
||||||
|
$name = Split-Path $Src -Leaf
|
||||||
|
if (-not (Test-Path $Src -PathType Container)) {
|
||||||
|
Write-Host " [skip] $name/ (not found)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$dst = Join-Path $DstDir $name
|
||||||
|
New-Item -ItemType Directory -Path $dst -Force | Out-Null
|
||||||
|
if ($Update) {
|
||||||
|
Get-ChildItem $Src | ForEach-Object {
|
||||||
|
Copy-Item $_.FullName $dst -Recurse -Force
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Get-ChildItem $Src | ForEach-Object {
|
||||||
|
$targetPath = Join-Path $dst $_.Name
|
||||||
|
if (-not (Test-Path $targetPath)) {
|
||||||
|
Copy-Item $_.FullName $dst -Recurse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host " [copy] $name/"
|
||||||
|
}
|
||||||
|
|
||||||
|
Copy-File (Join-Path $MetaAgentSrc "META_AGENT_GUIDE.md") $SrcDir
|
||||||
|
Copy-File (Join-Path $MetaAgentSrc "BOUNDARIES.md") $SrcDir
|
||||||
|
Copy-File (Join-Path $MetaAgentSrc "WORKFLOW.md") $SrcDir
|
||||||
|
Copy-File (Join-Path $MetaAgentSrc "VERSION") $SrcDir
|
||||||
|
Copy-Dir (Join-Path $MetaAgentSrc "PROTOCOLS") $SrcDir
|
||||||
|
Copy-Dir (Join-Path $MetaAgentSrc "TEMPLATES") $SrcDir
|
||||||
|
Copy-File (Join-Path $MetaAgentSrc "install.sh") $SrcDir
|
||||||
|
Copy-File (Join-Path $MetaAgentSrc "install.ps1") $SrcDir
|
||||||
|
|
||||||
|
# --- create / update AGENTS.md in root of target ---
|
||||||
|
$AgentsMd = Join-Path $TargetPath "AGENTS.md"
|
||||||
|
|
||||||
|
function New-AgentsMd {
|
||||||
|
param([string]$Path)
|
||||||
|
@"
|
||||||
|
# MetaAgent
|
||||||
|
|
||||||
|
Этот проект использует [MetaAgent](.agent/src/META_AGENT_GUIDE.md) v$Version —
|
||||||
|
набор инструкций для AI-агента.
|
||||||
|
|
||||||
|
## Контекст MetaAgent
|
||||||
|
|
||||||
|
| Ресурс | Путь |
|
||||||
|
|--------|------|
|
||||||
|
| Главная инструкция | `.agent/src/META_AGENT_GUIDE.md` |
|
||||||
|
| Протоколы фаз | `.agent/src/PROTOCOLS/` |
|
||||||
|
| Шаблоны артефактов | `.agent/src/TEMPLATES/` |
|
||||||
|
| Границы (что разрешено/запрещено) | `.agent/src/BOUNDARIES.md` |
|
||||||
|
| Правила проекта | `.agent/rules/project-rules.md` |
|
||||||
|
| Примеры работы | `.agent/src/WORKFLOW.md` |
|
||||||
|
| Версия | `.agent/src/VERSION` |
|
||||||
|
|
||||||
|
## Состояние сессии (если инициализировано)
|
||||||
|
|
||||||
|
| Артефакт | Путь |
|
||||||
|
|----------|------|
|
||||||
|
| Чекпоинты сессии | `.agent/checkpoints.json` |
|
||||||
|
| Манифест задач | `.agent/task-manifest.json` |
|
||||||
|
| Сводка для exec-агента | `.agent/handoff-summary.md` |
|
||||||
|
| Анализ репозитория | `.agent/analysis-report.md` |
|
||||||
|
|
||||||
|
## Для исполнительного агента
|
||||||
|
|
||||||
|
1. **Прочитай** `.agent/src/META_AGENT_GUIDE.md` — пойми жизненный цикл MetaAgent.
|
||||||
|
2. **Прочитай** `.agent/src/BOUNDARIES.md` — соблюдай границы.
|
||||||
|
3. **Прочитай** `.agent/rules/project-rules.md` — выполни пользовательские правила.
|
||||||
|
4. **Проверь** `.agent/checkpoints.json` — если существует, используй как состояние сессии.
|
||||||
|
5. **Проверь** `.agent/task-manifest.json` — если существует, выполняй задачи по порядку.
|
||||||
|
6. Если `.agent/` не инициализирован или устарел — запусти `install.ps1 -Update` для
|
||||||
|
обновления исходников MetaAgent до актуальной версии.
|
||||||
|
"@
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $AgentsMd -PathType Leaf)) {
|
||||||
|
New-AgentsMd $AgentsMd | Out-File -FilePath $AgentsMd -Encoding utf8
|
||||||
|
Write-Host " [create] AGENTS.md"
|
||||||
|
} elseif ($Update) {
|
||||||
|
New-AgentsMd $AgentsMd | Out-File -FilePath $AgentsMd -Encoding utf8
|
||||||
|
Write-Host " [update] AGENTS.md"
|
||||||
|
} else {
|
||||||
|
Write-Host " [skip] AGENTS.md (exists, use -Update to overwrite)"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Done! MetaAgent v$Version installed at $SrcDir"
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# MetaAgent — установка исходников в целевой проект
|
||||||
|
# Usage: ./install.sh [--update] [target_path]
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
METAAGENT_SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $0 [--update] [target_path]
|
||||||
|
|
||||||
|
Install MetaAgent sources into <target>/.agent/src/
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--update, -u Overwrite existing files in .agent/src/
|
||||||
|
--help, -h Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$0
|
||||||
|
$0 /path/to/project
|
||||||
|
$0 --update /path/to/project
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
UPDATE=false
|
||||||
|
TARGET_PATH=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--update|-u) UPDATE=true; shift ;;
|
||||||
|
--help|-h) usage ;;
|
||||||
|
--*) echo "Unknown option: $1"; usage ;;
|
||||||
|
*) TARGET_PATH="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$TARGET_PATH" ]]; then
|
||||||
|
read -r -p "Enter path to target project: " TARGET_PATH
|
||||||
|
fi
|
||||||
|
|
||||||
|
TARGET_PATH="${TARGET_PATH/#\~/$HOME}"
|
||||||
|
TARGET_PATH="$(cd "$TARGET_PATH" 2>/dev/null && pwd)" || {
|
||||||
|
echo "Error: Directory '$TARGET_PATH' does not exist."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
AGENT_DIR="$TARGET_PATH/.agent"
|
||||||
|
SRC_DIR="$AGENT_DIR/src"
|
||||||
|
VERSION="$(cat "$METAAGENT_SRC/VERSION" 2>/dev/null || echo '?')"
|
||||||
|
|
||||||
|
RULES_DIR="$AGENT_DIR/rules"
|
||||||
|
ARCHIVE_DIR="$AGENT_DIR/archive"
|
||||||
|
mkdir -p "$SRC_DIR" "$RULES_DIR" "$ARCHIVE_DIR"
|
||||||
|
echo "Installing MetaAgent v$VERSION → $SRC_DIR"
|
||||||
|
|
||||||
|
# --- copy files ---
|
||||||
|
copy_file() {
|
||||||
|
local src="$1" dst_dir="$2"
|
||||||
|
local name; name="$(basename "$src")"
|
||||||
|
if [[ ! -f "$src" ]]; then
|
||||||
|
echo " [skip] $name (not found)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [[ "$UPDATE" == true ]] || [[ ! -f "$dst_dir/$name" ]]; then
|
||||||
|
cp "$src" "$dst_dir/$name"
|
||||||
|
echo " [copy] $name"
|
||||||
|
else
|
||||||
|
echo " [skip] $name (exists, use --update to overwrite)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_dir() {
|
||||||
|
local src="$1" dst_dir="$2"
|
||||||
|
local name; name="$(basename "$src")"
|
||||||
|
if [[ ! -d "$src" ]]; then
|
||||||
|
echo " [skip] $name/ (not found)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
mkdir -p "$dst_dir/$name"
|
||||||
|
if [[ "$UPDATE" == true ]]; then
|
||||||
|
cp -rf "$src"/* "$dst_dir/$name/" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
cp -rn "$src"/* "$dst_dir/$name/" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
echo " [copy] $name/"
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_file "$METAAGENT_SRC/META_AGENT_GUIDE.md" "$SRC_DIR"
|
||||||
|
copy_file "$METAAGENT_SRC/BOUNDARIES.md" "$SRC_DIR"
|
||||||
|
copy_file "$METAAGENT_SRC/WORKFLOW.md" "$SRC_DIR"
|
||||||
|
copy_file "$METAAGENT_SRC/VERSION" "$SRC_DIR"
|
||||||
|
copy_dir "$METAAGENT_SRC/PROTOCOLS" "$SRC_DIR"
|
||||||
|
copy_dir "$METAAGENT_SRC/TEMPLATES" "$SRC_DIR"
|
||||||
|
copy_file "$METAAGENT_SRC/install.sh" "$SRC_DIR"
|
||||||
|
copy_file "$METAAGENT_SRC/install.ps1" "$SRC_DIR"
|
||||||
|
|
||||||
|
# --- create / update AGENTS.md in root of target ---
|
||||||
|
AGENTS_MD="$TARGET_PATH/AGENTS.md"
|
||||||
|
|
||||||
|
create_agents_md() {
|
||||||
|
cat > "$1" << AGENTS_EOF
|
||||||
|
# MetaAgent
|
||||||
|
|
||||||
|
Этот проект использует [MetaAgent](.agent/src/META_AGENT_GUIDE.md) v$VERSION —
|
||||||
|
набор инструкций для AI-агента.
|
||||||
|
|
||||||
|
## Контекст MetaAgent
|
||||||
|
|
||||||
|
| Ресурс | Путь |
|
||||||
|
|--------|------|
|
||||||
|
| Главная инструкция | \`.agent/src/META_AGENT_GUIDE.md\` |
|
||||||
|
| Протоколы фаз | \`.agent/src/PROTOCOLS/\` |
|
||||||
|
| Шаблоны артефактов | \`.agent/src/TEMPLATES/\` |
|
||||||
|
| Границы (что разрешено/запрещено) | \`.agent/src/BOUNDARIES.md\` |
|
||||||
|
| Правила проекта | \`.agent/rules/project-rules.md\` |
|
||||||
|
| Примеры работы | \`.agent/src/WORKFLOW.md\` |
|
||||||
|
| Версия | \`.agent/src/VERSION\` |
|
||||||
|
|
||||||
|
## Состояние сессии (если инициализировано)
|
||||||
|
|
||||||
|
| Артефакт | Путь |
|
||||||
|
|----------|------|
|
||||||
|
| Чекпоинты сессии | \`.agent/checkpoints.json\` |
|
||||||
|
| Манифест задач | \`.agent/task-manifest.json\` |
|
||||||
|
| Сводка для exec-агента | \`.agent/handoff-summary.md\` |
|
||||||
|
| Анализ репозитория | \`.agent/analysis-report.md\` |
|
||||||
|
|
||||||
|
## Для исполнительного агента
|
||||||
|
|
||||||
|
1. **Прочитай** \`.agent/src/META_AGENT_GUIDE.md\` — пойми жизненный цикл MetaAgent.
|
||||||
|
2. **Прочитай** \`.agent/src/BOUNDARIES.md\` — соблюдай границы.
|
||||||
|
3. **Прочитай** \`.agent/rules/project-rules.md\` — выполни пользовательские правила.
|
||||||
|
4. **Проверь** \`.agent/checkpoints.json\` — если существует, используй как состояние сессии.
|
||||||
|
5. **Проверь** \`.agent/task-manifest.json\` — если существует, выполняй задачи по порядку.
|
||||||
|
6. Если \`.agent/\` не инициализирован или устарел — запусти \`install.sh --update\` для
|
||||||
|
обновления исходников MetaAgent до актуальной версии.
|
||||||
|
AGENTS_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ! -f "$AGENTS_MD" ]]; then
|
||||||
|
create_agents_md "$AGENTS_MD"
|
||||||
|
echo " [create] AGENTS.md"
|
||||||
|
elif [[ "$UPDATE" == true ]]; then
|
||||||
|
create_agents_md "$AGENTS_MD"
|
||||||
|
echo " [update] AGENTS.md"
|
||||||
|
else
|
||||||
|
echo " [skip] AGENTS.md (exists, use --update to overwrite)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done! MetaAgent v$VERSION installed at $SRC_DIR"
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
{
|
||||||
|
"$schema": "metaagent-task-manifest",
|
||||||
|
"version": "1.0",
|
||||||
|
"session_id": "metaagent-002",
|
||||||
|
"goal": "Обновление metaagent-артефактов до v1.0.0, валидация существующего кода и окружения",
|
||||||
|
"created_at": "2026-07-12T20:00:00Z",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"id": "T1",
|
||||||
|
"title": "Инициализация проекта и зависимостей",
|
||||||
|
"description": "Создать структуру директорий, pyproject.toml, venv, установить зависимости",
|
||||||
|
"type": "config",
|
||||||
|
"files": [
|
||||||
|
"pyproject.toml",
|
||||||
|
"cashflow_model/__init__.py",
|
||||||
|
"sync/__init__.py",
|
||||||
|
"engine/__init__.py",
|
||||||
|
"ai/__init__.py",
|
||||||
|
"cli/__init__.py",
|
||||||
|
"data/.gitkeep",
|
||||||
|
"exports/.gitkeep"
|
||||||
|
],
|
||||||
|
"depends_on": [],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"pyproject.toml создан с правильными зависимостями",
|
||||||
|
"Все директории модулей созданы с __init__.py",
|
||||||
|
"ruff lint проходит без ошибок",
|
||||||
|
"pytest запускается"
|
||||||
|
],
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "T2",
|
||||||
|
"title": "Модель данных (dataclass + JSON serialization)",
|
||||||
|
"description": "Реализовать все сущности: Account, Transaction, RecurringCashflow, Asset, Liability, ForecastScenario, FinancialModel",
|
||||||
|
"type": "feature",
|
||||||
|
"files": [
|
||||||
|
"cashflow_model/__init__.py",
|
||||||
|
"cashflow_model/account.py",
|
||||||
|
"cashflow_model/transaction.py",
|
||||||
|
"cashflow_model/recurring.py",
|
||||||
|
"cashflow_model/asset.py",
|
||||||
|
"cashflow_model/liability.py",
|
||||||
|
"cashflow_model/scenario.py",
|
||||||
|
"cashflow_model/model.py"
|
||||||
|
],
|
||||||
|
"depends_on": ["T1"],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"Все сущности — dataclass с правильными полями и типами",
|
||||||
|
"FinancialModel корректно сохраняется и загружается из JSON",
|
||||||
|
"Создание Account, Transaction, Asset, Liability через конструктор работает"
|
||||||
|
],
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "T3",
|
||||||
|
"title": "Forecast Engine (базовый прогноз)",
|
||||||
|
"description": "Реализовать ForecastService с методами forecast_cashflow, apply_recurring, project_balance",
|
||||||
|
"type": "feature",
|
||||||
|
"files": [
|
||||||
|
"engine/__init__.py",
|
||||||
|
"engine/forecast.py"
|
||||||
|
],
|
||||||
|
"depends_on": ["T2"],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"forecast_cashflow(months=12) возвращает список помесячных балансов",
|
||||||
|
"Регулярные платежи корректно проецируются на будущие периоды",
|
||||||
|
"Активы учитываются с ростом (growth_rate)",
|
||||||
|
"Обязательства учитываются с процентами и платежами"
|
||||||
|
],
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "T4",
|
||||||
|
"title": "Scenario Analysis",
|
||||||
|
"description": "Реализовать ScenarioService с методами: сценарии, what-if, сравнение",
|
||||||
|
"type": "feature",
|
||||||
|
"files": [
|
||||||
|
"engine/__init__.py",
|
||||||
|
"engine/scenarios.py"
|
||||||
|
],
|
||||||
|
"depends_on": ["T3"],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"Три предустановленных сценария (baseline, optimistic, pessimistic)",
|
||||||
|
"What-if: изменение параметров (доход +10%, расход -5%)",
|
||||||
|
"Сравнение сценариев возвращает сводку различий"
|
||||||
|
],
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "T5",
|
||||||
|
"title": "Excel Sync (import/export)",
|
||||||
|
"description": "Реализовать ExcelSync: чтение модели из .xlsx, запись результатов прогноза в .xlsx",
|
||||||
|
"type": "feature",
|
||||||
|
"files": [
|
||||||
|
"sync/__init__.py",
|
||||||
|
"sync/excel_sync.py"
|
||||||
|
],
|
||||||
|
"depends_on": ["T2"],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"Импорт из Excel заполняет FinancialModel",
|
||||||
|
"Экспорт FinancialModel в Excel создаёт корректный .xlsx",
|
||||||
|
"Обработка ошибок при невалидном формате Excel"
|
||||||
|
],
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "T6",
|
||||||
|
"title": "CLI (Typer) — все команды",
|
||||||
|
"description": "Реализовать CLI через Typer с командами: init, import, export, forecast, scenario, analyze, whatif, compare",
|
||||||
|
"type": "feature",
|
||||||
|
"files": [
|
||||||
|
"cli/__init__.py",
|
||||||
|
"cli/main.py",
|
||||||
|
"pyproject.toml"
|
||||||
|
],
|
||||||
|
"depends_on": ["T2", "T3", "T4", "T5", "T7"],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"Команда 'cf init' создаёт пустую модель и JSON",
|
||||||
|
"Команда 'cf forecast --months 12' выводит таблицу прогноза",
|
||||||
|
"Команда 'cf analyze' вызывает AI Assistant",
|
||||||
|
"Команда 'cf import' и 'cf export' работают с Excel",
|
||||||
|
"Команда 'cf scenario' применяет и выводит сценарий",
|
||||||
|
"Команда 'cf whatif' выполняет what-if анализ",
|
||||||
|
"Команда 'cf compare' сравнивает сценарии"
|
||||||
|
],
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "T7",
|
||||||
|
"title": "AI Assistant (промпты + интерфейс)",
|
||||||
|
"description": "Реализовать AssistantService: генерация промптов, заглушка для вызова AI API",
|
||||||
|
"type": "feature",
|
||||||
|
"files": [
|
||||||
|
"ai/__init__.py",
|
||||||
|
"ai/prompts.py",
|
||||||
|
"ai/assistant.py"
|
||||||
|
],
|
||||||
|
"depends_on": ["T3"],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"Промпт 'analyze' включает модель и прогноз в JSON",
|
||||||
|
"Промпт 'advice' формирует запрос на финансовые рекомендации",
|
||||||
|
"AssistantService возвращает структурированный ответ (заглушка)"
|
||||||
|
],
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "T8",
|
||||||
|
"title": "Тесты на все модули",
|
||||||
|
"description": "Написать pytest-тесты для всех модулей",
|
||||||
|
"type": "test",
|
||||||
|
"files": [
|
||||||
|
"tests/test_model.py",
|
||||||
|
"tests/test_forecast.py",
|
||||||
|
"tests/test_scenarios.py",
|
||||||
|
"tests/test_excel_sync.py",
|
||||||
|
"tests/test_cli.py",
|
||||||
|
"tests/test_ai.py",
|
||||||
|
"tests/conftest.py"
|
||||||
|
],
|
||||||
|
"depends_on": ["T2", "T3", "T4", "T5", "T6", "T7"],
|
||||||
|
"acceptance_criteria": [
|
||||||
|
"pytest запускается и все тесты проходят",
|
||||||
|
"Покрытие базовых сценариев для каждой сущности",
|
||||||
|
"Roundtrip-тест Excel: export → import → compare",
|
||||||
|
"Forecast-тест: известные входные данные → ожидаемый результат"
|
||||||
|
],
|
||||||
|
"status": "completed"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Task Manifest
|
||||||
|
|
||||||
|
**Session:** metaagent-002
|
||||||
|
**Goal:** Обновление metaagent-артефактов до v1.0.0, валидация существующего кода и окружения
|
||||||
|
**Date:** 2026-07-12T20:00:00Z
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task Overview
|
||||||
|
|
||||||
|
| ID | Title | Type | Depends On | Status |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| T1 | Инициализация проекта и зависимостей | config | — | completed |
|
||||||
|
| T2 | Модель данных (dataclass + JSON serialization) | feature | T1 | completed |
|
||||||
|
| T3 | Forecast Engine (базовый прогноз) | feature | T2 | completed |
|
||||||
|
| T4 | Scenario Analysis | feature | T3 | completed |
|
||||||
|
| T5 | Excel Sync (import/export) | feature | T2 | completed |
|
||||||
|
| T6 | CLI (Typer) — все команды | feature | T2, T3, T4, T5, T7 | completed |
|
||||||
|
| T7 | AI Assistant (промпты + интерфейс) | feature | T3 | completed |
|
||||||
|
| T8 | Тесты на все модули | test | T2, T3, T4, T5, T6, T7 | completed |
|
||||||
|
|
||||||
|
**Total tasks:** 8 — all completed
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# MetaAgent
|
||||||
|
|
||||||
|
Этот проект использует [MetaAgent](.agent/src/META_AGENT_GUIDE.md) v1.1.0 —
|
||||||
|
набор инструкций для AI-агента.
|
||||||
|
|
||||||
|
## Контекст MetaAgent
|
||||||
|
|
||||||
|
| Ресурс | Путь |
|
||||||
|
|--------|------|
|
||||||
|
| Главная инструкция | `.agent/src/META_AGENT_GUIDE.md` |
|
||||||
|
| Протоколы фаз | `.agent/src/PROTOCOLS/` |
|
||||||
|
| Шаблоны артефактов | `.agent/src/TEMPLATES/` |
|
||||||
|
| Границы (что разрешено/запрещено) | `.agent/src/BOUNDARIES.md` |
|
||||||
|
| Правила проекта | `.agent/rules/project-rules.md` |
|
||||||
|
| Примеры работы | `.agent/src/WORKFLOW.md` |
|
||||||
|
| Версия | `.agent/src/VERSION` |
|
||||||
|
|
||||||
|
## Состояние сессии (если инициализировано)
|
||||||
|
|
||||||
|
| Артефакт | Путь |
|
||||||
|
|----------|------|
|
||||||
|
| Чекпоинты сессии | `.agent/checkpoints.json` |
|
||||||
|
| Манифест задач | `.agent/task-manifest.json` |
|
||||||
|
| Сводка для exec-агента | `.agent/handoff-summary.md` |
|
||||||
|
| Анализ репозитория | `.agent/analysis-report.md` |
|
||||||
|
|
||||||
|
## Для исполнительного агента
|
||||||
|
|
||||||
|
1. **Прочитай** `.agent/src/META_AGENT_GUIDE.md` — пойми жизненный цикл MetaAgent.
|
||||||
|
2. **Прочитай** `.agent/src/BOUNDARIES.md` — соблюдай границы.
|
||||||
|
3. **Прочитай** `.agent/rules/project-rules.md` — выполни пользовательские правила.
|
||||||
|
4. **Проверь** `.agent/checkpoints.json` — если существует, используй как состояние сессии.
|
||||||
|
5. **Проверь** `.agent/task-manifest.json` — если существует, выполняй задачи по порядку.
|
||||||
|
6. Если `.agent/` не инициализирован или устарел — запусти `install.sh --update` для
|
||||||
|
обновления исходников MetaAgent до актуальной версии.
|
||||||
+193
@@ -0,0 +1,193 @@
|
|||||||
|
# CashFlow Forecast
|
||||||
|
|
||||||
|
Простой open-source проект для построения и анализа личной финансовой модели с использованием Spreadsheet, Python и AI.
|
||||||
|
|
||||||
|
## Идея
|
||||||
|
|
||||||
|
Большинство приложений для учета финансов отвечают на вопрос:
|
||||||
|
|
||||||
|
> "Что произошло?"
|
||||||
|
|
||||||
|
Этот проект пытается ответить на другой вопрос:
|
||||||
|
|
||||||
|
> "Что произойдет дальше?"
|
||||||
|
|
||||||
|
Основная цель — построение прогнозной модели денежных потоков (Cash Flow Forecasting), позволяющей моделировать различные сценарии будущего.
|
||||||
|
|
||||||
|
## Принципы
|
||||||
|
|
||||||
|
- Spreadsheet используется как удобный визуальный редактор.
|
||||||
|
- Python является вычислительным ядром системы.
|
||||||
|
- JSON служит внутренним представлением модели данных.
|
||||||
|
- AI используется как инструмент анализа и взаимодействия с моделью.
|
||||||
|
- Все компоненты должны быть взаимозаменяемыми.
|
||||||
|
|
||||||
|
## Архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
User
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Spreadsheet (UI)
|
||||||
|
│
|
||||||
|
Synchronization Layer
|
||||||
|
(Python)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Financial Model
|
||||||
|
(JSON)
|
||||||
|
│
|
||||||
|
┌───────────┴───────────┐
|
||||||
|
▼ ▼
|
||||||
|
Forecast Engine AI Assistant
|
||||||
|
Scenario Analysis Data Analysis
|
||||||
|
```
|
||||||
|
|
||||||
|
Spreadsheet рассматривается как пользовательский интерфейс, а не как источник бизнес-логики.
|
||||||
|
|
||||||
|
## Основные сущности
|
||||||
|
|
||||||
|
```
|
||||||
|
Accounts
|
||||||
|
Transactions
|
||||||
|
Assets
|
||||||
|
Liabilities
|
||||||
|
Recurring Cashflows
|
||||||
|
Forecast Scenarios
|
||||||
|
Parameters
|
||||||
|
```
|
||||||
|
|
||||||
|
### Account
|
||||||
|
|
||||||
|
```
|
||||||
|
id
|
||||||
|
name
|
||||||
|
currency
|
||||||
|
balance
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transaction
|
||||||
|
|
||||||
|
```
|
||||||
|
id
|
||||||
|
date
|
||||||
|
account
|
||||||
|
category
|
||||||
|
amount
|
||||||
|
description
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recurring Cashflow
|
||||||
|
|
||||||
|
```
|
||||||
|
id
|
||||||
|
start_date
|
||||||
|
end_date
|
||||||
|
frequency
|
||||||
|
amount
|
||||||
|
category
|
||||||
|
```
|
||||||
|
|
||||||
|
### Asset
|
||||||
|
|
||||||
|
```
|
||||||
|
id
|
||||||
|
name
|
||||||
|
value
|
||||||
|
growth_rate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Liability
|
||||||
|
|
||||||
|
```
|
||||||
|
id
|
||||||
|
name
|
||||||
|
balance
|
||||||
|
interest
|
||||||
|
payment
|
||||||
|
```
|
||||||
|
|
||||||
|
## Основной цикл
|
||||||
|
|
||||||
|
```
|
||||||
|
Spreadsheet
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Python Import
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
JSON Model
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Forecast Calculation
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Scenario Simulation
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
AI Analysis
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Spreadsheet Export
|
||||||
|
```
|
||||||
|
|
||||||
|
## Возможности
|
||||||
|
|
||||||
|
- прогноз денежных потоков;
|
||||||
|
- моделирование бюджета;
|
||||||
|
- сценарный анализ;
|
||||||
|
- учет активов и обязательств;
|
||||||
|
- прогноз ликвидности;
|
||||||
|
- анализ финансовой устойчивости;
|
||||||
|
- моделирование достижения финансовых целей;
|
||||||
|
- анализ "что если" (What-if Analysis).
|
||||||
|
|
||||||
|
|
||||||
|
## Будущие возможности
|
||||||
|
|
||||||
|
- Monte-Carlo Simulation;
|
||||||
|
- FIRE Planning;
|
||||||
|
- инвестиционный прогноз;
|
||||||
|
- импорт банковских выписок;
|
||||||
|
- импорт брокерских отчетов;
|
||||||
|
- REST API;
|
||||||
|
- Web UI;
|
||||||
|
- Mobile App;
|
||||||
|
- AI Financial Assistant.
|
||||||
|
|
||||||
|
|
||||||
|
## Структура репозитория
|
||||||
|
|
||||||
|
```
|
||||||
|
cashflow-forecast/
|
||||||
|
|
||||||
|
├── README.md
|
||||||
|
├── spreadsheet/
|
||||||
|
│ model.xlsx
|
||||||
|
│
|
||||||
|
├── data/
|
||||||
|
│ model.json
|
||||||
|
│
|
||||||
|
├── sync/
|
||||||
|
│ excel_sync.py
|
||||||
|
│
|
||||||
|
├── engine/
|
||||||
|
│ forecast.py
|
||||||
|
│ scenarios.py
|
||||||
|
│
|
||||||
|
├── ai/
|
||||||
|
│ prompts.py
|
||||||
|
│ assistant.py
|
||||||
|
│
|
||||||
|
├── exports/
|
||||||
|
│
|
||||||
|
└── docs/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Долгосрочная идея
|
||||||
|
|
||||||
|
На ранних этапах Spreadsheet используется как быстрый инструмент проектирования финансовой модели.
|
||||||
|
|
||||||
|
По мере развития проекта вычисления и логика постепенно переносятся в Python, а Spreadsheet превращается исключительно в средство отображения и редактирования данных.
|
||||||
|
|
||||||
|
Конечной целью является независимый интерактивный open-source инструмент для персонального финансового планирования и прогнозирования денежных потоков, в котором AI выступает естественным интерфейсом для анализа и построения сценариев.
|
||||||
@@ -1,193 +1,118 @@
|
|||||||
# CashFlow Forecast
|
# CashFlow Forecast
|
||||||
|
|
||||||
Простой open-source проект для построения и анализа личной финансовой модели с использованием Spreadsheet, Python и AI.
|
Личная финансовая модель с прогнозом денежных потоков, сценарным анализом и AI-ассистентом.
|
||||||
|
|
||||||
## Идея
|
Python + JSON + Excel + AI.
|
||||||
|
|
||||||
Большинство приложений для учета финансов отвечают на вопрос:
|
## Установка
|
||||||
|
|
||||||
> "Что произошло?"
|
```bash
|
||||||
|
git clone <repo>
|
||||||
Этот проект пытается ответить на другой вопрос:
|
cd cashflow-forecast
|
||||||
|
python -m venv .venv
|
||||||
> "Что произойдет дальше?"
|
source .venv/bin/activate # Linux/Mac
|
||||||
|
# .venv\Scripts\activate # Windows
|
||||||
Основная цель — построение прогнозной модели денежных потоков (Cash Flow Forecasting), позволяющей моделировать различные сценарии будущего.
|
pip install -e .
|
||||||
|
|
||||||
## Принципы
|
|
||||||
|
|
||||||
- Spreadsheet используется как удобный визуальный редактор.
|
|
||||||
- Python является вычислительным ядром системы.
|
|
||||||
- JSON служит внутренним представлением модели данных.
|
|
||||||
- AI используется как инструмент анализа и взаимодействия с моделью.
|
|
||||||
- Все компоненты должны быть взаимозаменяемыми.
|
|
||||||
|
|
||||||
## Архитектура
|
|
||||||
|
|
||||||
```
|
|
||||||
User
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Spreadsheet (UI)
|
|
||||||
│
|
|
||||||
Synchronization Layer
|
|
||||||
(Python)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Financial Model
|
|
||||||
(JSON)
|
|
||||||
│
|
|
||||||
┌───────────┴───────────┐
|
|
||||||
▼ ▼
|
|
||||||
Forecast Engine AI Assistant
|
|
||||||
Scenario Analysis Data Analysis
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Spreadsheet рассматривается как пользовательский интерфейс, а не как источник бизнес-логики.
|
## Использование
|
||||||
|
|
||||||
## Основные сущности
|
```bash
|
||||||
|
# Инициализация пустой модели
|
||||||
|
cf init
|
||||||
|
|
||||||
```
|
# Прогноз на 12 месяцев
|
||||||
Accounts
|
cf forecast --months 12
|
||||||
Transactions
|
|
||||||
Assets
|
# Сценарии
|
||||||
Liabilities
|
cf scenario baseline
|
||||||
Recurring Cashflows
|
cf scenario optimistic
|
||||||
Forecast Scenarios
|
cf scenario pessimistic
|
||||||
Parameters
|
|
||||||
|
# What-if анализ
|
||||||
|
cf whatif --income 1.2 --expense 0.9 --growth 1.0 --months 12
|
||||||
|
|
||||||
|
# Сравнение всех сценариев
|
||||||
|
cf compare --months 12
|
||||||
|
|
||||||
|
# Импорт/экспорт Excel
|
||||||
|
cf import data.xlsx
|
||||||
|
cf export exports/report.xlsx
|
||||||
|
|
||||||
|
# AI-анализ (заглушка, генерация промпта)
|
||||||
|
cf analyze
|
||||||
```
|
```
|
||||||
|
|
||||||
### Account
|
### Пример: создание тестовых данных
|
||||||
|
|
||||||
```
|
Подготовьте Excel-файл с листами: `Accounts`, `Transactions`, `Recurring`, `Assets`, `Liabilities`. Заголовки колонок соответствуют полям моделей. Затем импортируйте:
|
||||||
id
|
|
||||||
name
|
```bash
|
||||||
currency
|
cf import my_finances.xlsx
|
||||||
balance
|
cf forecast --months 12
|
||||||
```
|
```
|
||||||
|
|
||||||
### Transaction
|
## Структура проекта
|
||||||
|
|
||||||
```
|
```
|
||||||
id
|
├── cashflow_model/ # Модели данных (dataclass + JSON)
|
||||||
date
|
│ ├── account.py # Account
|
||||||
account
|
│ ├── transaction.py # Transaction
|
||||||
category
|
│ ├── recurring.py # RecurringCashflow
|
||||||
amount
|
│ ├── asset.py # Asset
|
||||||
description
|
│ ├── liability.py # Liability
|
||||||
|
│ ├── scenario.py # ForecastScenario
|
||||||
|
│ └── model.py # FinancialModel (корень, save/load JSON)
|
||||||
|
├── engine/ # Вычислительное ядро
|
||||||
|
│ ├── forecast.py # ForecastService — прогноз
|
||||||
|
│ └── scenarios.py # ScenarioService — сценарии + what-if
|
||||||
|
├── sync/ # Синхронизация с Excel
|
||||||
|
│ └── excel_sync.py # ExcelSync — import/export .xlsx
|
||||||
|
├── ai/ # AI-ассистент
|
||||||
|
│ ├── prompts.py # Шаблоны промптов
|
||||||
|
│ └── assistant.py # AssistantService (заглушка)
|
||||||
|
├── cli/ # CLI (Typer)
|
||||||
|
│ └── main.py # Команды: cf init/forecast/scenario/...
|
||||||
|
├── tests/ # Тесты pytest
|
||||||
|
│ ├── conftest.py # Фикстуры
|
||||||
|
│ ├── test_model.py
|
||||||
|
│ ├── test_forecast.py
|
||||||
|
│ ├── test_scenarios.py
|
||||||
|
│ ├── test_excel_sync.py
|
||||||
|
│ ├── test_ai.py
|
||||||
|
│ └── test_cli.py
|
||||||
|
├── data/ # JSON-модели
|
||||||
|
├── exports/ # Экспортированные .xlsx
|
||||||
|
├── .agent/ # Артефакты MetaAgent (планирование)
|
||||||
|
├── pyproject.toml # Зависимости и конфигурация
|
||||||
|
└── README.arch.md # Оригинальная архитектурная концепция
|
||||||
```
|
```
|
||||||
|
|
||||||
### Recurring Cashflow
|
## Разработка
|
||||||
|
|
||||||
```
|
```bash
|
||||||
id
|
# Тесты
|
||||||
start_date
|
pytest
|
||||||
end_date
|
|
||||||
frequency
|
# Линтер
|
||||||
amount
|
ruff check .
|
||||||
category
|
|
||||||
|
# Автоформат
|
||||||
|
ruff format .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Asset
|
### Зависимости
|
||||||
|
|
||||||
```
|
- Python >= 3.11
|
||||||
id
|
- openpyxl — работа с Excel
|
||||||
name
|
- typer — CLI
|
||||||
value
|
- rich — форматирование вывода
|
||||||
growth_rate
|
- pytest — тесты
|
||||||
```
|
- ruff — линтер
|
||||||
|
|
||||||
### Liability
|
## Известные ограничения (MVP)
|
||||||
|
|
||||||
```
|
- **AI-ассистент** — заглушка. Промпты готовы, но не подключены к API.
|
||||||
id
|
- **База данных** — JSON-файлы (не подходит для многопользовательской работы).
|
||||||
name
|
- **Excel** — только `.xlsx` через openpyxl.
|
||||||
balance
|
- **Лицензия** — не выбрана.
|
||||||
interest
|
|
||||||
payment
|
|
||||||
```
|
|
||||||
|
|
||||||
## Основной цикл
|
|
||||||
|
|
||||||
```
|
|
||||||
Spreadsheet
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Python Import
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
JSON Model
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Forecast Calculation
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Scenario Simulation
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
AI Analysis
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Spreadsheet Export
|
|
||||||
```
|
|
||||||
|
|
||||||
## Возможности
|
|
||||||
|
|
||||||
- прогноз денежных потоков;
|
|
||||||
- моделирование бюджета;
|
|
||||||
- сценарный анализ;
|
|
||||||
- учет активов и обязательств;
|
|
||||||
- прогноз ликвидности;
|
|
||||||
- анализ финансовой устойчивости;
|
|
||||||
- моделирование достижения финансовых целей;
|
|
||||||
- анализ "что если" (What-if Analysis).
|
|
||||||
|
|
||||||
|
|
||||||
## Будущие возможности
|
|
||||||
|
|
||||||
- Monte-Carlo Simulation;
|
|
||||||
- FIRE Planning;
|
|
||||||
- инвестиционный прогноз;
|
|
||||||
- импорт банковских выписок;
|
|
||||||
- импорт брокерских отчетов;
|
|
||||||
- REST API;
|
|
||||||
- Web UI;
|
|
||||||
- Mobile App;
|
|
||||||
- AI Financial Assistant.
|
|
||||||
|
|
||||||
|
|
||||||
## Структура репозитория
|
|
||||||
|
|
||||||
```
|
|
||||||
cashflow-forecast/
|
|
||||||
|
|
||||||
├── README.md
|
|
||||||
├── spreadsheet/
|
|
||||||
│ model.xlsx
|
|
||||||
│
|
|
||||||
├── data/
|
|
||||||
│ model.json
|
|
||||||
│
|
|
||||||
├── sync/
|
|
||||||
│ excel_sync.py
|
|
||||||
│
|
|
||||||
├── engine/
|
|
||||||
│ forecast.py
|
|
||||||
│ scenarios.py
|
|
||||||
│
|
|
||||||
├── ai/
|
|
||||||
│ prompts.py
|
|
||||||
│ assistant.py
|
|
||||||
│
|
|
||||||
├── exports/
|
|
||||||
│
|
|
||||||
└── docs/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Долгосрочная идея
|
|
||||||
|
|
||||||
На ранних этапах Spreadsheet используется как быстрый инструмент проектирования финансовой модели.
|
|
||||||
|
|
||||||
По мере развития проекта вычисления и логика постепенно переносятся в Python, а Spreadsheet превращается исключительно в средство отображения и редактирования данных.
|
|
||||||
|
|
||||||
Конечной целью является независимый интерактивный open-source инструмент для персонального финансового планирования и прогнозирования денежных потоков, в котором AI выступает естественным интерфейсом для анализа и построения сценариев.
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from ai import prompts
|
||||||
|
from ai.assistant import AssistantError, AssistantService
|
||||||
|
|
||||||
|
__all__ = ["AssistantService", "AssistantError", "prompts"]
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from ai import prompts
|
||||||
|
from cashflow_model import FinancialModel
|
||||||
|
from engine.forecast import ForecastService
|
||||||
|
|
||||||
|
|
||||||
|
class AssistantError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AssistantService:
|
||||||
|
def __init__(self, model: FinancialModel):
|
||||||
|
self.model = model
|
||||||
|
|
||||||
|
def analyze(self, months: int = 12) -> dict:
|
||||||
|
forecast_service = ForecastService(self.model)
|
||||||
|
forecast_result = forecast_service.forecast_cashflow(months)
|
||||||
|
summary = forecast_service.summary(months)
|
||||||
|
|
||||||
|
prompt = prompts.format_context(
|
||||||
|
model_json=json.dumps(self.model.to_dict(), indent=2, ensure_ascii=False),
|
||||||
|
forecast_json=json.dumps(forecast_result, indent=2, ensure_ascii=False),
|
||||||
|
months=months,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"prompt": prompt,
|
||||||
|
"summary": summary,
|
||||||
|
"forecast": forecast_result,
|
||||||
|
"ai_response": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
def advice(self, question: str, months: int = 12) -> dict:
|
||||||
|
forecast_service = ForecastService(self.model)
|
||||||
|
forecast_result = forecast_service.forecast_cashflow(months)
|
||||||
|
|
||||||
|
prompt = prompts.ADVICE_PROMPT.format(
|
||||||
|
model_json=json.dumps(self.model.to_dict(), indent=2, ensure_ascii=False),
|
||||||
|
forecast_json=json.dumps(forecast_result, indent=2, ensure_ascii=False),
|
||||||
|
question=question,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"prompt": prompt,
|
||||||
|
"ai_response": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
def compare_scenarios(self, scenarios_json: str) -> dict:
|
||||||
|
prompt = prompts.SCENARIO_COMPARISON_PROMPT.format(
|
||||||
|
scenarios_json=scenarios_json,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"prompt": prompt,
|
||||||
|
"ai_response": None,
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
ANALYZE_PROMPT = """
|
||||||
|
Ты — финансовый AI-ассистент. Проанализируй финансовую модель пользователя.
|
||||||
|
|
||||||
|
### Модель (JSON):
|
||||||
|
{model_json}
|
||||||
|
|
||||||
|
### Прогноз на {months} месяцев:
|
||||||
|
{forecast_json}
|
||||||
|
|
||||||
|
Дай анализ по пунктам:
|
||||||
|
1. Общее финансовое состояние
|
||||||
|
2. Тренд денежного потока (рост/падение)
|
||||||
|
3. Достаточность ликвидности
|
||||||
|
4. Рекомендации по улучшению
|
||||||
|
"""
|
||||||
|
|
||||||
|
ADVICE_PROMPT = """
|
||||||
|
Ты — финансовый AI-ассистент. Дай персональные рекомендации.
|
||||||
|
|
||||||
|
### Модель:
|
||||||
|
{model_json}
|
||||||
|
|
||||||
|
### Прогноз:
|
||||||
|
{forecast_json}
|
||||||
|
|
||||||
|
Вопрос пользователя: {question}
|
||||||
|
|
||||||
|
Ответь как опытный финансовый консультант.
|
||||||
|
"""
|
||||||
|
|
||||||
|
SCENARIO_COMPARISON_PROMPT = """
|
||||||
|
Ты — финансовый AI-ассистент. Сравни сценарии прогноза.
|
||||||
|
|
||||||
|
### Результаты сценариев:
|
||||||
|
{scenarios_json}
|
||||||
|
|
||||||
|
Дай рекомендацию: какой сценарий наиболее вероятен и почему.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def format_context(model_json: str, forecast_json: str, months: int = 12) -> str:
|
||||||
|
return ANALYZE_PROMPT.format(
|
||||||
|
model_json=model_json,
|
||||||
|
forecast_json=forecast_json,
|
||||||
|
months=months,
|
||||||
|
)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
from cashflow_model.account import Account
|
||||||
|
from cashflow_model.asset import Asset
|
||||||
|
from cashflow_model.liability import Liability
|
||||||
|
from cashflow_model.model import FinancialModel
|
||||||
|
from cashflow_model.recurring import RecurringCashflow
|
||||||
|
from cashflow_model.scenario import ForecastScenario
|
||||||
|
from cashflow_model.transaction import Transaction
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"Account",
|
||||||
|
"Transaction",
|
||||||
|
"RecurringCashflow",
|
||||||
|
"Asset",
|
||||||
|
"Liability",
|
||||||
|
"ForecastScenario",
|
||||||
|
"FinancialModel",
|
||||||
|
]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Account:
|
||||||
|
id: UUID = field(default_factory=uuid4)
|
||||||
|
name: str = ""
|
||||||
|
currency: str = "USD"
|
||||||
|
balance: float = 0.0
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
"id": str(self.id),
|
||||||
|
"name": self.name,
|
||||||
|
"currency": self.currency,
|
||||||
|
"balance": self.balance,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict) -> "Account":
|
||||||
|
return cls(
|
||||||
|
id=UUID(data["id"]),
|
||||||
|
name=data["name"],
|
||||||
|
currency=data.get("currency", "USD"),
|
||||||
|
balance=data.get("balance", 0.0),
|
||||||
|
)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Asset:
|
||||||
|
id: UUID = field(default_factory=uuid4)
|
||||||
|
name: str = ""
|
||||||
|
value: float = 0.0
|
||||||
|
growth_rate: float = 0.0
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
"id": str(self.id),
|
||||||
|
"name": self.name,
|
||||||
|
"value": self.value,
|
||||||
|
"growth_rate": self.growth_rate,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict) -> "Asset":
|
||||||
|
return cls(
|
||||||
|
id=UUID(data["id"]),
|
||||||
|
name=data["name"],
|
||||||
|
value=data.get("value", 0.0),
|
||||||
|
growth_rate=data.get("growth_rate", 0.0),
|
||||||
|
)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Liability:
|
||||||
|
id: UUID = field(default_factory=uuid4)
|
||||||
|
name: str = ""
|
||||||
|
balance: float = 0.0
|
||||||
|
interest: float = 0.0
|
||||||
|
payment: float = 0.0
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
"id": str(self.id),
|
||||||
|
"name": self.name,
|
||||||
|
"balance": self.balance,
|
||||||
|
"interest": self.interest,
|
||||||
|
"payment": self.payment,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict) -> "Liability":
|
||||||
|
return cls(
|
||||||
|
id=UUID(data["id"]),
|
||||||
|
name=data["name"],
|
||||||
|
balance=data.get("balance", 0.0),
|
||||||
|
interest=data.get("interest", 0.0),
|
||||||
|
payment=data.get("payment", 0.0),
|
||||||
|
)
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import json
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from cashflow_model.account import Account
|
||||||
|
from cashflow_model.asset import Asset
|
||||||
|
from cashflow_model.liability import Liability
|
||||||
|
from cashflow_model.recurring import RecurringCashflow
|
||||||
|
from cashflow_model.scenario import ForecastScenario
|
||||||
|
from cashflow_model.transaction import Transaction
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FinancialModel:
|
||||||
|
accounts: list[Account] = field(default_factory=list)
|
||||||
|
transactions: list[Transaction] = field(default_factory=list)
|
||||||
|
recurring: list[RecurringCashflow] = field(default_factory=list)
|
||||||
|
assets: list[Asset] = field(default_factory=list)
|
||||||
|
liabilities: list[Liability] = field(default_factory=list)
|
||||||
|
scenarios: list[ForecastScenario] = field(default_factory=list)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
"accounts": [a.to_dict() for a in self.accounts],
|
||||||
|
"transactions": [t.to_dict() for t in self.transactions],
|
||||||
|
"recurring": [r.to_dict() for r in self.recurring],
|
||||||
|
"assets": [a.to_dict() for a in self.assets],
|
||||||
|
"liabilities": [li.to_dict() for li in self.liabilities],
|
||||||
|
"scenarios": [s.to_dict() for s in self.scenarios],
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict) -> "FinancialModel":
|
||||||
|
return cls(
|
||||||
|
accounts=[Account.from_dict(a) for a in data.get("accounts", [])],
|
||||||
|
transactions=[Transaction.from_dict(t) for t in data.get("transactions", [])],
|
||||||
|
recurring=[RecurringCashflow.from_dict(r) for r in data.get("recurring", [])],
|
||||||
|
assets=[Asset.from_dict(a) for a in data.get("assets", [])],
|
||||||
|
liabilities=[Liability.from_dict(li) for li in data.get("liabilities", [])],
|
||||||
|
scenarios=[ForecastScenario.from_dict(s) for s in data.get("scenarios", [])],
|
||||||
|
)
|
||||||
|
|
||||||
|
def save(self, path: str | Path) -> None:
|
||||||
|
path = Path(path)
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, path: str | Path) -> "FinancialModel":
|
||||||
|
path = Path(path)
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
return cls.from_dict(data)
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RecurringCashflow:
|
||||||
|
id: UUID = field(default_factory=uuid4)
|
||||||
|
start_date: str = ""
|
||||||
|
end_date: str = ""
|
||||||
|
frequency: str = "monthly"
|
||||||
|
amount: float = 0.0
|
||||||
|
category: str = ""
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
"id": str(self.id),
|
||||||
|
"start_date": self.start_date,
|
||||||
|
"end_date": self.end_date,
|
||||||
|
"frequency": self.frequency,
|
||||||
|
"amount": self.amount,
|
||||||
|
"category": self.category,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict) -> "RecurringCashflow":
|
||||||
|
return cls(
|
||||||
|
id=UUID(data["id"]),
|
||||||
|
start_date=data.get("start_date", ""),
|
||||||
|
end_date=data.get("end_date", ""),
|
||||||
|
frequency=data.get("frequency", "monthly"),
|
||||||
|
amount=data.get("amount", 0.0),
|
||||||
|
category=data.get("category", ""),
|
||||||
|
)
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ForecastScenario:
|
||||||
|
id: UUID = field(default_factory=uuid4)
|
||||||
|
name: str = "baseline"
|
||||||
|
income_multiplier: float = 1.0
|
||||||
|
expense_multiplier: float = 1.0
|
||||||
|
growth_multiplier: float = 1.0
|
||||||
|
description: str = ""
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
"id": str(self.id),
|
||||||
|
"name": self.name,
|
||||||
|
"income_multiplier": self.income_multiplier,
|
||||||
|
"expense_multiplier": self.expense_multiplier,
|
||||||
|
"growth_multiplier": self.growth_multiplier,
|
||||||
|
"description": self.description,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict) -> "ForecastScenario":
|
||||||
|
return cls(
|
||||||
|
id=UUID(data["id"]),
|
||||||
|
name=data["name"],
|
||||||
|
income_multiplier=data.get("income_multiplier", 1.0),
|
||||||
|
expense_multiplier=data.get("expense_multiplier", 1.0),
|
||||||
|
growth_multiplier=data.get("growth_multiplier", 1.0),
|
||||||
|
description=data.get("description", ""),
|
||||||
|
)
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Transaction:
|
||||||
|
id: UUID = field(default_factory=uuid4)
|
||||||
|
date: str = ""
|
||||||
|
account: str = ""
|
||||||
|
category: str = ""
|
||||||
|
amount: float = 0.0
|
||||||
|
description: str = ""
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return {
|
||||||
|
"id": str(self.id),
|
||||||
|
"date": self.date,
|
||||||
|
"account": self.account,
|
||||||
|
"category": self.category,
|
||||||
|
"amount": self.amount,
|
||||||
|
"description": self.description,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict) -> "Transaction":
|
||||||
|
return cls(
|
||||||
|
id=UUID(data["id"]),
|
||||||
|
date=data["date"],
|
||||||
|
account=data.get("account", ""),
|
||||||
|
category=data.get("category", ""),
|
||||||
|
amount=data.get("amount", 0.0),
|
||||||
|
description=data.get("description", ""),
|
||||||
|
)
|
||||||
+201
@@ -0,0 +1,201 @@
|
|||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import typer
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.table import Table
|
||||||
|
|
||||||
|
from ai.assistant import AssistantService
|
||||||
|
from cashflow_model import FinancialModel
|
||||||
|
from engine.forecast import ForecastService
|
||||||
|
from engine.scenarios import DEFAULT_SCENARIOS, ScenarioService
|
||||||
|
from sync.excel_sync import ExcelSync
|
||||||
|
|
||||||
|
try:
|
||||||
|
sys.stdout.reconfigure(encoding="utf-8")
|
||||||
|
except (AttributeError, OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
app = typer.Typer(name="cf", help="CashFlow Forecast - personal finance model")
|
||||||
|
console = Console()
|
||||||
|
|
||||||
|
DATA_DIR = Path("data")
|
||||||
|
MODEL_PATH = DATA_DIR / "model.json"
|
||||||
|
|
||||||
|
|
||||||
|
def _load_model() -> FinancialModel:
|
||||||
|
if MODEL_PATH.exists():
|
||||||
|
return FinancialModel.load(MODEL_PATH)
|
||||||
|
return FinancialModel()
|
||||||
|
|
||||||
|
|
||||||
|
def _save_model(model: FinancialModel) -> None:
|
||||||
|
model.save(MODEL_PATH)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def init() -> None:
|
||||||
|
"""Создать пустую финансовую модель"""
|
||||||
|
model = FinancialModel()
|
||||||
|
_save_model(model)
|
||||||
|
console.print("[green]OK[/green] Пустая модель создана в data/model.json")
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def forecast(
|
||||||
|
months: int = typer.Option(12, "--months", "-m", help="Количество месяцев прогноза"),
|
||||||
|
) -> None:
|
||||||
|
"""Запустить прогноз денежных потоков"""
|
||||||
|
model = _load_model()
|
||||||
|
service = ForecastService(model)
|
||||||
|
results = service.forecast_cashflow(months)
|
||||||
|
summary = service.summary(months)
|
||||||
|
|
||||||
|
if results:
|
||||||
|
table = Table(title=f"Прогноз на {months} мес.")
|
||||||
|
table.add_column("Счёт", style="cyan")
|
||||||
|
table.add_column("Месяц", style="white")
|
||||||
|
table.add_column("Баланс", justify="right", style="green")
|
||||||
|
table.add_column("Доход", justify="right")
|
||||||
|
table.add_column("Расход", justify="right")
|
||||||
|
|
||||||
|
for r in results:
|
||||||
|
table.add_row(
|
||||||
|
r["account"], str(r["month"]),
|
||||||
|
f"${r['balance']:.2f}",
|
||||||
|
f"${r['income']:.2f}",
|
||||||
|
f"${r['expenses']:.2f}",
|
||||||
|
)
|
||||||
|
console.print(table)
|
||||||
|
|
||||||
|
console.print(f"\n[bold]Итог:[/bold] Баланс: ${summary['total_balance']:.2f} | "
|
||||||
|
f"Доход: ${summary['total_income']:.2f} | "
|
||||||
|
f"Расход: ${summary['total_expenses']:.2f}")
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def scenario(
|
||||||
|
name: str = typer.Argument("baseline", help="Имя сценария: baseline, optimistic, pessimistic"),
|
||||||
|
months: int = typer.Option(12, "--months", "-m", help="Количество месяцев"),
|
||||||
|
) -> None:
|
||||||
|
"""Применить сценарий и показать прогноз"""
|
||||||
|
model = _load_model()
|
||||||
|
service = ScenarioService(model)
|
||||||
|
|
||||||
|
if name in DEFAULT_SCENARIOS:
|
||||||
|
scenario_obj = DEFAULT_SCENARIOS[name]
|
||||||
|
else:
|
||||||
|
console.print(f"[red]Неизвестный сценарий: {name}[/red]")
|
||||||
|
console.print(f"Доступны: {', '.join(DEFAULT_SCENARIOS.keys())}")
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
result = service.apply(scenario_obj, months)
|
||||||
|
console.print(f"[bold]Сценарий:[/bold] {result['scenario']}")
|
||||||
|
console.print(f"[dim]{result['scenario_description']}[/dim]")
|
||||||
|
console.print(f"Баланс: ${result['total_balance']:.2f}")
|
||||||
|
console.print(f"Доход: ${result['total_income']:.2f}")
|
||||||
|
console.print(f"Расход: ${result['total_expenses']:.2f}")
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def whatif(
|
||||||
|
income_mult: float = typer.Option(1.0, "--income", "-i", help="Множитель дохода"),
|
||||||
|
expense_mult: float = typer.Option(1.0, "--expense", "-e", help="Множитель расхода"),
|
||||||
|
growth_mult: float = typer.Option(1.0, "--growth", "-g", help="Множитель роста активов"),
|
||||||
|
months: int = typer.Option(12, "--months", "-m", help="Количество месяцев"),
|
||||||
|
) -> None:
|
||||||
|
"""What-if анализ с произвольными множителями"""
|
||||||
|
model = _load_model()
|
||||||
|
service = ScenarioService(model)
|
||||||
|
result = service.what_if(income_mult, expense_mult, growth_mult, months)
|
||||||
|
|
||||||
|
console.print("[bold]What-if анализ[/bold]")
|
||||||
|
console.print(f"Доход x{income_mult} | Расход x{expense_mult} | Рост x{growth_mult}")
|
||||||
|
console.print(f"Баланс: ${result['total_balance']:.2f}")
|
||||||
|
console.print(f"Доход: ${result['total_income']:.2f}")
|
||||||
|
console.print(f"Расход: ${result['total_expenses']:.2f}")
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def compare(
|
||||||
|
months: int = typer.Option(12, "--months", "-m", help="Количество месяцев"),
|
||||||
|
) -> None:
|
||||||
|
"""Сравнить все сценарии"""
|
||||||
|
model = _load_model()
|
||||||
|
service = ScenarioService(model)
|
||||||
|
results = service.compare(months)
|
||||||
|
|
||||||
|
table = Table(title="Сравнение сценариев")
|
||||||
|
table.add_column("Сценарий", style="cyan")
|
||||||
|
table.add_column("Баланс", justify="right")
|
||||||
|
table.add_column("Доход", justify="right")
|
||||||
|
table.add_column("Расход", justify="right")
|
||||||
|
|
||||||
|
for name, r in results.items():
|
||||||
|
table.add_row(
|
||||||
|
name,
|
||||||
|
f"${r['total_balance']:.2f}",
|
||||||
|
f"${r['total_income']:.2f}",
|
||||||
|
f"${r['total_expenses']:.2f}",
|
||||||
|
)
|
||||||
|
console.print(table)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def import_xlsx(
|
||||||
|
path: str = typer.Argument(..., help="Путь к .xlsx файлу"),
|
||||||
|
) -> None:
|
||||||
|
"""Импорт данных из Excel"""
|
||||||
|
sync = ExcelSync()
|
||||||
|
try:
|
||||||
|
model = sync.import_model(path)
|
||||||
|
_save_model(model)
|
||||||
|
console.print(f"[green]OK[/green] Импортировано: {len(model.accounts)} счетов, "
|
||||||
|
f"{len(model.transactions)} транзакций, "
|
||||||
|
f"{len(model.recurring)} регулярных платежей, "
|
||||||
|
f"{len(model.assets)} активов, "
|
||||||
|
f"{len(model.liabilities)} обязательств")
|
||||||
|
except Exception as e:
|
||||||
|
console.print(f"[red]Ошибка импорта: {e}[/red]")
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def export_xlsx(
|
||||||
|
path: str = typer.Argument("exports/forecast.xlsx", help="Путь для .xlsx файла"),
|
||||||
|
) -> None:
|
||||||
|
"""Экспорт модели в Excel"""
|
||||||
|
model = _load_model()
|
||||||
|
sync = ExcelSync()
|
||||||
|
try:
|
||||||
|
sync.export_model(model, path)
|
||||||
|
console.print(f"[green]OK[/green] Модель экспортирована в {path}")
|
||||||
|
except Exception as e:
|
||||||
|
console.print(f"[red]Ошибка экспорта: {e}[/red]")
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def analyze(
|
||||||
|
months: int = typer.Option(12, "--months", "-m", help="Количество месяцев для анализа"),
|
||||||
|
) -> None:
|
||||||
|
"""AI-анализ финансовой модели"""
|
||||||
|
model = _load_model()
|
||||||
|
assistant = AssistantService(model)
|
||||||
|
result = assistant.analyze(months)
|
||||||
|
|
||||||
|
console.print("[bold]Промпт для AI:[/bold]")
|
||||||
|
console.print(result["prompt"][:500] + "...\n")
|
||||||
|
|
||||||
|
console.print("[bold]Сводка:[/bold]")
|
||||||
|
s = result["summary"]
|
||||||
|
console.print(f"Баланс: ${s['total_balance']:.2f}")
|
||||||
|
console.print(f"Доход: ${s['total_income']:.2f}")
|
||||||
|
console.print(f"Расход: ${s['total_expenses']:.2f}")
|
||||||
|
console.print(
|
||||||
|
"\n[yellow]AI-ответ: заглушка. Подключите реальный API в ai/assistant.py[/yellow]"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"accounts": [],
|
||||||
|
"transactions": [],
|
||||||
|
"recurring": [],
|
||||||
|
"assets": [],
|
||||||
|
"liabilities": [],
|
||||||
|
"scenarios": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from engine.forecast import ForecastError, ForecastService
|
||||||
|
from engine.scenarios import DEFAULT_SCENARIOS, ScenarioError, ScenarioService
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ForecastService",
|
||||||
|
"ForecastError",
|
||||||
|
"ScenarioService",
|
||||||
|
"ScenarioError",
|
||||||
|
"DEFAULT_SCENARIOS",
|
||||||
|
]
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from cashflow_model import Account, FinancialModel
|
||||||
|
|
||||||
|
|
||||||
|
class ForecastError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ForecastService:
|
||||||
|
def __init__(self, model: FinancialModel):
|
||||||
|
self.model = deepcopy(model)
|
||||||
|
|
||||||
|
def forecast_cashflow(self, months: int = 12) -> list[dict]:
|
||||||
|
if months < 1:
|
||||||
|
raise ForecastError("months must be >= 1")
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for account in self.model.accounts:
|
||||||
|
balance = account.balance
|
||||||
|
monthly = self._project_account(account, months)
|
||||||
|
for m in range(months):
|
||||||
|
balance = monthly[m]["balance"]
|
||||||
|
results.append({
|
||||||
|
"account": account.name,
|
||||||
|
"month": m + 1,
|
||||||
|
"balance": round(balance, 2),
|
||||||
|
"income": round(monthly[m]["income"], 2),
|
||||||
|
"expenses": round(monthly[m]["expenses"], 2),
|
||||||
|
})
|
||||||
|
return results
|
||||||
|
|
||||||
|
def _project_account(self, account: Account, months: int) -> list[dict]:
|
||||||
|
balance = account.balance
|
||||||
|
monthly = []
|
||||||
|
for m in range(months):
|
||||||
|
income = 0.0
|
||||||
|
expenses = 0.0
|
||||||
|
|
||||||
|
for t in self.model.transactions:
|
||||||
|
if t.account == str(account.id):
|
||||||
|
if t.amount > 0:
|
||||||
|
income += t.amount
|
||||||
|
else:
|
||||||
|
expenses += abs(t.amount)
|
||||||
|
|
||||||
|
for r in self.model.recurring:
|
||||||
|
if r.amount > 0:
|
||||||
|
income += r.amount
|
||||||
|
else:
|
||||||
|
expenses += abs(r.amount)
|
||||||
|
|
||||||
|
income += self._asset_income(account)
|
||||||
|
expenses += self._liability_cost(account)
|
||||||
|
|
||||||
|
balance += income - expenses
|
||||||
|
|
||||||
|
asset_growth = sum(
|
||||||
|
a.value * a.growth_rate / 12
|
||||||
|
for a in self.model.assets
|
||||||
|
)
|
||||||
|
balance += asset_growth
|
||||||
|
|
||||||
|
monthly.append({
|
||||||
|
"balance": balance,
|
||||||
|
"income": income,
|
||||||
|
"expenses": expenses,
|
||||||
|
})
|
||||||
|
return monthly
|
||||||
|
|
||||||
|
def _asset_income(self, account: Account) -> float:
|
||||||
|
return sum(
|
||||||
|
a.value * a.growth_rate / 12
|
||||||
|
for a in self.model.assets
|
||||||
|
)
|
||||||
|
|
||||||
|
def _liability_cost(self, account: Account) -> float:
|
||||||
|
total = 0.0
|
||||||
|
for liability in self.model.liabilities:
|
||||||
|
interest_cost = liability.balance * liability.interest / 100 / 12
|
||||||
|
total += interest_cost
|
||||||
|
liability.balance -= liability.payment - interest_cost
|
||||||
|
if liability.balance < 0:
|
||||||
|
liability.balance = 0
|
||||||
|
return total
|
||||||
|
|
||||||
|
def summary(self, months: int = 12) -> dict:
|
||||||
|
results = self.forecast_cashflow(months)
|
||||||
|
if not results:
|
||||||
|
return {"total_balance": 0, "total_income": 0, "total_expenses": 0, "months": months}
|
||||||
|
|
||||||
|
final = results[-1]
|
||||||
|
all_income = sum(r["income"] for r in results)
|
||||||
|
all_expenses = sum(r["expenses"] for r in results)
|
||||||
|
return {
|
||||||
|
"total_balance": final["balance"],
|
||||||
|
"total_income": round(all_income, 2),
|
||||||
|
"total_expenses": round(all_expenses, 2),
|
||||||
|
"months": months,
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from cashflow_model import FinancialModel, ForecastScenario
|
||||||
|
from engine.forecast import ForecastService
|
||||||
|
|
||||||
|
|
||||||
|
class ScenarioError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_SCENARIOS = {
|
||||||
|
"baseline": ForecastScenario(
|
||||||
|
name="baseline",
|
||||||
|
income_multiplier=1.0,
|
||||||
|
expense_multiplier=1.0,
|
||||||
|
growth_multiplier=1.0,
|
||||||
|
description="Базовый сценарий без изменений",
|
||||||
|
),
|
||||||
|
"optimistic": ForecastScenario(
|
||||||
|
name="optimistic",
|
||||||
|
income_multiplier=1.15,
|
||||||
|
expense_multiplier=0.95,
|
||||||
|
growth_multiplier=1.2,
|
||||||
|
description="Оптимистичный: доход +15%, расход -5%, рост активов +20%",
|
||||||
|
),
|
||||||
|
"pessimistic": ForecastScenario(
|
||||||
|
name="pessimistic",
|
||||||
|
income_multiplier=0.85,
|
||||||
|
expense_multiplier=1.1,
|
||||||
|
growth_multiplier=0.8,
|
||||||
|
description="Пессимистичный: доход -15%, расход +10%, рост активов -20%",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ScenarioService:
|
||||||
|
def __init__(self, model: FinancialModel):
|
||||||
|
self.model = deepcopy(model)
|
||||||
|
|
||||||
|
def apply(self, scenario: ForecastScenario, months: int = 12) -> dict:
|
||||||
|
model = deepcopy(self.model)
|
||||||
|
|
||||||
|
for t in model.transactions:
|
||||||
|
if t.amount > 0:
|
||||||
|
t.amount *= scenario.income_multiplier
|
||||||
|
else:
|
||||||
|
t.amount *= scenario.expense_multiplier
|
||||||
|
|
||||||
|
for r in model.recurring:
|
||||||
|
if r.category == "income":
|
||||||
|
r.amount *= scenario.income_multiplier
|
||||||
|
else:
|
||||||
|
r.amount *= scenario.expense_multiplier
|
||||||
|
|
||||||
|
for a in model.assets:
|
||||||
|
a.growth_rate *= scenario.growth_multiplier
|
||||||
|
|
||||||
|
service = ForecastService(model)
|
||||||
|
result = service.summary(months)
|
||||||
|
result["scenario"] = scenario.name
|
||||||
|
result["scenario_description"] = scenario.description
|
||||||
|
return result
|
||||||
|
|
||||||
|
def compare(self, months: int = 12) -> dict:
|
||||||
|
results = {}
|
||||||
|
for name, scenario in DEFAULT_SCENARIOS.items():
|
||||||
|
results[name] = self.apply(scenario, months)
|
||||||
|
return results
|
||||||
|
|
||||||
|
def what_if(
|
||||||
|
self,
|
||||||
|
income_mult: float = 1.0,
|
||||||
|
expense_mult: float = 1.0,
|
||||||
|
growth_mult: float = 1.0,
|
||||||
|
months: int = 12,
|
||||||
|
) -> dict:
|
||||||
|
scenario = ForecastScenario(
|
||||||
|
name="what-if",
|
||||||
|
income_multiplier=income_mult,
|
||||||
|
expense_multiplier=expense_mult,
|
||||||
|
growth_multiplier=growth_mult,
|
||||||
|
description=(
|
||||||
|
f"What-if: income x{income_mult}, "
|
||||||
|
f"expense x{expense_mult}, growth x{growth_mult}"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return self.apply(scenario, months)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=68.0"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "cashflow-forecast"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Личная финансовая модель с прогнозом денежных потоков"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
dependencies = [
|
||||||
|
"openpyxl>=3.1",
|
||||||
|
"typer>=0.9",
|
||||||
|
"rich>=13.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
cf = "cli.main:app"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
include = ["cashflow_model*", "sync*", "engine*", "ai*", "cli*"]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py311"
|
||||||
|
line-length = 100
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "I", "N", "W"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
testpaths = ["tests"]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from sync.excel_sync import ExcelSync, SyncError
|
||||||
|
|
||||||
|
__all__ = ["ExcelSync", "SyncError"]
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from openpyxl import Workbook, load_workbook
|
||||||
|
|
||||||
|
from cashflow_model import Account, Asset, FinancialModel, Liability, RecurringCashflow, Transaction
|
||||||
|
|
||||||
|
|
||||||
|
class SyncError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
_SHEET_CONFIG = {
|
||||||
|
"Accounts": {
|
||||||
|
"fields": ["id", "name", "currency", "balance"],
|
||||||
|
"cls": Account,
|
||||||
|
},
|
||||||
|
"Transactions": {
|
||||||
|
"fields": ["id", "date", "account", "category", "amount", "description"],
|
||||||
|
"cls": Transaction,
|
||||||
|
},
|
||||||
|
"Recurring": {
|
||||||
|
"fields": ["id", "start_date", "end_date", "frequency", "amount", "category"],
|
||||||
|
"cls": RecurringCashflow,
|
||||||
|
},
|
||||||
|
"Assets": {
|
||||||
|
"fields": ["id", "name", "value", "growth_rate"],
|
||||||
|
"cls": Asset,
|
||||||
|
},
|
||||||
|
"Liabilities": {
|
||||||
|
"fields": ["id", "name", "balance", "interest", "payment"],
|
||||||
|
"cls": Liability,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ExcelSync:
|
||||||
|
def import_model(self, path: str | Path) -> FinancialModel:
|
||||||
|
path = Path(path)
|
||||||
|
if not path.exists():
|
||||||
|
raise SyncError(f"File not found: {path}")
|
||||||
|
|
||||||
|
wb = load_workbook(path, read_only=True, data_only=True)
|
||||||
|
model = FinancialModel()
|
||||||
|
|
||||||
|
for sheet_name, config in _SHEET_CONFIG.items():
|
||||||
|
if sheet_name not in wb.sheetnames:
|
||||||
|
continue
|
||||||
|
ws = wb[sheet_name]
|
||||||
|
rows = list(ws.iter_rows(values_only=True))
|
||||||
|
if len(rows) < 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
headers = [str(h).strip().lower() if h else "" for h in rows[0]]
|
||||||
|
for row in rows[1:]:
|
||||||
|
if not any(v is not None for v in row):
|
||||||
|
continue
|
||||||
|
data = {}
|
||||||
|
for i, header in enumerate(headers):
|
||||||
|
val = row[i] if i < len(row) else None
|
||||||
|
if val is not None:
|
||||||
|
data[header] = str(val) if not isinstance(val, (int, float)) else val
|
||||||
|
self._add_to_model(model, sheet_name, data)
|
||||||
|
|
||||||
|
wb.close()
|
||||||
|
return model
|
||||||
|
|
||||||
|
def export_model(self, model: FinancialModel, path: str | Path) -> None:
|
||||||
|
path = Path(path)
|
||||||
|
wb = Workbook()
|
||||||
|
wb.remove(wb.active)
|
||||||
|
|
||||||
|
collections = {
|
||||||
|
"Accounts": model.accounts,
|
||||||
|
"Transactions": model.transactions,
|
||||||
|
"Recurring": model.recurring,
|
||||||
|
"Assets": model.assets,
|
||||||
|
"Liabilities": model.liabilities,
|
||||||
|
}
|
||||||
|
|
||||||
|
for sheet_name, items in collections.items():
|
||||||
|
config = _SHEET_CONFIG[sheet_name]
|
||||||
|
ws = wb.create_sheet(title=sheet_name)
|
||||||
|
ws.append(config["fields"])
|
||||||
|
for item in items:
|
||||||
|
row = [
|
||||||
|
str(getattr(item, f)) if isinstance(getattr(item, f), UUID)
|
||||||
|
else getattr(item, f)
|
||||||
|
for f in config["fields"]
|
||||||
|
]
|
||||||
|
ws.append(row)
|
||||||
|
|
||||||
|
wb.save(path)
|
||||||
|
|
||||||
|
def _add_to_model(self, model: FinancialModel, sheet_name: str, data: dict) -> None:
|
||||||
|
try:
|
||||||
|
if sheet_name == "Accounts":
|
||||||
|
model.accounts.append(Account.from_dict(data))
|
||||||
|
elif sheet_name == "Transactions":
|
||||||
|
model.transactions.append(Transaction.from_dict(data))
|
||||||
|
elif sheet_name == "Recurring":
|
||||||
|
model.recurring.append(RecurringCashflow.from_dict(data))
|
||||||
|
elif sheet_name == "Assets":
|
||||||
|
model.assets.append(Asset.from_dict(data))
|
||||||
|
elif sheet_name == "Liabilities":
|
||||||
|
model.liabilities.append(Liability.from_dict(data))
|
||||||
|
except Exception as e:
|
||||||
|
raise SyncError(f"Failed to parse row in {sheet_name}: {e}") from e
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cashflow_model import (
|
||||||
|
Account,
|
||||||
|
Asset,
|
||||||
|
FinancialModel,
|
||||||
|
Liability,
|
||||||
|
RecurringCashflow,
|
||||||
|
Transaction,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sample_model() -> FinancialModel:
|
||||||
|
acc_id = uuid4()
|
||||||
|
return FinancialModel(
|
||||||
|
accounts=[
|
||||||
|
Account(id=acc_id, name="Основной счёт", currency="USD", balance=5000.0),
|
||||||
|
Account(name="Сбережения", currency="USD", balance=10000.0),
|
||||||
|
],
|
||||||
|
transactions=[
|
||||||
|
Transaction(
|
||||||
|
date="2026-01-01", account=str(acc_id),
|
||||||
|
category="income", amount=3000.0,
|
||||||
|
description="Зарплата",
|
||||||
|
),
|
||||||
|
Transaction(
|
||||||
|
date="2026-01-05", account=str(acc_id),
|
||||||
|
category="rent", amount=-1200.0,
|
||||||
|
description="Аренда",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
recurring=[
|
||||||
|
RecurringCashflow(
|
||||||
|
start_date="2026-01-01", frequency="monthly",
|
||||||
|
amount=500.0, category="income",
|
||||||
|
),
|
||||||
|
RecurringCashflow(
|
||||||
|
start_date="2026-01-01", frequency="monthly",
|
||||||
|
amount=-200.0, category="subscription",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
assets=[
|
||||||
|
Asset(name="Акции", value=50000.0, growth_rate=8.0),
|
||||||
|
],
|
||||||
|
liabilities=[
|
||||||
|
Liability(name="Кредит", balance=20000.0, interest=5.0, payment=500.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def empty_model() -> FinancialModel:
|
||||||
|
return FinancialModel()
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from ai.assistant import AssistantService
|
||||||
|
from ai.prompts import ADVICE_PROMPT, ANALYZE_PROMPT
|
||||||
|
|
||||||
|
|
||||||
|
class TestAssistantService:
|
||||||
|
def test_analyze_returns_prompt(self, sample_model):
|
||||||
|
assistant = AssistantService(sample_model)
|
||||||
|
result = assistant.analyze(months=6)
|
||||||
|
assert "prompt" in result
|
||||||
|
assert "summary" in result
|
||||||
|
assert "forecast" in result
|
||||||
|
assert result["ai_response"] is None
|
||||||
|
|
||||||
|
def test_advice_returns_prompt(self, sample_model):
|
||||||
|
assistant = AssistantService(sample_model)
|
||||||
|
result = assistant.advice("Как мне сэкономить?", months=6)
|
||||||
|
assert "prompt" in result
|
||||||
|
assert "ai_response" in result
|
||||||
|
|
||||||
|
def test_prompt_templates(self):
|
||||||
|
assert "{model_json}" in ANALYZE_PROMPT
|
||||||
|
assert "{forecast_json}" in ANALYZE_PROMPT
|
||||||
|
assert "{model_json}" in ADVICE_PROMPT
|
||||||
|
assert "{question}" in ADVICE_PROMPT
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
from typer.testing import CliRunner
|
||||||
|
|
||||||
|
from cli.main import app
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
|
||||||
|
|
||||||
|
class TestCli:
|
||||||
|
def test_init(self):
|
||||||
|
result = runner.invoke(app, ["init"])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert "Пустая модель" in result.stdout
|
||||||
|
|
||||||
|
def test_forecast_after_init(self):
|
||||||
|
runner.invoke(app, ["init"])
|
||||||
|
result = runner.invoke(app, ["forecast", "--months", "3"])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
def test_unknown_scenario(self):
|
||||||
|
result = runner.invoke(app, ["scenario", "unknown"])
|
||||||
|
assert result.exit_code != 0
|
||||||
|
|
||||||
|
def test_help(self):
|
||||||
|
result = runner.invoke(app, ["--help"])
|
||||||
|
assert result.exit_code == 0
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from cashflow_model import FinancialModel
|
||||||
|
from sync.excel_sync import ExcelSync, SyncError
|
||||||
|
|
||||||
|
|
||||||
|
class TestExcelSync:
|
||||||
|
def test_export_creates_file(self, sample_model, tmp_path: Path):
|
||||||
|
sync = ExcelSync()
|
||||||
|
p = tmp_path / "test.xlsx"
|
||||||
|
sync.export_model(sample_model, p)
|
||||||
|
assert p.exists()
|
||||||
|
|
||||||
|
def test_export_import_roundtrip(self, sample_model, tmp_path: Path):
|
||||||
|
sync = ExcelSync()
|
||||||
|
p = tmp_path / "roundtrip.xlsx"
|
||||||
|
sync.export_model(sample_model, p)
|
||||||
|
|
||||||
|
loaded = sync.import_model(p)
|
||||||
|
assert len(loaded.accounts) == len(sample_model.accounts)
|
||||||
|
assert len(loaded.transactions) == len(sample_model.transactions)
|
||||||
|
assert len(loaded.recurring) == len(sample_model.recurring)
|
||||||
|
assert len(loaded.assets) == len(sample_model.assets)
|
||||||
|
assert len(loaded.liabilities) == len(sample_model.liabilities)
|
||||||
|
|
||||||
|
def test_import_missing_file(self):
|
||||||
|
sync = ExcelSync()
|
||||||
|
try:
|
||||||
|
sync.import_model("nonexistent.xlsx")
|
||||||
|
assert False, "Expected SyncError"
|
||||||
|
except SyncError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_export_empty_model(self, tmp_path: Path):
|
||||||
|
sync = ExcelSync()
|
||||||
|
model = FinancialModel()
|
||||||
|
p = tmp_path / "empty.xlsx"
|
||||||
|
sync.export_model(model, p)
|
||||||
|
assert p.exists()
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from engine.forecast import ForecastError, ForecastService
|
||||||
|
|
||||||
|
|
||||||
|
class TestForecastService:
|
||||||
|
def test_forecast_returns_results(self, sample_model):
|
||||||
|
service = ForecastService(sample_model)
|
||||||
|
results = service.forecast_cashflow(months=12)
|
||||||
|
assert len(results) > 0
|
||||||
|
assert "balance" in results[0]
|
||||||
|
assert "month" in results[0]
|
||||||
|
|
||||||
|
def test_forecast_12_months(self, sample_model):
|
||||||
|
service = ForecastService(sample_model)
|
||||||
|
results = service.forecast_cashflow(months=12)
|
||||||
|
months = set(r["month"] for r in results)
|
||||||
|
assert max(months) == 12
|
||||||
|
|
||||||
|
def test_invalid_months(self, sample_model):
|
||||||
|
service = ForecastService(sample_model)
|
||||||
|
with pytest.raises(ForecastError):
|
||||||
|
service.forecast_cashflow(months=0)
|
||||||
|
|
||||||
|
def test_summary(self, sample_model):
|
||||||
|
service = ForecastService(sample_model)
|
||||||
|
s = service.summary(months=6)
|
||||||
|
assert "total_balance" in s
|
||||||
|
assert "total_income" in s
|
||||||
|
assert "total_expenses" in s
|
||||||
|
assert s["months"] == 6
|
||||||
|
|
||||||
|
def test_empty_model(self, empty_model):
|
||||||
|
service = ForecastService(empty_model)
|
||||||
|
results = service.forecast_cashflow(months=3)
|
||||||
|
assert results == []
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from cashflow_model import (
|
||||||
|
Account,
|
||||||
|
Asset,
|
||||||
|
FinancialModel,
|
||||||
|
ForecastScenario,
|
||||||
|
Liability,
|
||||||
|
RecurringCashflow,
|
||||||
|
Transaction,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestAccount:
|
||||||
|
def test_create(self):
|
||||||
|
a = Account(name="Test", balance=100.0)
|
||||||
|
assert a.name == "Test"
|
||||||
|
assert a.balance == 100.0
|
||||||
|
assert a.currency == "USD"
|
||||||
|
|
||||||
|
def test_to_dict_roundtrip(self):
|
||||||
|
a = Account(name="Test", balance=100.0)
|
||||||
|
d = a.to_dict()
|
||||||
|
a2 = Account.from_dict(d)
|
||||||
|
assert a2.name == a.name
|
||||||
|
assert a2.balance == a.balance
|
||||||
|
assert a2.currency == a.currency
|
||||||
|
|
||||||
|
|
||||||
|
class TestTransaction:
|
||||||
|
def test_create(self):
|
||||||
|
t = Transaction(amount=500.0, category="food")
|
||||||
|
assert t.amount == 500.0
|
||||||
|
|
||||||
|
def test_roundtrip(self):
|
||||||
|
t = Transaction(amount=-100.0, category="rent", description="test")
|
||||||
|
d = t.to_dict()
|
||||||
|
t2 = Transaction.from_dict(d)
|
||||||
|
assert t2.amount == t.amount
|
||||||
|
assert t2.category == t.category
|
||||||
|
assert t2.description == t.description
|
||||||
|
|
||||||
|
|
||||||
|
class TestFinancialModel:
|
||||||
|
def test_save_load(self, tmp_path: Path):
|
||||||
|
model = FinancialModel()
|
||||||
|
model.accounts.append(Account(name="Test", balance=100.0))
|
||||||
|
model.transactions.append(Transaction(amount=50.0, category="income"))
|
||||||
|
|
||||||
|
p = tmp_path / "model.json"
|
||||||
|
model.save(p)
|
||||||
|
assert p.exists()
|
||||||
|
|
||||||
|
loaded = FinancialModel.load(p)
|
||||||
|
assert len(loaded.accounts) == 1
|
||||||
|
assert len(loaded.transactions) == 1
|
||||||
|
assert loaded.accounts[0].name == "Test"
|
||||||
|
|
||||||
|
def test_empty_model(self):
|
||||||
|
model = FinancialModel()
|
||||||
|
d = model.to_dict()
|
||||||
|
assert d["accounts"] == []
|
||||||
|
assert d["transactions"] == []
|
||||||
|
|
||||||
|
def test_all_entities_roundtrip(self, tmp_path: Path):
|
||||||
|
model = FinancialModel(
|
||||||
|
accounts=[Account(name="A"), Account(name="B")],
|
||||||
|
transactions=[Transaction(amount=100.0)],
|
||||||
|
recurring=[RecurringCashflow(amount=50.0)],
|
||||||
|
assets=[Asset(name="Stock", value=1000.0)],
|
||||||
|
liabilities=[Liability(name="Loan", balance=500.0, interest=5.0, payment=100.0)],
|
||||||
|
scenarios=[ForecastScenario(name="test")],
|
||||||
|
)
|
||||||
|
p = tmp_path / "full.json"
|
||||||
|
model.save(p)
|
||||||
|
loaded = FinancialModel.load(p)
|
||||||
|
assert len(loaded.accounts) == 2
|
||||||
|
assert len(loaded.transactions) == 1
|
||||||
|
assert len(loaded.recurring) == 1
|
||||||
|
assert len(loaded.assets) == 1
|
||||||
|
assert len(loaded.liabilities) == 1
|
||||||
|
assert len(loaded.scenarios) == 1
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
from engine.scenarios import DEFAULT_SCENARIOS, ScenarioService
|
||||||
|
|
||||||
|
|
||||||
|
class TestScenarioService:
|
||||||
|
def test_baseline(self, sample_model):
|
||||||
|
service = ScenarioService(sample_model)
|
||||||
|
result = service.apply(DEFAULT_SCENARIOS["baseline"], months=6)
|
||||||
|
assert result["scenario"] == "baseline"
|
||||||
|
assert result["total_balance"] is not None
|
||||||
|
|
||||||
|
def test_compare_returns_three(self, sample_model):
|
||||||
|
service = ScenarioService(sample_model)
|
||||||
|
results = service.compare(months=6)
|
||||||
|
assert "baseline" in results
|
||||||
|
assert "optimistic" in results
|
||||||
|
assert "pessimistic" in results
|
||||||
|
|
||||||
|
def test_what_if(self, sample_model):
|
||||||
|
service = ScenarioService(sample_model)
|
||||||
|
result = service.what_if(income_mult=1.2, expense_mult=0.9, months=6)
|
||||||
|
assert result["scenario"] == "what-if"
|
||||||
Reference in New Issue
Block a user