feat(fs): файловые тулы для агента — читает/ищет/пишет файлы проекта - #17
Conversation
…в tool-loop Добавляет встроенный источник файловых инструментов, дающий агенту реальную работу с файлами без внешнего MCP-сервера. - infra/local_fs_client.py: LocalFilesystemClient реализует протокол McpClient напрямую над ФС. Тулы fs__list_dir/read_file/search (grep, glob/regex)/write_file. Sandbox-root (по умолчанию cwd, override JARVIS_FS_ROOT); выход за корень запрещён. - Запись = unified diff + инъектируемый confirm(rel, diff)->bool. - cli/fs_confirm.py: интерактивное подтверждение с цветным diff (красный/зелёный). - infra/mcp_registry.py: register() для in-process клиента; ToolRouter сам роутит fs__*. - cli/main.py: сборка в composition root (provider=deepseek). - examples/fs_agent_demo.py: воспроизводимое демо (JARVIS_DEMO_SCRIPTED=1 без ключа). - Тесты: infra (tmp_path), e2e через ToolRouter, cli confirm. pytest — зелёный. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WPCiJi7DVBdV2fjN2x3HMd
🤖 AI-ревью🐞 Потенциальные баги
🏛 Архитектурные проблемы
💡 Рекомендации
Контекст (RAG): cli/main.py · CLAUDE.md — Jarvis CLI > Конвенции · domain/mcp.py — не накладывает, поэтому при склейке Сгенерировано jarvis-cli · review_pr.py |
По замечанию AI-ревью: sys импортировался внутри make_interactive_confirm при каждом вызове. Перенесён на уровень модуля. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WPCiJi7DVBdV2fjN2x3HMd
🤖 AI-ревью🐞 Потенциальные баги— замечаний нет (в представленном diff багов не обнаружено). 🏛 Архитектурные проблемы
💡 Рекомендации
Контекст (RAG): cli/main.py · CLAUDE.md — Jarvis CLI > Конвенции · domain/mcp.py — не накладывает, поэтому при склейке Сгенерировано jarvis-cli · review_pr.py |
Что это
Прокачивает jarvis-cli до агента, который реально работает с файлами проекта, а не просто отвечает текстом. Ключевая идея: в проекте уже был весь tool-calling каркас (
ToolRouter,McpRegistry, протоколMcpClient, live-прогресс) — не хватало только источника файловых инструментов. Он и добавлен, бесшовно встраиваясь в существующий tool-loop.Как работает
infra/local_fs_client.py::LocalFilesystemClientреализует тот же протоколMcpClient, но операции идут в ФС напрямую (не по JSON-RPC). За счёт этого клиент встаёт вMcpRegistry.register(), аToolRouterсам отдаёт его тулы модели и роутит вызовыfs__*— правок в tool-loop не потребовалось.Тулы:
fs__list_dir,fs__read_file,fs__search(grep по дереву, glob/regex),fs__write_file.JARVIS_FS_ROOT, по умолчанию — cwd, откуда запущенjarvis, так что агента можно подключить к любому проекту из его терминала). Выход за корень (.., симлинк) отклоняется; служебные каталоги (.git,.venv, …) не обходятся.write_fileсчитает unified diff и вызывает инъектируемыйconfirm(rel, diff)->bool. В CLI (cli/fs_confirm.py) печатается цветной diff (удалённые строки красным, добавленные зелёным) и запрашивается y/N — без подтверждения на диск ничего не пишется.provider=deepseek(tool calling).Сценарии (агент сам инициирует, цель уровня «что», а не «открой файл X»)
fs__searchпо нескольким файлам + чтение.fs__write_file.Воспроизводимость
```bash
JARVIS_DEMO_SCRIPTED=1 python3 examples/fs_agent_demo.py # без ключа, детерминированно
python3 examples/fs_agent_demo.py # реальный DeepSeek
```
Проверено реальным DeepSeek: агент сам поискал использования, прочитал файлы, обработал ошибку несуществующего каталога и сгенерировал ADR с таблицей потребителей.
Тесты
tests/infra/test_local_fs_client.py— чтение/поиск/sandbox/запись через confirm (tmp_path)tests/app/test_fs_agent_e2e.py— полный tool-loop через реальныйToolRouter+LocalFilesystemClient, LLM замоканtests/cli/test_fs_confirm.py— раскраска diff и y/n-логикаpytest— 513 passed.🤖 Generated with Claude Code
https://claude.ai/code/session_01WPCiJi7DVBdV2fjN2x3HMd