Звичайна година роботи в режимі 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: збіглося - відмова. Якщо ні, зі списком ask: збіглося - запитання в будь-якому режимі. Якщо ні, зі списком allow: збіглося - виконується без запитання. Якщо не збіглося нічого, вирішує режим: у Manual агент спитає, в acceptEdits правка пройде сама.Дія агентакоманда, правка, читанняЗбіглося з deny?Збіглося з ask?Збіглося з allow?Відмовадія не виконаєтьсяЗапитанняу будь-якому режиміБез запитаннявиконується одразуНічого не збіглося - вирішує режиму Manual агент спитає, в acceptEdits правка пройде саматактактакнініні
Вирішує перший збіг, якщо йти згори

Схему читають згори вниз, і кожна дія агента проходить її наново. Щойно правило збіглося, перевірка закінчується, а конкретніше правило пріоритету не отримує. Якщо в 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*) збіжиться.
Як правило для команди збігається з текстом. Правило Bash(git log *): частину git log звіряють літера в літеру, зірочка покриває --oneline main, тому правило пропускає тільки git log з будь-якими прапорцями. Правило Bash(git *): літера в літеру звіряють тільки git, зірочка покриває push origin main, тому правило пропускає будь-яку команду git, зокрема push.Bash(git log *)Bash(git *)тільки git log, з будь-якими прапорцямибудь-яка команда git, зокрема pushgit log--oneline maingitpush origin mainдо зірочки: літера в літерузамість зірочки: будь-який текст
Межу правила задає текст до зірочки

Блакитну частину звіряють із командою літера в літеру, помаранчеву зірочка закриває цілком, хоч би що там стояло. Тому ширину правила визначає те, що написано до зірочки. У першому правилі це git log, і нічого, крім перегляду історії, воно не пропустить. У другому тільки git, і за зірочкою може опинитися push, видалення гілки чи скидання змін. Коли читатимете чуже правило, починайте з цього запитання: що стоїть до зірочки?

ПравилоЗбігаєтьсяНе збігається
Bash(npm test)npm testnpm test --watch
Bash(npm run *)npm run build, npm run lint --fixnpm install
Bash(git log *)git log, git log --onelinegit 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 до збереження з тими самими запитаннями, що в розборі вище. Чи не ширше правило, ніж треба? Чи в той файл воно потрапить?

Режим задає загальний темп роботи, правила вирішують, де цей темп скидати. Разом вони дають те, чого не дає жоден режим сам по собі: швидку роботу там, де помилка дешева, і запитання або відмову там, де дорога.