Files
claude-skills/.wiki/concepts/interns-repo-read-design.md

145 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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.