--- date: '2026-05-05' status: design-approved parent: concepts/interns-design.md source_buffer: .meeting-room/.brainstorm/repo-read.md title: interns-repo-read-design type: concept ingested_at: '2026-05-05T20:04:56.490Z' ingested_by: OpeItcLoc03@DESKTOP-NSEF0UK source_project: .meeting-room --- # Interns — `repo_read` intern (v0.1.0) Расширение каталога `interns-mcp` MVP: добавляет интерн `repo_read`, специализированный на «прочитай эту директорию/репо и ответь на вопрос». Реализуется поверх существующей трёхслойной архитектуры (config / MCP server / skills) без изменений в архитектуре, только новый tool + routing-добавка в `using-interns`. ## Context Из исходного research line: «Repomix лучше код». Интерн оборачивает `repomix` CLI + cheap LLM (`deepseek-v4-flash`) в один MCP tool, чтобы Claude делегировал понимание целой кодовой базы дешёвой модели вместо того чтобы прочитать репо своим Read'ом и сжечь Anthropic-квоту. Это первый **tool-wrapping** интерн (subprocess + LLM-call в одной операции) — паттерн под будущие PDF (Marker) и JS-web (Firecrawl) интерны. ## Decisions | # | Решение | Аргумент | |---|---|---| | 1 | Шейп — композитный `repo_read(paths, question) → answer` | Симметрия с `bulk_text_read(paths, question) → answer`. Pure pack не экономит квоту — Claude всё равно читает результат. | | 2 | Tool name `repo_read` (не `repo_qa`, не `code_read`) | Симметрия с `bulk_text_read` (тоже семантически Q&A, но `_read`). Не `code_read` — repomix именно про **директорию целиком**, оставляем `code_read` под будущий grep+cheap-LLM интерн. | | 3 | Overflow при превышении контекста — hard fail с подсказкой | YAGNI. Без авто-compress / map-reduce / smart-selection. Параметр `compress: bool = False` явный, user-controlled. | | 4 | Runtime — `npx repomix@latest` | Zero-config: `setup-interns` только проверяет `node` в PATH. Всегда свежая версия. Первый запуск 5-10s (npm cache populate), потом instant. Pin'ить версию — позже, если будут регрессии. | | 5 | Safety — два слоя: input matcher + transitive `--ignore` | Always-ask matcher проверяет input `paths` (как у остальных interns). Дополнительно: `safety.always_ask_globs()` транслируется в флаги `--ignore` для repomix subprocess'а. Repomix walks recursively, нельзя надеяться что `.gitignore` юзера полный. Защита on-server. | | 6 | Без `include`/`ignore` параметров от Claude | YAGNI: исключение через узкие `paths` + `.gitignore` + always-ask. Если упрёмся — добавим в v0.2.0. | ## Сигнатура ```python repo_read( paths: list[str], # директории и/или файлы — targets для repomix question: str, # вопрос compress: bool = False, # repomix --compress (lossy: убирает комменты/whitespace) ) -> InternResponse ``` `InternResponse = {text: str, usage: {tokens_in, tokens_out, cost_usd, packed_files: int, packed_tokens: int}}` При overflow: вместо `InternResponse` возвращается `BudgetExceededError{tokens, limit, top_files: [...], hint}` — Claude получает structured ответ и переформулирует (узкие paths или `compress=True`). ## Реализация (`interns_mcp/interns/repo_read.py`) ```python class RepoRead(Intern): id = "repo_read" def run(self, paths, question, compress=False): safety.check_paths(paths) # raise if always-ask ignore_globs = safety.always_ask_globs() # transitive guard with tempfile.NamedTemporaryFile(suffix=".xml", delete=False) as packed: args = ["npx", "repomix@latest", *paths, "--output", packed.name, "--style", "xml"] if compress: args.append("--compress") for glob in ignore_globs: args += ["--ignore", glob] subprocess.run(args, check=True, timeout=120) packed_text = Path(packed.name).read_text() tokens = count_tokens(packed_text, model=self.config.model) if tokens > self.config.context_budget: return BudgetExceededError( tokens=tokens, limit=self.config.context_budget, top_files=top_files_by_size(packed_text, n=5), hint="use compress=True or narrow paths", ) return self.client.complete( system=self.config.system_prompt, user=f"# Packed repo\n\n{packed_text}\n\n# Question\n\n{question}", max_tokens=self.config.max_tokens, ) ``` ## Layer 1 — config (`.common/config/interns/config.yaml`) ```yaml interns: repo_read: description: "Pack a repo/directory via repomix and answer a focused question via cheap LLM." endpoint: ollama_cloud model: deepseek-v4-flash max_tokens: 4096 temperature: 0.2 context_budget: 120000 # input-token cap before BudgetExceededError system_prompt: | You are reading a packed code repository. Answer ONLY what the user asks, citing file:line refs from the packed structure. Do not hallucinate file contents. If the answer requires files outside the pack, say so explicitly. ``` ## Layer 3 — skill update `using-interns/SKILL.md`, секция Routing-подсказки — добавить: > - Вопрос про целое репо/директорию (архитектура, «где используется X», «что делает модуль Y», обзор кодбазы) и нужно прочесть >5 файлов кода → `repo_read`. > - `bulk_text_read` vs `repo_read`: первый — пути известны и явные; второй — нужна целая директория без явного выбора файлов. > - Не делегировать `repo_read` для редактирования или отладки конкретного файла — читать файл сам. ## Setup-side change `setup-interns/SKILL.md` — добавить runtime check: 1. `shutil.which("node")` — если нет, instruct: install Node.js 20+, retry. 2. (Опц.) Pre-warm: `npx --yes repomix@latest --version` чтобы первый реальный вызов был быстрым. Bump `setup-interns` 0.1.0 → 0.2.0 (MINOR — capability added). ## Cross-platform | Слой | Windows | Linux | macOS | |---|---|---|---| | `npx repomix@latest` | OK через node from PATH | OK | OK | | `tempfile.NamedTemporaryFile` | OK | OK | OK | | Always-ask globs (`PurePath.match`) | OK POSIX-style работают везде | OK | OK | | `subprocess.run` timeout | OK | OK | OK | ## Action items - [ ] **`.common/lib/interns-mcp`**: реализовать `interns_mcp/interns/repo_read.py`, зарегистрировать в `server.py`. Тесты: happy-path, safety-block (`paths=[".env"]`), budget-exceeded. Patch-bump `interns-mcp`. - [ ] **`.common/config/interns/config.yaml`**: добавить запись `repo_read`. - [ ] **`claude-skills/using-interns`**: добавить routing-подсказки в SKILL.md. Bump MINOR. - [ ] **`claude-skills/setup-interns`**: добавить Node-check + (опц.) pre-warm. Bump MINOR. ## Open questions - Реальный context window `deepseek-v4-flash` на Ollama Cloud — проверить эмпирически после первого боевого вызова, скорректировать `context_budget`. - Когда появится 2-3-й tool-wrapping интерн (PDF/web) — выделить общий helper `interns_mcp/tool_wrapper.py` для пары (subprocess + safety-translation + budget-check). Сейчас inline в `repo_read.py`. - Cost-cap >$0.10 (упомянут в parent spec как always-ask trigger) — не реализован. Если появится несколько tool-wrapping interns с разной стоимостью — добавим `safety.check_cost(estimated)` рядом с `check_paths`. ## References - `concepts/interns-design.md` — parent design (архитектура interns-mcp, "How to add an intern" recipe, action items). - [Repomix](https://github.com/yamadashy/repomix) — JS CLI, упаковывает директорию в один LLM-friendly файл. - `using-interns/SKILL.md` — целевой файл для routing-добавки. - `setup-interns/SKILL.md` — целевой файл для Node-check.