meta(wiki): ingest concepts/interns-repo-read-design in claude-skills
This commit is contained in:
144
.wiki/concepts/interns-repo-read-design.md
Normal file
144
.wiki/concepts/interns-repo-read-design.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
Reference in New Issue
Block a user