W skrócie i werdykt
Cztery zadania, które wciąż wpisujesz w Claude Code, należą do czterech różnych plików. Checklista release'u to skill. Reguła, która nigdy nie może się złamać, to hook PreToolUse. Audyt czytający wiele plików to subagent. Dane, których Claude nie widzi, to serwer MCP.
Wykonaliśmy wszystkie cztery na jednym prawdziwym repozytorium, API do faktur z 34 testami, za każdym razem najpierw źle wybierając. Złe wybory zawodziły na ekranie:
- Checklista w CLAUDE.md dodała do /context linię
Memory files: 210 tokens, w każdej turze. - Pierwszy hook się uruchomił, wypisał ostrzeżenie i zapis przeszedł (unitPrice z 8000 na 12000).
- Audyt uruchomiony inline zalał wątek: Messages na poziomie 19.3k tokenów, wobec 7.2k z nazwanym subagentem.
- Bez serwera dokumentacji Claude odpowiedział "I couldn't find that setting... I won't guess one."
Werdykt: wybierz rodzaj z tabeli z dokumentacji, a potem spraw, żeby plik odpowiadał, a nie tylko istniał. Hook, który tylko loguje, jest doradczy, tak jak linia w CLAUDE.md, którą zastąpił. Spośród czterech tylko hook nigdy nie pyta.
Skopiuj pliki poniżej, zrób commit i zacznij od tego, który wpisujesz najczęściej.
Synteza ze źródłami
Co twoja sesja ładuje przed twoim pierwszym słowem. Nasz bazowy /context pokazywał 26.2k/1m tokenów, bez linii pamięci i bez linii MCP. Każde zadanie, które dodajesz, wstawia linię do tej tabeli albo nie. Przeczytaj ją najpierw, potem mierz względem niej.
Reguła w CLAUDE.md to prośba, hook to egzekwowanie. Dokumentacja mówi, że instrukcja w rodzaju "never edit .env" w CLAUDE.md lub w skillu to prośba, a hook PreToolUse blokujący edycję to egzekwowanie s1. Strona Memory nazywa CLAUDE.md kontekstem, a nie egzekwowaniem s8. Wątek na Hacker News opisał to samo od strony hooków: hook kończący się kodem 0 niczego nie blokuje s10.
Tabela wyzwalaczy mówi, który plik. Trzecie wklejenie procedury oznacza skill. Wyjście, które zalewa czat, oznacza subagenta. Karta, której Claude nie widzi, oznacza serwer MCP. Coś, co ma się dziać za każdym razem, oznacza hook s1.
Zadanie 1, checklista release'u, to skill.
- Procedura, która urosła w CLAUDE.md, należy do skilla, a jego treść ładuje się tylko wtedy, gdy jest używana s3.
- W naszym uruchomieniu checklista wklejona do CLAUDE.md dodała linię
Memory files: 210 tokens. Jako skill zdisable-model-invocation: true/context wrócił do punktu bazowego. /releasewykonał sześć kroków (34 testy, 0.4.0 do 0.4.1, tag v0.4.1). Zauważył też, żegit commit -ampomija pliki nieśledzone, więc najpierw dodał nowy changelog do poczekalni.
Zadanie 2, reguła, która musi trzymać, to hook, a gwarancją jest odpowiedź, którą zwraca.
- Exit 0 bez wyjścia nie jest decyzją. Exit 2 blokuje. JSON deny też blokuje s2.
- Nasz pierwszy hook logował, kończył się kodem 0 i zapis przeszedł (unitPrice z 8000 na 12000). Odpowiedź Claude'a ani razu nie wspomniała o hooku.
- Ten sam hook zwracający JSON deny odrzucił edycję z komunikatem
PreToolUse:Edit hook error: Stored data is never edited by hand: write a migration in src/store/migrations.ts (README § Conventions).Claude zapytał wtedy: obejść, czy napisać migrację. - Dokumentacja publikuje przepis PreToolUse na chronione pliki s23.
- Ograniczenie: ten hook pilnuje Edit i Write. Pokryj także powłokę albo trzymaj powłokę poza swoją allow listą.
onFailure: "block"sprawia, że nieudany hook blokuje zamiast przepuszczać. Pojawiło się w v2.1.295 s2 s15.
Zadanie 3, audyt, który zalewa, to subagent.
- Subagent startuje w ciemno i zwraca podsumowanie s4.
- Inline Messages pokazywało 19.3k. Z nazwanym subagentem pokazywało 7.2k.
- Sam plik agenta delegował w 0 z 2 uruchomień. Nazwanie go w prompcie delegowało. Plik sprawia, że istnieje, twój prompt sprawia, że Claude go używa.
- Przypnij
model:w pliku. Rachunek się nie zmienił: czytanie i tak się odbywa, tylko nie w twoim oknie.
Zadanie 4, dane poza zasięgiem Claude'a, to serwer MCP.
claude mcp add --scope projectzapisuje.mcp.json, a tool search ładuje tylko nazwy narzędzi s5.- Istnieje darmowy serwer HTTP bez logowania dla dokumentacji samego Claude Code, z
search_claude_code_docsiquery_docs_filesystem_claude_code_docss24. - Bez niego Claude odpowiedział "I couldn't find that setting... I won't guess one." Z nim Claude odpowiedział
"onFailure": "block", dodane w v2.1.295, podczas gdy nasza instalacja miała 2.1.294. - Serwer dokumentacji dla biblioteki, którą model już zna, niczego u nas nie zmienił. Podłączaj to, czego model mieć nie może.
Relacje z terenu. Wątek na Reddicie opisał, jak Claude ignoruje linię "MUST use agent" w CLAUDE.md s9. Cytujemy go jako relację, nie pomiar. Wpisy inżynieryjne Anthropic dotyczą tego samego podziału s6 s7.
Kod
Cztery pliki z uruchomienia plus dwa pomocnicze. Ścieżki pokazują, gdzie każdy z nich leży w twoim repozytorium.
1. Skill release, w .claude/skills/release/SKILL.md. Wpisz /release. disable-model-invocation: true zostawia jego uruchamianie tobie, o to chodzi przy czymś, co robi commit i tag.
SKILL.md
---
name: release
description: Cut a patch release of ledgerly (typecheck, tests, version bump, changelog line, commit, tag).
disable-model-invocation: true
---
Run these steps in order and stop at the first failure:
1. `pnpm typecheck` must exit 0.
2. `pnpm test` must pass.
3. Bump the patch version in `package.json` (e.g. 0.4.0 -> 0.4.1).
4. Add one line at the top of `CHANGELOG.md` (create it if missing): `## vX.Y.Z - YYYY-MM-DD` followed by a one-line summary of the commits since the last tag.
5. `git commit -am "release vX.Y.Z"`
6. `git tag vX.Y.Z`. Never push.
2. Hook chroniący dane, w .claude/settings.json. Zwraca decyzję deny z powodem, który Claude czyta. Wymaga jq w twoim PATH i mieści się inline, więc hook nie potrzebuje własnego pliku ze skryptem. Zmień wzorzec data/.*\.json$ i powód na swoją regułę.
settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -c 'if (.tool_input.file_path // \"\") | test(\"data/.*\\\\.json$\") then {hookSpecificOutput: {hookEventName: \"PreToolUse\", permissionDecision: \"deny\", permissionDecisionReason: \"Stored data is never edited by hand: write a migration in src/store/migrations.ts (README § Conventions).\"}} else empty end'"
}
]
}
]
}
}
3. Audytor konwencji, w .claude/agents/conventions-auditor.md. Narzędzia są ograniczone do tych tylko do odczytu, a model jest przypięty. Dopasuj listę konwencji do swojego README.
conventions-auditor.md
---
name: conventions-auditor
description: Use when asked to audit the code against the README conventions
model: sonnet
tools: Read, Grep, Glob
---
You audit ledgerly's source against README § Conventions:
- Money is always an integer number of cents. Never floats.
- Domain functions return new objects; they never mutate their input.
- Validation failures throw `ValidationError` (400), missing records `NotFoundError` (404), illegal state transitions `ConflictError` (409).
- A change of stored shape bumps `schemaVersion` and adds a migration in `src/store/migrations.ts`.
Read every file under `src/`. Return only the findings: one line per violation as `file:line - convention - what is wrong`, then borderline cases the same way, then one line with the count of files read. No file contents, no summary of clean files.
conventions-auditor.md on GitHub
4. Konfiguracja MCP, zapisana jako .mcp.json w głównym katalogu repozytorium. To właśnie zapisuje claude mcp add --transport http claude-docs --scope project https://code.claude.com/docs/mcp. Claude Code wyświetla serwer projektu jako oczekujący, dopóki nie zatwierdzisz go w następnej sesji.
mcp.json
{
"mcpServers": {
"claude-docs": {
"type": "http",
"url": "https://code.claude.com/docs/mcp"
}
}
}
Pomocnicze. ctx.sh uruchamia /context w sesji headless i wypisuje linie z tokenami, żebyś mógł porównać przed i po. mcp-probe.sh wysyła initialize i tools/list na url HTTP MCP, żeby sprawdzić, czy serwer odpowiada, zanim go dodasz.
ctx.sh
#!/usr/bin/env bash
# Usage: ctx.sh <step> -> runs "/context" through run-session.sh, writes context-<step>.txt (honours STRICT_MCP / EXTRA_ALLOW)
C="$(cd "$(dirname "$0")" && pwd)"
"$C/run-session.sh" "$C/out/context-$1.json" "/context" >/dev/null
python3 -c "import json,sys;d=json.load(open(sys.argv[1]));print(d['result'])" "$C/out/context-$1.json" > "$C/context-$1.txt"
grep -E '^\*\*Tokens|^\| (System prompt|System tools|MCP tools|Memory files|Skills|Custom agents|Messages) ' "$C/context-$1.txt"
mcp-probe.sh
#!/usr/bin/env bash
# Usage: mcp-probe.sh <url> [tools/call json params] -> initialize + notifications/initialized + tools/list (+ optional tools/call) over streamable HTTP
U="$1"; H=$(mktemp)
curl -s -D "$H" -m 30 -X POST "$U" -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}'
echo; SID=$(grep -i '^mcp-session-id:' "$H" | tr -d '\r' | cut -d' ' -f2); echo "session-id: ${SID:-none}"
HS=(-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream'); [ -n "$SID" ] && HS+=(-H "Mcp-Session-Id: $SID")
curl -s -m 30 -X POST "$U" "${HS[@]}" -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' >/dev/null
curl -s -m 30 -X POST "$U" "${HS[@]}" -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'; echo
[ -n "${2:-}" ] && { curl -s -m 90 -X POST "$U" "${HS[@]}" -d "{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":$2}"; echo; }
rm -f "$H"
Zrób to w poniedziałek
- Otwórz świeżą sesję i uruchom /context. Zapisz sumę. To twój punkt bazowy.
- Wybierz checklistę, którą wklejasz najczęściej. Przenieś ją do
.claude/skills/<name>/SKILL.md, dodajdisable-model-invocation: true, jeśli robi commit lub tag, i uruchom ją przez/<name>. - Weź jedną regułę, która nigdy nie może się złamać, i włóż ją do hooka PreToolUse, który zwraca
permissionDecision: "deny"albo kończy się kodem 2. Poproś Claude'a, żeby złamał regułę, i sprawdź, czy edycja zostaje odrzucona. - Jeśli twój hook tylko loguje, zmień go. Hook kończący się kodem 0 niczego nie blokuje.
- Przenieś jedno zadanie czytające wiele plików do
.claude/agents/<name>.mdz przypiętymmodel:. Nazywaj go w prompcie, dopóki Claude nie zacznie wybierać go sam. - Sprawdź jeden fakt, który Claude niedawno zgadł za ciebie. Jeśli jest nowszy niż model, dodaj serwer dokumentacji przez
claude mcp add --transport http <name> --scope project <url>i zrób commit.mcp.json. - Sprawdź wersję Claude Code, zanim zaczniesz polegać na ustawieniu.
onFailure: "block"wymaga 2.1.295. - Uruchom
git add -A && git statusi zrób commit nowych plików. Nasze uruchomienie pokazało dokładnie cztery.
Dalsze kroki
Karta wyboru. Jedna strona. To wiersze z dokumentacji s1 z naszymi czterema zadaniami wpisanymi obok. Bohaterem tego pakietu są cztery pliki powyżej plus ta karta.
| Rodzaj | Co robi | Użyj, gdy | Nasze zadanie | Nasz plik |
|---|---|---|---|---|
| CLAUDE.md | Trwały kontekst ładowany w każdej rozmowie | Konwencja, którą Claude myli dwa razy | Jedna linia, nie procedura | brak |
| Skill | Instrukcje i przepływy pracy, których Claude może użyć | Wklejasz ten sam playbook po raz trzeci | Checklista release'u | .claude/skills/release/SKILL.md |
| Hook | Skrypt uruchamiany zdarzeniami | Chcesz, żeby działo się za każdym razem, bez pytania | Nigdy nie edytuj zapisanych danych ręcznie | .claude/settings.json |
| Subagent | Odizolowany kontekst zwracający podsumowane wyniki | Zadanie poboczne zalewa rozmowę | Audyt konwencji | .claude/agents/conventions-auditor.md |
| MCP | Łączy z zewnętrznymi usługami | Wciąż kopiujesz dane z karty, której Claude nie widzi | Dokumentacja Claude Code | .mcp.json |
| Plugin | Łączy cztery w jedną instalowalną jednostkę | Drugie repozytorium potrzebuje tej samej konfiguracji | Nieużyty | brak |
Karta rozstrzyga o rodzaju, nie o jakości. Hook, który tylko loguje, subagent, którego nigdy nie nazywasz, i serwer dla dokumentacji, którą model zna, każdy stał w dobrym rodzaju i zawiódł. Wybierz rodzaj, a potem spraw, żeby plik odpowiadał.
Kiedy po to nie sięgać.
- Konwencja, którą Claude wciąż myli, zostaje w CLAUDE.md jako jedna linia, nie procedura.
- Zadanie, którym sterujesz tam i z powrotem, mając przed oczami swoją historię, zostaje w głównym wątku, nie w subagencie.
- Serwer dokumentacji dla biblioteki, którą model już zna, kosztuje kontekst i niczego nie zmienia.
pnpm testto CLI, które już masz. To nie jest zadanie dla MCP.
Pluginy. Plugin to pudełko, które wysyła te cztery razem. Sięgnij po niego, gdy inne repozytorium potrzebuje tej samej konfiguracji.
Skill plus MCP. Dokumentacja je łączy: serwer daje połączenie, a skill uczy Claude'a, jak je dobrze wykorzystać.
Skill z context: fork. Skill może działać w rozwidlonym subagencie, dzięki czemu jego czytanie zostaje poza twoim oknem. Przetestuj to na jednym zadaniu, zanim zaczniesz na tym polegać.
Typy hooków poza command. Command to ten, którego użyliśmy. Dokumentacja hooków wymienia inne typy handlerów, w tym hooki http i prompt. Obowiązuje ta sama zasada: decyduje to, co hook zwraca, a cichy hook niczego nie rozstrzyga.
MCP kontra CLI. Trwa otwarta debata o serwerach MCP zjadających kontekst, z jednym wpisem argumentującym za CLI na pierwszym miejscu s11 i drugim o koszcie kontekstu serwerów MCP s12. Nasza zasada: jeśli CLI już robi tę robotę, użyj go. MCP stosuj do tego, co go nie ma. Tool search ładuje teraz tylko nazwy, więc zmierz /context na swojej konfiguracji, zanim zdecydujesz s5.
Trzymaj skille krótkie i sprawdzone. Jeden krótki skill na zadanie, sprawdzony na prawdziwym uruchomieniu. Zobacz artykuł SkillsBench s13 oraz spojrzenie praktyka na to, dlaczego skille kosztują mało, dopóki nie są używane s14.
FAQ
Dlaczego mój hook się uruchomił, a edycja i tak przeszła? Hook decyduje tylko przez to, co zwraca jego polecenie. Exit 0 bez wyjścia nie jest decyzją s2. Zwróć JSON deny albo zakończ kodem 2.
Dlaczego Claude sam czyta pliki, skoro mam subagenta? W naszym uruchomieniu sam plik agenta delegował w 0 z 2 uruchomień. Nazwij go w prompcie, na przykład "use the conventions auditor subagent".
Czy hook pokrywa wszystko, co Claude może zrobić? Nie. Nasz dopasowuje Edit i Write. W późniejszym uruchomieniu Claude zaproponował wprowadzenie zmiany przez powłokę, więc pokryj powłokę albo trzymaj ją poza swoją allow listą.
Czy powinienem dodać serwer MCP dla każdej biblioteki, której używam? Nie. Jeśli model już zna bibliotekę, serwer niczego nie zmienia i kosztuje kontekst. Dodaj go dla danych nowszych niż model albo leżących poza twoim dyskiem i porównaj zainstalowaną wersję z tą, która dodała funkcję.
Czy mogę zrobić commit tego wszystkiego?
Tak. Skill, plik ustawień, plik agenta i .mcp.json leżą w repozytorium, więc każdy, kto je sklonuje, je dostaje. Zatwierdź serwer MCP projektu raz w następnej sesji.
AIDive