Звичайна година роботи в режимі Manual. Агент читає проєкт, пропонує правку, просить запустити тести, знову правка, знову тести. На двадцятому запитанні ви тиснете "Yes", майже не дивлячись. І десь серед цих запитань проскакує правка в теці legacy/, яку в проєкті давно ніхто не чіпає і чіпати не повинен.
Ця стаття про те, як залишити запитання лише там, де рішення справді за вами. Для цього в Claude Code є правила доступу: три списки у файлі налаштувань, які кажуть, що агентові можна робити одразу, про що питати і чого не робити взагалі. Розберемо, навіщо вони потрібні, як їх читати і як навести лад у файлі з правилами, який у вас, найпевніше, уже є.
Одне налаштування на все
Режим доступу - один регулятор на всю сесію. Повернути його можна в будь-який бік, але тільки для всього одразу.
У суворому Manual агент питає перед кожною правкою і майже перед кожною командою. Запитання захищає лише доти, доки ви його читаєте. Коли запитань багато, їх перестають читати, і захист лишається тільки на папері.
У вільному acceptEdits правки проходять без запитання, а заодно й файлові команди на кшталт rm і mv. Для вашого власного коду це зручно. Але режим не розрізняє тек: правка в legacy/ пройде так само тихо, як будь-де в проєкті.
Можна написати в CLAUDE.md "не чіпай legacy/". Агент намагатиметься, це правда. Але це прохання до моделі, а не заборона. Правила доступу перевіряє сам Claude Code перед тим, як дія виконається, і текст у CLAUDE.md чи в запиті на них не впливає.
Виходить, що різні дії мають різну ціну помилки, а режим реагує на всі однаково. Запуск тестів варто пропускати мовчки. Правку чужого модуля хочеться бачити. У заморожену теку агентові заходити не треба зовсім. Три різні відповіді на три різні дії. А режим так просто не вміє.
Три списки
Саме ці три відповіді й дають правила:
allow- виконувати без запитання. Прибирає шум: часте й безпечне більше не відволікає;ask- завжди питати. Тримає запитання там, де рішення за вами, причому в будь-якому режимі, навіть найвільнішому;deny- не виконувати. Агент отримує відмову, а вам запитання навіть не показують. Теж у будь-якому режимі.
Правила записують у файл налаштувань, наприклад у .claude/settings.json проєкту. Для проєкту зі вступу це могло б мати такий вигляд:
{
"permissions": {
"allow": [
"Bash(npm test)"
],
"ask": [
"Edit(src/partner/**)"
],
"deny": [
"Edit(legacy/**)",
"Bash(git push *)"
]
}
}Тут src/partner/ - чужий модуль, який ви правите обережно, legacy/ - заморожена частина проєкту, а npm test - перевірка, яку агент запускає по десять разів за завдання. Дві зірочки означають "усе всередині теки на будь-якій глибині".
Тепер подивимося, що змінюється в сесії. Ті самі п'ять дій у трьох варіантах:
| Дія | Manual без правил | acceptEdits без правил | acceptEdits із правилами |
|---|---|---|---|
Правка в решті src/ | запитання | без запитання | без запитання |
Правка в src/partner/ | запитання | без запитання | запитання |
Правка в legacy/ | запитання | без запитання | відмова |
npm test | запитання | запитання | без запитання |
git push | запитання | запитання | відмова |
Дивіться на останній стовпець. Сесія йде у швидкому режимі: свій код правиться без зупинок, тести запускаються самі. Запитання лишилося одне, і воно про місце, де ви справді хочете подивитися на правку. А дві неприпустимі дії більше не залежать від того, наскільки ви втомилися під вечір.
Ось заради цього правила й потрібні. Мета не в тому, щоб додати агентові дозволів, а в тому, щоб розкласти їх за ціною помилки. Тоді можна працювати у вільнішому режимі й не втрачати контролю над важливими місцями.
Одна дія може підпасти одразу під кілька правил, тому Claude Code перевіряє списки в суворому порядку:
Схему читають згори вниз, і кожна дія агента проходить її наново. Щойно правило збіглося, перевірка закінчується, а конкретніше правило пріоритету не отримує. Якщо в deny стоїть Bash(git push *), жодне правило в allow push уже не дозволить. Випадково перебити заборону не вийде, і це, як на мене, добре.
Нижній прямокутник варто запам'ятати окремо. Якщо дія не потрапила в жоден список, це не означає "дозволено". Вирішує режим: у Manual агент спитає, в acceptEdits правка пройде сама. тобто правила нічого не скасовують, вони уточнюють режим у тих місцях, які ви назвали.
Як читається правило
Правило, записане майже правильно, гірше за відсутнє. Без правила ви пам'ятаєте, що межі немає. З неточним ви впевнені, що межа є, а вона тримає не те, що ви думали.
Усі правила мають однаковий формат: ім'я інструмента і в дужках уточнення, що саме. Без дужок правило стосується всіх викликів інструмента, тож Bash у deny забирає в агента термінал цілком.
Файли
Для файлів потрібні два інструменти: Edit для будь-якої правки і Read для читання. Шлях записують шаблоном, як у .gitignore: одна зірочка - будь-яке ім'я всередині однієї теки, дві - будь-яка глибина вкладеності. Шлях без слеша на початку відлічується від теки, в якій запущено сесію, тому запускайте Claude Code з кореня проєкту.
Часта помилка - написати Write(legacy/**). Інструмент із такою назвою в агента є, але правила для файлів Claude Code звіряє тільки з Edit і Read. Правило з Write він прийме, попередить про це під час запуску і далі перевіряти не буде. А от Edit якраз покриває всі вбудовані способи змінювати файли, включно зі створенням нового.
І один зручний збіг: заборона на читання, наприклад Read(./.env) у deny, заодно забороняє агентові й правку цього файлу.
Команди
Правило Bash(...) порівнює текст команди. Зірочка замінює будь-який текст, включно з пробілами. А чи збіжиться правило, вирішують три дрібниці, які легко проґавити:
- без зірочки правило збігається з однією командою, літера в літеру;
- зірочку ставлять після підкоманди.
Bash(git log *)пропускає тількиgit logз будь-якими прапорцями, аBash(git *)пропускає будь-яку команду git, зокрема push; - пробіл перед зірочкою теж частина правила:
Bash(ls *)не збіжиться зlsof, аBash(ls*)збіжиться.
Блакитну частину звіряють із командою літера в літеру, помаранчеву зірочка закриває цілком, хоч би що там стояло. Тому ширину правила визначає те, що написано до зірочки. У першому правилі це git log, і нічого, крім перегляду історії, воно не пропустить. У другому тільки git, і за зірочкою може опинитися push, видалення гілки чи скидання змін. Коли читатимете чуже правило, починайте з цього запитання: що стоїть до зірочки?
| Правило | Збігається | Не збігається |
|---|---|---|
Bash(npm test) | npm test | npm test --watch |
Bash(npm run *) | npm run build, npm run lint --fix | npm install |
Bash(git log *) | git log, git log --oneline | git push |
Bash(git *) | будь-яка команда git, включно з git push | - |
Edit(src/partner/**) | правка будь-якого файлу всередині src/partner/ | правка src/app.js |
Read(./.env) | читання .env у корені проєкту | читання .env.example |
Складену команду на кшталт a && b Claude Code ділить на частини. allow пропустить її, тільки якщо кожна частина підходить під свій дозвіл. ask і deny спрацюють, якщо підходить хоча б одна. Тож дозвіл на npm test не потягне за собою npm test && git push.
Правила бувають і для інших інструментів, напр. mcp__github__* для всіх інструментів одного MCP-сервера або WebFetch(domain:example.com) для завантаження сторінок з одного сайту. Логіка в них та сама, окремо розбирати їх тут не будемо.
У якому файлі тримати правила
Правило не в тому файлі ламає роботу тихо. Поклали заборону в особистий файл - у колег її немає, і їхній агент заходить у legacy/ як ні в чому не бувало. Поклали зручний для себе дозвіл у спільний файл - він поїхав до всіх, хто завантажить проєкт.
| Файл | Чиї правила | Потрапляє в Git |
|---|---|---|
~/.claude/settings.json | ваші, в усіх проєктах на цьому комп'ютері | ні, лежить у домашній теці |
.claude/settings.json | спільні для всіх, хто працює з проєктом | так, його комітять |
.claude/settings.local.json | ваші, тільки в цьому проєкті | ні |
Крім цих трьох, організація може роздати свій файл налаштувань, і його правила перекрити не можна. У навчальних проєктах такого зазвичай немає.
Списки з різних файлів не сперечаються, а об'єднуються: allow з вашого файлу додасться до allow спільного. Порядок перевірки при цьому один для всіх, і deny з будь-якого файлу сильніший за allow з будь-якого іншого. Звідси простий розподіл. Межі проєкту, як-от legacy/, .env чи push, кладіть у спільний файл, щоб вони їхали разом із репозиторієм. Зручності для власної манери роботи - в локальний.
Спільний файл має одну захисну особливість. Його allow починають працювати лише після того, як ви підтвердили довіру до теки: під час першого запуску в проєкті Claude Code питає про це і показує, що файл збирається дозволити. ask і deny діють одразу, бо вони тільки обмежують. Чужий репозиторій із дозволом на весь термінал нічого не отримає, доки ви не погодилися.
Перевірити, що діє насправді, можна двома командами. /permissions показує всі правила і файл, з якого прийшло кожне, там само їх можна додати або видалити. /status у рядку Setting sources перелічує завантажені файли. До речі, правки у файлах Claude Code підхоплює без перезапуску. Файл налаштувань - суворий JSON: коментар через // або кома після останнього елемента його зламають, і Claude Code попередить про це під час старту.
Коли агент питає про команду і ви вибираєте "Yes, and don't ask again", Claude Code записує правило allow у .claude/settings.local.json. Якщо файлу ще не було, він його створить і подбає, щоб той не потрапив у коміти. З правками файлів інакше: така згода діє до кінця сесії і у файл не записується.
Розбираємо справжній settings.local.json
Цей файл у вас, найпевніше, уже є, і писали його не ви. Кожне "більше не питати" додало в нього рядок, і за пару тижнів він дозволяє агентові більше, ніж ви думаєте. Ось справжній блок, що накопичився за кілька тижнів роботи з Python-проєктом:
{
"permissions": {
"allow": [
"Bash(python3 *)",
"Bash(PYTHONIOENCODING=utf-8 python3 *)",
"Bash(git status *)",
"Bash(git log *)",
"Bash(git diff *)",
"Bash(git show *)",
"Bash(git branch *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git fetch *)"
]
}
}Перш ніж читати розбір, спробуйте самі. Які рядки ви б залишили, які звузили, а які прибрали?
Bash(python3 *)
Найширший рядок у файлі. Він дозволяє запуск будь-якого скрипта і будь-якого коду через python3 -c. А Python уміє все: видаляти файли, ходити в мережу, запускати інші програми. Фактично це дозвіл виконувати що завгодно без запитання. Правило варто звузити до скрипта, заради якого воно з'явилося, наприклад до Bash(python3 scripts/build.py *).
Bash(PYTHONIOENCODING=utf-8 python3 *)
Та сама команда зі змінною середовища попереду. Правило allow пропускає такий запис, тільки якщо змінна є в короткому списку заздалегідь відомих безпечних. Якщо її там немає, Bash(python3 *) цю команду не покриє, агент спитає окремо, і кнопка збереже ще одне правило. Так цей рядок, найпевніше, і з'явився. Звужується так само, до конкретного скрипта.
git status, git log, git diff, git show
У звичайній формі ці команди тільки читають, а команди, що читають, Claude Code виконує без запитання і без жодних правил. Рядки не шкодять, але майже нічого не додають. Приберіть їх, і файл стане коротшим. І зрозумілішим.
Bash(git branch *)
Здається безневинним: подивитися список гілок. Але правило порівнює текст, а git branch -D feature теж починається з git branch. Видалення гілки пройде без запитання. Рядок краще прибрати. Якщо запитання про список гілок почне заважати, дозвольте точну команду Bash(git branch) без зірочки.
git add і git fetch
Перша додає зміни до майбутнього коміту, друга завантажує свіжу історію із сервера і робочих файлів не чіпає. Обидві легко скасувати, тож хай лишаються.
Bash(git commit *)
Агент комітить без запитання, тобто раніше, ніж ви прочитали diff. Якраз тут запитання й потрібне: коміт - це момент, коли ви погоджуєтеся зі зміною. Рядок варто перенести в ask. Просто видалити його замало. У Manual агент і так спитає, але ask тримає запитання в будь-якому режимі, включно з найвільнішим.
Чого у файлі немає
git push. Це добре, тільки тримається на тому, що ніхто жодного разу не натиснув "більше не питати" на пуші. Надійніше записати заборону явно.
Ось що виходить після розбору:
{
"permissions": {
"allow": [
"Bash(python3 scripts/build.py *)",
"Bash(PYTHONIOENCODING=utf-8 python3 scripts/build.py *)",
"Bash(git add *)",
"Bash(git fetch *)"
],
"ask": [
"Bash(git commit *)"
],
"deny": [
"Bash(git push *)"
]
}
}З десяти рядків залишилося шість, і кожен робить те, що задумано. Запитань у роботі майже не додалося: збірка йде сама, а питає агент в одному місці, перед комітом. Заборону на push, якщо вона стосується всієї команди, краще перенести в спільний .claude/settings.json, за правилом із попереднього розділу.
Правила на зразок Bash(python3 *), Bash(node *), Bash(npm *) або Bash(npx *) скидаються на дозвіл на один інструмент. Насправді це дозвіл на будь-який код, який цей інструмент здатен запустити. Дозволяйте конкретний скрипт або конкретну команду, а не програму цілком.
Чого правило не ловить
Агент отримав відмову. Для нього це звичайна відповідь інструмента, і робота триває. Іноді він шукає інший спосіб зробити те саме, і тут важливо розуміти, що саме перевіряє правило.
Правило Bash(...) порівнює текст команди, а не програму, яка запуститься. Тому заборона перекриває звичайну форму команди, але не будь-яку:
Bash(git push *)уdenyне зупинитьgit -C . push. Команда та сама, запис інший;Bash(rm *)не зупинить/bin/rmабоbash -c 'rm ...';- заборона на
Read(...)не завадить скрипту на Python, який сам відкриє файл.
Відмовлятися від правил через це не треба. Агент майже завжди пише команду у звичайній формі, і deny ловить саме її. Але правило - не стіна (і документація Claude Code каже про це прямо). Щоб агентові не доводилося шукати обхід, поясніть заборону словами в CLAUDE.md: "legacy/ заморожена; якщо завдання вимагає правки там, зупинись і спитай". Правило тримає, CLAUDE.md пояснює, і разом вони працюють краще, ніж окремо.
Якщо межа має триматися надійно, для цього є інші механізми: hook, який перевіряє кожну команду перед запуском, і sandbox, який обмежує доступ на рівні системи. Але це вже окрема розмова.
Правила на кожен день
Якщо звести все до кількох звичок, виходить приблизно так:
- у
deny- те, що в цьому проєкті неприпустимо за будь-яких обставин: заморожена тека,.env, push; - в
ask- тільки спірне, де рішення за вами.askна все поверне ту саму втому від запитань; - в
allow- вузько і те, що повторюється: конкретна перевірка, конкретний скрипт; - межі проєкту - у спільний
.claude/settings.json, особисті зручності - в.claude/settings.local.json; - час від часу відкривайте
/permissionsі прибирайте те, що накопичилося кнопкою "більше не питати"; - після нового правила перевірте його: попросіть агента зробити заборонене і переконайтеся, що прийшла відмова.
Опишіть агентові словами зони проєкту: що правиться вільно, що із запитанням, куди заходити не можна. Попросіть запропонувати блок permissions і прочитайте JSON до збереження з тими самими запитаннями, що в розборі вище. Чи не ширше правило, ніж треба? Чи в той файл воно потрапить?
Режим задає загальний темп роботи, правила вирішують, де цей темп скидати. Разом вони дають те, чого не дає жоден режим сам по собі: швидку роботу там, де помилка дешева, і запитання або відмову там, де дорога.