# ipuaro: Локальный AI-агент для работы с кодовой базой ## Цель Один пользователь, одна сессия, работа с кодовой базой любого размера. Ощущение «бесконечного» контекста через ленивую загрузку и умное управление контекстом. --- ## Технологический стек | Компонент | Технология | |-----------|------------| | **Язык** | TypeScript | | **Runtime** | Node.js | | **TUI** | Ink (React для терминала) | | **Storage** | Redis с AOF persistence | | **AST парсинг** | tree-sitter (js, ts, jsx, tsx, json, yaml) | | **LLM** | Ollama API (qwen2.5-coder:7b-instruct, 128K контекст) | | **Git** | simple-git | | **File watching** | chokidar | | **CLI** | Commander.js | | **Тестирование** | Vitest | | **Сборка** | tsup | | **Gitignore парсинг** | ignore (npm) | | **Подсчёт токенов** | Ollama API | | **Хеширование файлов** | MD5 | ### Поддерживаемые языки Фокус на TypeScript/JavaScript экосистеме: - TypeScript (.ts, .tsx) - JavaScript (.js, .jsx) - JSON, YAML конфиги ### Структура проекта ``` src/ ├── cli/ # Точка входа, CLI парсинг │ └── index.ts ├── tui/ # Терминальный интерфейс │ ├── App.tsx # Главный компонент (Ink) │ ├── components/ # UI компоненты │ │ ├── Chat.tsx │ │ ├── StatusBar.tsx │ │ ├── DiffView.tsx # Отображение изменений (inline highlights) │ │ ├── ConfirmDialog.tsx # Подтверждение действий │ │ └── Input.tsx │ └── hooks/ # React hooks ├── core/ # Ядро системы │ ├── orchestrator.ts # Оркестратор │ ├── context.ts # Управление контекстом (ленивая загрузка) │ ├── undo.ts # Стек отмены (5-10 изменений) │ └── session.ts # Управление сессией ├── llm/ # Работа с моделью │ ├── client.ts # Ollama клиент │ ├── tools.ts # Определения тулов (18 штук) │ ├── prompts.ts # Системные промпты │ └── parser.ts # Парсинг ответов модели ├── indexer/ # Индексация проекта │ ├── scanner.ts # Сканирование файлов │ ├── parser.ts # AST парсинг (tree-sitter) │ ├── analyzer.ts # Анализ связей │ └── watcher.ts # Watchdog (chokidar) ├── redis/ # Работа с Redis │ ├── client.ts # Подключение + AOF config │ ├── schema.ts # Типы ключей (группировка по типу) │ └── queries.ts # Запросы ├── tools/ # Реализация тулов │ ├── read.ts # get_lines, get_function │ ├── search.ts # find_references, find_definition │ ├── edit.ts # edit_lines, create_file │ ├── git.ts # git_status, git_diff, git_commit │ ├── run.ts # run_command, run_tests │ └── index.ts # Регистрация всех тулов ├── security/ # Безопасность │ ├── blacklist.ts # Blacklist опасных команд │ ├── whitelist.ts # Whitelist разрешённых команд │ └── validator.ts # Валидация параметров ├── types/ # TypeScript типы │ ├── ast.ts │ ├── redis.ts │ ├── tools.ts │ └── session.ts └── utils/ # Утилиты ├── hash.ts └── tokens.ts # Подсчёт токенов tests/ ├── unit/ ├── integration/ └── fixtures/ config/ ├── default.json # Настройки по умолчанию ├── blacklist.json # Blacklist команд └── whitelist.json # Whitelist команд (расширяемый) ``` --- ## Архитектура ``` ┌─────────────────────────────────────────────────────────────┐ │ Пользователь │ └─────────────────────────────┬───────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ TUI (Ink/React) │ │ Chat режим │ │ - подтверждение изменений (или auto-apply режим) │ │ - diff с inline highlights │ │ - статистика: токены, время, вызванные tools │ └─────────────────────────────┬───────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Оркестратор │ │ - начальный контекст: структура + AST (без кода) │ │ - код подгружается через tools по запросу модели │ │ - user choice при ошибках (retry/skip/abort) │ │ - авто-сжатие контекста при >80% │ │ - обработка конфликтов редактирования │ └───────────┬─────────────────┬───────────────────────────────┘ │ │ ▼ ▼ ┌───────────────────┐ ┌───────────────────────────────────────┐ │ qwen2.5-coder │ │ Redis + AOF │ │ 7B, 128K ctx │ │ │ │ │ │ - код файлов (lines) │ │ - основная │ │ - AST структуры │ │ - рефакторинг │ │ - метаданные и индексы │ │ - ревью │ │ - сессии (бессрочно) │ │ - tools │ │ │ └───────────────────┘ └───────────────────────────────────────┘ ``` ### Ключевые принципы | Принцип | Реализация | |---------|------------| | **Одна модель** | qwen2.5-coder:7b-instruct для всех задач | | **Ленивая загрузка кода** | Код НЕ в начальном контексте. Модель запрашивает через tools | | **User choice** | При ошибке — выбор пользователя: retry/skip/abort | | **Код в Redis** | Дублирование для быстрого доступа без I/O | | **Подтверждение изменений** | По умолчанию спрашивать, опционально auto-apply | | **Undo stack** | История 5-10 изменений для отката | | **Без лимитов** | Нет ограничений на размер проекта и файлов | | **Язык промптов** | Системный промпт на EN, ответы адаптируются к языку пользователя | | **Монорепозитории** | Индексируется весь репозиторий | | **Tool format** | XML в промпте (как Claude Code: ``) | | **Encoding** | Только UTF-8, остальные пропускаются | | **Symlinks** | Хранятся как метаданные (имя, target) | --- ## Redis: структура данных ### Принцип: группировка по типу Вместо множества мелких ключей — несколько больших JSON-структур: ``` # ═══════════════════════════════════════════════════════════════ # ПРОЕКТ (5 основных ключей) # Имя проекта: {parent-folder}-{project-folder} # Пример: projects-myapp # ═══════════════════════════════════════════════════════════════ project:{name}:files # Hash: path → {lines[], hash, size, lastModified} project:{name}:ast # Hash: path → {imports[], exports[], functions[], classes[], parseError?} project:{name}:meta # Hash: path → {complexity, deps, dependents, isHub, isEntry} project:{name}:indexes # Hash: symbols, deps_graph, call_graph project:{name}:config # Hash: settings, whitelist, last_indexed # ═══════════════════════════════════════════════════════════════ # СЕССИИ (бессрочное хранение) # ═══════════════════════════════════════════════════════════════ session:{id}:data # Hash: history[], context, created_at, last_activity, stats session:{id}:undo # List: стек изменений для отмены (5-10 элементов) sessions:list # List: все session_id для проекта ``` ### Итого ключей | Категория | Ключей | Описание | |-----------|--------|----------| | Проект | 5 | files, ast, meta, indexes, config | | Сессия | 3 на сессию | data + undo + общий list | | **Всего** | ~10-20 | Компактная структура | ### Пример данных в project:{name}:files ```json { "src/auth/login.ts": { "lines": ["import jwt from 'jsonwebtoken'", "import { User } from '../models'", "..."], "hash": "a3f2b1c...", "size": 2431, "lastModified": 1705312200 } } ``` ### Пример данных в project:{name}:ast ```json { "src/auth/login.ts": { "imports": [ {"name": "jwt", "from": "jsonwebtoken", "line": 1, "type": "external"}, {"name": "User", "from": "../models", "line": 2, "type": "internal"} ], "exports": [ {"name": "AuthManager", "type": "class", "line": 10}, {"name": "refreshToken", "type": "function", "line": 55} ], "functions": [ {"name": "refreshToken", "lineStart": 55, "lineEnd": 70, "params": ["token"], "isAsync": true} ], "classes": [ { "name": "AuthManager", "lineStart": 10, "lineEnd": 52, "methods": [ {"name": "login", "lineStart": 15, "lineEnd": 30, "params": ["username", "password"]}, {"name": "validateToken", "lineStart": 32, "lineEnd": 50, "params": ["token"]} ] } ], "parseError": false } } ``` ### Redis конфигурация (AOF) ```conf # redis.conf appendonly yes appendfsync everysec auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb ``` --- ## Индексация проекта ### Первичная индексация ``` При первом запуске: 0. ПРОВЕРКА РАЗМЕРА └→ Если >10K файлов → показать предупреждение └→ Спросить: продолжить / выбрать поддиректорию / отмена 1. СКАНИРОВАНИЕ (с прогресс-баром) └→ [=====> ] 45% (120/267 files) └→ Обход файловой системы проекта └→ Фильтрация: .gitignore, node_modules, dist, etc └→ TypeScript/JavaScript файлы + JSON/YAML └→ Только UTF-8 файлы (остальные пропускаются) 2. ПАРСИНГ (для каждого файла, без ограничений размера) └→ Чтение файла → lines[] → Redis (project:{name}:files) └→ Парсинг через tree-sitter → Redis (project:{name}:ast) └→ При ошибке парсинга → parseError: true в AST └→ Базовые метрики → Redis (project:{name}:meta) 3. ПОСТРОЕНИЕ ИНДЕКСОВ └→ Граф импортов между файлами └→ Индекс символов (функции, классы) └→ Redis (project:{name}:indexes) ``` ### Watchdog (chokidar) ``` Фоновый процесс отслеживает изменения: 1. chokidar.watch() на директорию проекта 2. Debounce: 500ms (группировка быстрых изменений) 3. При изменении файла: └→ Пересчитать MD5 hash └→ Если изменился: └→ Тихо обновить lines в Redis └→ Перепарсить AST через tree-sitter └→ Обновить метрики 4. Модель получит свежие данные при следующем запросе через tools Бинарные файлы (.png, .pdf, etc): └→ Индексируются только метаданные (имя, размер, тип) └→ Содержимое не хранится ``` --- ## Tools модели (18 штук) ### Core Tools — Чтение | Tool | Описание | Параметры | |------|----------|-----------| | `get_lines` | Получить строки кода | `path`, `start?`, `end?` | | `get_function` | Получить функцию по имени | `path`, `name` | | `get_class` | Получить класс по имени | `path`, `name` | ### Core Tools — Редактирование (требуют подтверждения или auto-apply) | Tool | Описание | Параметры | |------|----------|-----------| | `edit_lines` | Заменить строки | `path`, `start`, `end`, `content` | | `create_file` | Создать новый файл | `path`, `content` | | `delete_file` | Удалить файл | `path` | ### Core Tools — Поиск и навигация | Tool | Описание | Параметры | |------|----------|-----------| | `find_references` | Где используется символ | `symbol` | | `find_definition` | Где определён символ | `symbol` | | `get_structure` | Получить структуру проекта | `path?` | ### Core Tools — Анализ | Tool | Описание | Параметры | |------|----------|-----------| | `get_dependencies` | От чего зависит файл | `path` | | `get_dependents` | Что зависит от файла | `path` | | `get_complexity` | Метрики сложности | `path?` | | `get_todos` | TODO/FIXME в проекте | `path?` | ### Core Tools — Git и выполнение | Tool | Описание | Параметры | |------|----------|-----------| | `git_status` | Статус репозитория | — | | `git_diff` | Незакоммиченные изменения | `path?` | | `git_commit` | Коммит изменений | `message`, `files?` | | `run_command` | Shell команда (whitelist) | `command` | | `run_tests` | Запустить тесты | `path?`, `filter?` | ### Итого: 18 tools Модель получает структуру проекта и AST в начальном контексте, а код запрашивает через tools по мере необходимости. --- ## Безопасность ### Blacklist опасных команд ```json { "blacklist": [ "rm -rf", "rm -r", "git push --force", "git reset --hard", "git clean -fd", "npm publish", "sudo", "chmod", "chown" ] } ``` ### Whitelist команд для run_command ```json { "default": [ "npm", "pnpm", "yarn", "git", "node", "npx", "tsx", "vitest", "jest", "tsc", "eslint", "prettier" ], "user": [] } ``` Пользователь может расширять whitelist через конфиг: ```json { "whitelist": { "user": ["bun", "deno", "cargo"] } } ``` ### Логика выполнения ```typescript async function runCommand(command: string): Promise { const [cmd, ...args] = command.split(' ') // 1. Проверка blacklist — безусловный отказ if (isBlacklisted(command)) { return { success: false, error: `Команда запрещена: ${command}` } } // 2. Проверка whitelist (default + user) if (!isWhitelisted(cmd)) { // 3. Спросить пользователя для неизвестных команд const confirmed = await askUserConfirmation( `Команда '${cmd}' не в whitelist. Выполнить?` ) if (!confirmed) { return { success: false, error: `Команда отклонена пользователем` } } } // 4. Выполнение return execute(command) } ``` ### Валидация параметров tools ```typescript function validatePath(path: string): boolean { // Запрет path traversal if (path.includes('..')) return false // Только внутри проекта if (!path.startsWith(projectRoot)) return false return true } ``` --- ## Управление контекстом ### Начальный контекст (при старте сессии) ``` ФИКСИРОВАННАЯ ЧАСТЬ: ├── Системный промпт с ролью и правилами ├── Список tools с описаниями └── ВСЁ из Redis КРОМЕ кода: ├── Структура проекта (папки, файлы) ├── AST метаданные (imports, exports, функции, классы — БЕЗ тел) ├── Граф зависимостей между файлами └── Метрики (complexity, hubs, entry points) КОД НЕ ВКЛЮЧАЕТСЯ — модель запрашивает через tools по необходимости ``` ### Бюджет токенов (128K окно) | Компонент | Токены | % | |-----------|--------|---| | Системный промпт | ~2,000 | 1.5% | | Структура + AST meta | ~10,000 | 8% | | **Доступно для работы** | ~116,000 | 90% | ### Авто-сжатие контекста ``` При заполнении >80%: 1. Комбинированное сжатие: └→ LLM суммаризирует старые сообщения └→ Удаляются tool results (код из get_lines, etc.) └→ Остаются ключевые решения и структура диалога 2. Удалить код файлов не упомянутых 5+ сообщений └→ Можно запросить снова через tools 3. Уведомление: только в status bar (ctx% меняется) └→ Нет модального уведомления ``` --- ## Error Handling ### Принцип: User Choice ```typescript interface ErrorResult { type: 'redis' | 'parse' | 'llm' | 'file' | 'command' | 'conflict' message: string recoverable: boolean } interface UserChoice { retry: boolean skip: boolean abort: boolean } async function handleError(error: ErrorResult): Promise { // 1. Логировать (локальная статистика) stats.recordError(error) // 2. Показать ошибку ui.showError(`❌ ${error.type}: ${error.message}`) // 3. Дать выбор пользователю (ВСЕГДА) return ui.askChoice({ message: 'Что делать?', options: [ { key: 'r', label: 'Retry — попробовать снова' }, { key: 's', label: 'Skip — пропустить и продолжить' }, { key: 'a', label: 'Abort — остановить' } ] }) } ``` ### Обработка конфликтов редактирования ```typescript async function handleEditConflict( path: string, expectedHash: string, currentHash: string ): Promise { ui.showWarning(`⚠️ Файл ${path} был изменён во время генерации`) return ui.askChoice({ message: 'Файл изменился. Что делать?', options: [ { key: 'a', label: 'Apply — применить поверх новой версии' }, { key: 's', label: 'Skip — пропустить это изменение' }, { key: 'r', label: 'Regenerate — попросить модель заново' } ] }) } ``` ### Типичные ошибки и варианты | Ошибка | Варианты | |--------|----------| | Redis недоступен | Retry / Abort | | AST парсинг упал | Skip файл (пометить parseError) / Abort | | LLM timeout | Retry / Skip запрос / Abort | | Файл не найден | Skip / Abort | | Команда не в whitelist | Подтвердить / Skip / Abort | | Конфликт редактирования | Apply / Skip / Regenerate | --- ## TUI Интерфейс ### Структура экрана ``` ┌─────────────────────────────────────────────────────────────┐ │ [ipuaro] [ctx: 12%] [project: projects-myapp] [47m] ✓ │ ├─────────────────────────────────────────────────────────────┤ │ │ │ User: объясни как работает авторизация │ │ │ │ Agent: [get_function src/auth/login.ts login] │ │ Авторизация построена на JWT... │ │ ⏱ 3.2s │ 1,247 tokens │ 1 tool call │ │ │ │ User: отрефактори функцию validateToken │ │ │ │ Agent: Предлагаю следующие изменения: │ │ │ │ ┌─── src/auth/login.ts (строки 32-50) ───┐ │ │ │ const isValid = jwt.verify(token) │ │ │ │ const isValid = this.verifyToken() │ ← изменено │ │ │ if (!isValid) throw new AuthError() │ ← добавлено │ │ └────────────────────────────────────────┘ │ │ │ │ Изменения: выделил валидацию, добавил проверку expiry │ │ │ │ ┌────────────────────────────────────────┐ │ │ │ [Y] Применить [N] Отменить [E] Edit │ │ │ └────────────────────────────────────────┘ │ │ │ ├─────────────────────────────────────────────────────────────┤ │ > _ │ └─────────────────────────────────────────────────────────────┘ ``` ### Команды (7 штук) | Команда | Действие | |---------|----------| | `/help` | Показать справку | | `/clear` | Очистить историю чата | | `/undo` | Отменить изменение (стек 5-10) | | `/sessions` | Управление сессиями (list, load, delete) | | `/status` | Состояние системы (Redis, контекст, модель, статистика) | | `/reindex` | Принудительная переиндексация проекта | | `/eval` | Самопроверка модели на галлюцинации (LLM проверяет свой ответ) | **Всё остальное — через естественный язык в чате.** Примеры: - "покажи все TODO" → модель вызывает `get_todos` - "найди где используется AuthManager" → модель вызывает `find_references` - "запусти тесты для auth" → модель вызывает `run_tests` ### Режим работы **Только Chat.** Один режим для всех задач: - Код-ревью - Рефакторинг - Написание тестов - Документирование - Навигация по коду - Git операции ### Режим Auto-Apply Опциональный режим для опытных пользователей: ```typescript // В конфиге или через команду { "edit": { "autoApply": false // по умолчанию выключен } } // Включить на сессию > /auto-apply on // При включённом режиме изменения применяются автоматически // Но всегда можно отменить через /undo ``` ### Индикаторы статус-бара | Индикатор | Описание | |-----------|----------| | `ctx: 12%` | Заполненность контекста | | `project: projects-myapp` | Текущий проект (parent-folder) | | `main` | Текущая git branch | | `47m` | Время сессии | | `✓` / `⟳` / `✗` | ready / thinking / error | ### Статистика ответа После каждого ответа модели показывается: - ⏱ Время генерации - 📊 Количество токенов - 🔧 Количество вызванных tools ### Горячие клавиши | Клавиша | Действие | |---------|----------| | `Ctrl+C` | Прервать генерацию (1-й раз), выход (2-й раз) | | `Ctrl+D` | Выход с автосохранением сессии | | `Ctrl+Z` | Undo (= /undo) | | `↑/↓` | История ввода | | `Tab` | Автодополнение путей | ### Подтверждение изменений При каждом edit_lines, create_file, delete_file (если не auto-apply): 1. **Diff с inline highlights** — подсветка изменённых символов 2. **Syntax highlighting** — полная подсветка синтаксиса в diff 3. **Описание** — краткое объяснение что изменится 4. **Длинные строки** — wrap в терминале (хранятся полностью) 5. **Выбор**: - `[Y]` — применить изменение - `[N]` — отменить - `[E]` — редактировать предложенный код --- ## TypeScript типы ### Метаданные файла ```typescript interface FileData { lines: string[] hash: string size: number lastModified: number } interface FileAST { imports: ImportInfo[] exports: ExportInfo[] functions: FunctionInfo[] classes: ClassInfo[] parseError: boolean // true если tree-sitter не смог распарсить } interface FileMeta { complexity: ComplexityScore // Простой скор: LOC + вложенность + imports dependencies: string[] dependents: string[] isHub: boolean // Комбинированно: >5 dependents ИЛИ часто импортируется isEntryPoint: boolean // Комбинированно: index.ts/main.ts ИЛИ 0 dependents } interface ComplexityScore { loc: number // Lines of code nesting: number // Макс. вложенность imports: number // Количество imports score: number // Общий скор } ``` ### AST структуры ```typescript interface ImportInfo { name: string from: string line: number type: 'internal' | 'external' | 'builtin' isDefault: boolean } interface ExportInfo { name: string type: 'function' | 'class' | 'interface' | 'type' | 'const' line: number } interface FunctionInfo { name: string lineStart: number lineEnd: number params: string[] returnType?: string isAsync: boolean isExported: boolean } interface ClassInfo { name: string lineStart: number lineEnd: number methods: MethodInfo[] properties: PropertyInfo[] extends?: string implements: string[] isExported: boolean } interface MethodInfo { name: string lineStart: number lineEnd: number params: string[] visibility: 'public' | 'private' | 'protected' isStatic: boolean isAsync: boolean } interface PropertyInfo { name: string line: number type?: string visibility: 'public' | 'private' | 'protected' isStatic: boolean isReadonly: boolean } ``` ### Сессия ```typescript interface Session { id: string projectName: string // формат: parent-folder createdAt: number lastActivityAt: number history: ChatMessage[] context: ContextState undoStack: UndoEntry[] stats: SessionStats inputHistory: string[] // История ввода для ↑/↓ (хранится в Redis) } interface SessionStats { totalTokens: number totalTime: number toolCalls: number editsApplied: number editsRejected: number } interface ChatMessage { role: 'user' | 'assistant' | 'tool' content: string timestamp: number toolCalls?: ToolCall[] toolResults?: ToolResult[] stats?: MessageStats } interface MessageStats { tokens: number timeMs: number toolCalls: number } interface ContextState { loadedFiles: string[] // Файлы загруженные в контекст tokensUsed: number tokensLimit: number // 128000 } interface UndoEntry { id: string timestamp: number filePath: string previousContent: string[] newContent: string[] description: string } ``` ### Tools ```typescript interface ToolDefinition { name: string description: string parameters: ToolParameter[] handler: (params: Record) => Promise requiresConfirmation: boolean // true для edit/create/delete (если не auto-apply) } interface ToolParameter { name: string type: 'string' | 'number' | 'boolean' description: string required: boolean default?: unknown } interface ToolCall { id: string name: string parameters: Record } interface ToolResult { callId: string success: boolean data?: unknown error?: string } ``` ### Error Handling ```typescript interface ErrorResult { type: 'redis' | 'parse' | 'llm' | 'file' | 'command' | 'conflict' message: string recoverable: boolean suggestion?: string } type UserErrorChoice = 'retry' | 'skip' | 'abort' type ConflictChoice = 'apply' | 'skip' | 'regenerate' ``` --- ## Флоу работы ### Общий флоу ``` 0. ONBOARDING (минимальный, при первом запуске) └→ Проверка Redis: подключение → если нет, ошибка с инструкцией └→ Проверка Ollama: доступность → если нет, ошибка и выход └→ Проверка модели: qwen2.5-coder:7b-instruct → если нет, предложить скачать 1. ИНИЦИАЛИЗАЦИЯ └→ Подключение к Redis └→ Проверка проекта в Redis └→ Если нет → первичная индексация (tree-sitter) └→ Запуск watchdog (chokidar, debounce 500ms) └→ Загрузка последней сессии или создание новой 2. ЗАПРОС ПОЛЬЗОВАТЕЛЯ └→ Добавить в историю └→ Сформировать контекст: - Системный промпт - Структура проекта + AST (без кода!) - История (последние N сообщений) └→ Отправить в модель └→ Показать индикатор [⟳ thinking...] 3. ОБРАБОТКА ОТВЕТА (ждём полный ответ) └→ Показать все tool calls по мере получения └→ Если tool call (чтение): └→ Валидация параметров └→ Выполнение └→ Результат обратно в модель └→ Если tool call (редактирование): └→ Проверить конфликт (hash изменился?) └→ Если конфликт → спросить пользователя └→ Показать diff с inline highlights + описание └→ Если auto-apply → применить автоматически └→ Иначе → СПРОСИТЬ подтверждение (Y/N/E) └→ Если Y: └→ Сохранить в undo stack └→ Применить изменения └→ Обновить Redis (lines, AST) └→ Показать статистику: время, токены, tools └→ Показать ответ пользователю 4. ОШИБКА → User Choice (всегда) └→ Показать ошибку └→ Спросить: Retry / Skip / Abort └→ Выполнить выбор пользователя 5. ПОВТОРИТЬ с шага 2 ``` ### Флоу код-ревью (через чат) ``` User: "сделай ревью src/auth/login.ts" Agent: 1. get_lines(src/auth/login.ts) → получить код 2. get_dependencies(src/auth/login.ts) → понять контекст 3. get_todos(src/auth/login.ts) → найти TODO 4. Анализ: - Баги и edge cases - Стиль кода - Security issues - Performance issues 5. Вывод: список замечаний с номерами строк 6. Статистика: ⏱ 5.2s │ 2,847 tokens │ 3 tool calls ``` ### Флоу рефакторинга (через чат) ``` User: "отрефактори функцию validateToken" Agent: 1. find_definition(validateToken) → найти где 2. get_function(path, validateToken) → получить код 3. find_references(validateToken) → где используется 4. Планирование изменений 5. Проверить не изменился ли файл 6. Показать diff с inline highlights + описание 7. Спросить подтверждение [Y/N/E] (или auto-apply) 8. Если Y: edit_lines(...) → применить 9. Добавить в undo stack 10. Статистика ответа ``` ### Флоу написания тестов (через чат) ``` User: "напиши тесты для AuthManager.login" Agent: 1. get_class(src/auth/login.ts, AuthManager) → получить класс 2. get_dependencies(src/auth/login.ts) → что мокать 3. Генерация тестов: - Happy path - Edge cases - Error cases 4. Показать diff для нового файла 5. Спросить подтверждение [Y/N/E] 6. Если Y: create_file(tests/auth/login.test.ts, content) 7. run_tests(tests/auth/login.test.ts) → проверить ``` --- ## Что НЕ делаем | Отвергнуто | Причина | |------------|---------| | RAG с векторной БД | Оверкилл, tree-sitter + Redis достаточно | | Вторая модель для ревью | Overhead, одна модель справляется | | TypeScript Compiler API | tree-sitter легче и мультиязычнее | | Regex поиск по коду | Модель сама запрашивает нужный код через tools | | Understanding через LLM | Модель понимает код при чтении через tools | | Множество режимов работы | Один Chat режим покрывает всё | | Сложная структура Redis | Группировка по типу проще | | Python/Go/Rust поддержка | Фокус на TypeScript/JS | | Автоматическое восстановление | User choice надёжнее | | Стриминг по токенам | Ждём полный ответ + статистика | | Внешние API интеграции | Только TUI, монолит | | Плагины/расширения | Всё встроено | | Файловые логи | Только локальная статистика | | Миграции данных | При несовместимости — /reindex | --- ## Конфигурация ### config/default.json ```json { "redis": { "host": "localhost", "port": 6379, "db": 0 }, "llm": { "provider": "ollama", "model": "qwen2.5-coder:7b-instruct", "contextWindow": 128000, "temperature": 0.1, "systemPromptLanguage": "en" }, "project": { "scope": "repository", "ignorePatterns": [ "node_modules", "dist", "build", ".git", "*.min.js" ], "binaryExtensions": [".png", ".jpg", ".pdf", ".zip", ".woff"] }, "session": { "persistIndefinitely": true, "maxHistoryMessages": 100, "saveInputHistory": true }, "context": { "systemPromptTokens": 2000, "maxContextUsage": 0.8, "autoCompressAt": 0.8, "compressionMethod": "llm-summary" }, "watchdog": { "debounceMs": 500 }, "commands": { "timeout": null }, "undo": { "stackSize": 10 }, "edit": { "alwaysConfirm": true, "autoApply": false, "showDiff": true, "diffFormat": "inline-highlights", "syntaxHighlight": true, "showDescription": true, "wrapLongLines": true }, "display": { "showStats": true, "showToolCalls": true, "theme": "dark", "bellOnComplete": true, "progressBar": true }, "autocomplete": { "source": "redis-index" }, "input": { "multiline": "auto" }, "llm": { "toolFormat": "xml", "autoRetry": false, "cache": false }, "limits": { "maxFilesWarning": 10000, "encoding": "utf-8" } } ``` ### config/blacklist.json ```json { "commands": [ "rm -rf", "rm -r", "git push --force", "git reset --hard", "git clean -fd", "npm publish", "sudo", "chmod", "chown" ] } ``` ### config/whitelist.json ```json { "default": [ "npm", "pnpm", "yarn", "git", "node", "npx", "tsx", "vitest", "jest", "tsc", "eslint", "prettier" ], "user": [], "askForUnknown": true } ``` --- ## Метрики успеха | Метрика | Цель | |---------|------| | Время первой индексации | Зависит от размера проекта | | Время ответа модели | Стриминг, показывать статистику | | Использование памяти Redis | Зависит от проекта | | Точность AST парсинга (tree-sitter) | > 99% для TypeScript | | Время синхронизации watchdog | < 1 секунда | | Локальная статистика | Токены, время, tools за сессию |