failproofai 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  13. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +2 -2
  19. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  22. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  24. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  25. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  26. package/.next/standalone/.next/server/app/index.html +1 -1
  27. package/.next/standalone/.next/server/app/index.rsc +15 -15
  28. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  30. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  31. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  32. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  33. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  34. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  37. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  39. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  40. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  43. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  44. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +1 -1
  50. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
  51. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
  52. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
  53. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0v1egkj._.js → [root-of-the-server]__0a~g15g._.js} +2 -2
  54. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
  55. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
  56. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okos0k._.js +2 -2
  57. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0lty_fo._.js → [root-of-the-server]__0qn95h3._.js} +2 -2
  58. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +2 -2
  59. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
  60. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
  61. package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
  62. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
  64. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  65. package/.next/standalone/.next/server/pages/404.html +2 -2
  66. package/.next/standalone/.next/server/pages/500.html +1 -1
  67. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
  69. package/.next/standalone/.next/static/chunks/{0de3q2juhg_dr.js → 09ikntpt2-o9b.js} +1 -1
  70. package/.next/standalone/.next/static/chunks/{11zlh73ggln0w.js → 0_yayar~bpphd.js} +1 -1
  71. package/.next/standalone/.next/static/chunks/{0b2_069x5qnxg.js → 0em7tspi4kylh.js} +2 -2
  72. package/.next/standalone/.next/static/chunks/{0y-bi_mp2rv4l.js → 0lgbwkfqmnsmc.js} +1 -1
  73. package/.next/standalone/.next/static/chunks/{0uxpbrcv44lga.js → 0sme4lkv.tgn-.js} +1 -1
  74. package/.next/standalone/.next/static/chunks/{0tw_xfxb1tto..js → 0yumumfzx_f27.js} +1 -1
  75. package/.next/standalone/.next/static/chunks/{0p5sfob-upg0g.js → 13juklu.vksks.js} +1 -1
  76. package/.next/standalone/.next/static/chunks/17manv47o-~wp.js +1 -0
  77. package/.next/standalone/CHANGELOG.md +9 -0
  78. package/.next/standalone/README.md +2 -2
  79. package/.next/standalone/dist/cli.mjs +52 -11
  80. package/.next/standalone/docs/ar/architecture.mdx +2 -2
  81. package/.next/standalone/docs/ar/configuration.mdx +1 -1
  82. package/.next/standalone/docs/ar/custom-policies.mdx +2 -5
  83. package/.next/standalone/docs/architecture.mdx +2 -2
  84. package/.next/standalone/docs/configuration.mdx +1 -1
  85. package/.next/standalone/docs/custom-policies.mdx +2 -6
  86. package/.next/standalone/docs/de/architecture.mdx +2 -2
  87. package/.next/standalone/docs/de/configuration.mdx +1 -1
  88. package/.next/standalone/docs/de/custom-policies.mdx +2 -5
  89. package/.next/standalone/docs/es/architecture.mdx +2 -2
  90. package/.next/standalone/docs/es/configuration.mdx +1 -1
  91. package/.next/standalone/docs/es/custom-policies.mdx +2 -5
  92. package/.next/standalone/docs/fr/architecture.mdx +2 -2
  93. package/.next/standalone/docs/fr/configuration.mdx +1 -1
  94. package/.next/standalone/docs/fr/custom-policies.mdx +2 -5
  95. package/.next/standalone/docs/he/architecture.mdx +2 -2
  96. package/.next/standalone/docs/he/configuration.mdx +1 -1
  97. package/.next/standalone/docs/he/custom-policies.mdx +2 -5
  98. package/.next/standalone/docs/hi/architecture.mdx +2 -2
  99. package/.next/standalone/docs/hi/configuration.mdx +1 -1
  100. package/.next/standalone/docs/hi/custom-policies.mdx +2 -5
  101. package/.next/standalone/docs/i18n/README.ar.md +2 -2
  102. package/.next/standalone/docs/i18n/README.de.md +2 -2
  103. package/.next/standalone/docs/i18n/README.es.md +2 -2
  104. package/.next/standalone/docs/i18n/README.fr.md +2 -2
  105. package/.next/standalone/docs/i18n/README.he.md +2 -2
  106. package/.next/standalone/docs/i18n/README.hi.md +2 -2
  107. package/.next/standalone/docs/i18n/README.it.md +2 -2
  108. package/.next/standalone/docs/i18n/README.ja.md +2 -2
  109. package/.next/standalone/docs/i18n/README.ko.md +2 -2
  110. package/.next/standalone/docs/i18n/README.pt-br.md +2 -2
  111. package/.next/standalone/docs/i18n/README.ru.md +2 -2
  112. package/.next/standalone/docs/i18n/README.tr.md +2 -2
  113. package/.next/standalone/docs/i18n/README.vi.md +2 -2
  114. package/.next/standalone/docs/i18n/README.zh.md +2 -2
  115. package/.next/standalone/docs/it/architecture.mdx +2 -2
  116. package/.next/standalone/docs/it/configuration.mdx +1 -1
  117. package/.next/standalone/docs/it/custom-policies.mdx +2 -5
  118. package/.next/standalone/docs/ja/architecture.mdx +2 -2
  119. package/.next/standalone/docs/ja/configuration.mdx +1 -1
  120. package/.next/standalone/docs/ja/custom-policies.mdx +2 -5
  121. package/.next/standalone/docs/ko/architecture.mdx +2 -2
  122. package/.next/standalone/docs/ko/configuration.mdx +1 -1
  123. package/.next/standalone/docs/ko/custom-policies.mdx +2 -5
  124. package/.next/standalone/docs/pt-br/architecture.mdx +2 -2
  125. package/.next/standalone/docs/pt-br/configuration.mdx +1 -1
  126. package/.next/standalone/docs/pt-br/custom-policies.mdx +2 -5
  127. package/.next/standalone/docs/ru/architecture.mdx +2 -2
  128. package/.next/standalone/docs/ru/configuration.mdx +1 -1
  129. package/.next/standalone/docs/ru/custom-policies.mdx +2 -5
  130. package/.next/standalone/docs/tr/architecture.mdx +2 -2
  131. package/.next/standalone/docs/tr/configuration.mdx +1 -1
  132. package/.next/standalone/docs/tr/custom-policies.mdx +2 -5
  133. package/.next/standalone/docs/vi/architecture.mdx +2 -2
  134. package/.next/standalone/docs/vi/configuration.mdx +1 -1
  135. package/.next/standalone/docs/vi/custom-policies.mdx +2 -5
  136. package/.next/standalone/docs/zh/architecture.mdx +2 -2
  137. package/.next/standalone/docs/zh/configuration.mdx +1 -1
  138. package/.next/standalone/docs/zh/custom-policies.mdx +2 -5
  139. package/.next/standalone/package.json +1 -1
  140. package/.next/standalone/server.js +1 -1
  141. package/.next/standalone/src/hooks/builtin-policies.ts +40 -8
  142. package/.next/standalone/src/hooks/policy-evaluator.ts +15 -1
  143. package/README.md +2 -2
  144. package/dist/cli.mjs +52 -11
  145. package/package.json +1 -1
  146. package/src/hooks/builtin-policies.ts +40 -8
  147. package/src/hooks/policy-evaluator.ts +15 -1
  148. package/.next/standalone/.next/static/chunks/0xjz3w.yw5tza.js +0 -1
  149. /package/.next/standalone/.next/static/{LayjxQulxIcNH19Lqgjbf → hYQM6iCWnF1W5XDpsIRhV}/_buildManifest.js +0 -0
  150. /package/.next/standalone/.next/static/{LayjxQulxIcNH19Lqgjbf → hYQM6iCWnF1W5XDpsIRhV}/_clientMiddlewareManifest.js +0 -0
  151. /package/.next/standalone/.next/static/{LayjxQulxIcNH19Lqgjbf → hYQM6iCWnF1W5XDpsIRhV}/_ssgManifest.js +0 -0
@@ -39,7 +39,7 @@ failproofai policies --install --custom ./my-policies.js
39
39
 
40
40
  ## Duas formas de carregar políticas personalizadas
41
41
 
42
- ### Opção 1: Baseada em convenção (recomendada, v0.0.2-beta.7+)
42
+ ### Opção 1: Baseada em convenção (recomendada)
43
43
 
44
44
  Coloque arquivos `*policies.{js,mjs,ts}` dentro de `.failproofai/policies/` e eles serão carregados automaticamente — sem flags ou alterações de configuração. Funciona como git hooks: coloque o arquivo e pronto.
45
45
 
@@ -125,11 +125,8 @@ customPolicies.add({
125
125
  Você pode adicionar orientações extras a qualquer mensagem `deny` ou `instruct` incluindo um campo `hint` em `policyParams` — sem necessidade de alteração de código. Isso funciona para políticas personalizadas (`custom/`), de convenção do projeto (`.failproofai-project/`) e de convenção do usuário (`.failproofai-user/`) também. Veja [Configuração → hint](/pt-br/configuration#hint-cross-cutting) para detalhes.
126
126
  </Tip>
127
127
 
128
- ### Mensagens informativas de allow (beta)
128
+ ### Mensagens informativas de allow
129
129
 
130
- <Note>
131
- `allow(message)` é um recurso beta disponível desde a v0.0.2-beta.3. A API pode mudar em versões futuras. Versões anteriores suportam apenas `allow()` sem argumentos.
132
- </Note>
133
130
 
134
131
  `allow(message)` permite a operação **e** envia uma mensagem informativa de volta para Claude. A mensagem é entregue como `additionalContext` na resposta stdout do handler de hook — o mesmo mecanismo usado por `instruct`, mas semanticamente diferente: é uma atualização de status, não um aviso.
135
132
 
@@ -104,9 +104,9 @@ failproofai состоит из двух независимых подсисте
104
104
  - Код выхода: `0`
105
105
  - Пустой stdout
106
106
 
107
- **Разрешить с сообщением (beta):**
107
+ **Разрешить с сообщением:**
108
108
 
109
- Начиная с версии v0.0.2-beta.3, `allow(message)` позволяет политике отправить контекстную информацию в Claude, даже если операция разрешена. Обработчик хука записывает в **stdout** следующий JSON (не в файл конфигурации — это ответ обработчика на Claude Code, как и ответы deny и instruct выше):
109
+ `allow(message)` позволяет политике отправить контекстную информацию в Claude, даже если операция разрешена. Обработчик хука записывает в **stdout** следующий JSON (не в файл конфигурации — это ответ обработчика на Claude Code, как и ответы deny и instruct выше):
110
110
 
111
111
  ```json
112
112
  // Written to stdout by the hook handler process
@@ -154,7 +154,7 @@ resolved: { allowPatterns: ["sudo systemctl status"] } ← переходит
154
154
 
155
155
  Файл загружается заново при каждом события hook — кеширования нет. Детали разработки см. в разделе [Custom Policies](/ru/custom-policies).
156
156
 
157
- ### Политики на основе конвенций (v0.0.2-beta.7+)
157
+ ### Политики на основе конвенций
158
158
 
159
159
  В дополнение к явному `customPoliciesPath`, failproofai автоматически обнаруживает и загружает файлы политик из директорий `.failproofai/policies/`:
160
160
 
@@ -39,7 +39,7 @@ failproofai policies --install --custom ./my-policies.js
39
39
 
40
40
  ## Два способа загрузки пользовательских политик
41
41
 
42
- ### Вариант 1: На основе соглашений (рекомендуется, v0.0.2-beta.7+)
42
+ ### Вариант 1: На основе соглашений (рекомендуется)
43
43
 
44
44
  Поместите файлы `*policies.{js,mjs,ts}` в `.failproofai/policies/` и они будут загружены автоматически — никаких флагов или изменений конфигурации не требуется. Это работает как git hooks: поместите файл, и он просто работает.
45
45
 
@@ -125,11 +125,8 @@ customPolicies.add({
125
125
  Вы можете добавить дополнительные рекомендации к любому сообщению `deny` или `instruct`, добавив поле `hint` в `policyParams` — без изменения кода. Это работает для пользовательских (`custom/`), проектных (`failproofai-project/`) и пользовательских (`failproofai-user/`) политик. См. [Конфигурация → hint](/ru/configuration#hint-cross-cutting) для подробностей.
126
126
  </Tip>
127
127
 
128
- ### Информационные сообщения allow (бета)
128
+ ### Информационные сообщения allow
129
129
 
130
- <Note>
131
- `allow(message)` — это функция бета, доступная с v0.0.2-beta.3. API может измениться в будущих версиях. Более ранние версии поддерживают только `allow()` без аргументов.
132
- </Note>
133
130
 
134
131
  `allow(message)` разрешает операцию **и** отправляет информационное сообщение обратно Claude. Сообщение доставляется как `additionalContext` в ответе stdout обработчика перехватчика — тот же механизм, используемый `instruct`, но семантически отличается: это обновление статуса, а не предупреждение.
135
132
 
@@ -103,9 +103,9 @@ Handler 1 MB'lık stdin sınırını zorunlu kılar. Bu limiti aşan payload'lar
103
103
  - Çıkış kodu: `0`
104
104
  - Boş stdout
105
105
 
106
- **İzinle mesaj (beta):**
106
+ **İzinle mesaj:**
107
107
 
108
- v0.0.2-beta.3'ten itibaren `allow(message)`, bir policy işleme izin verildiğinde bile Claude'a bilgilendirici bağlam gönderebilir. Hook handler aşağıdaki JSON'u **stdout'a** yazar (bir config dosyasına değil — bu handler'ın Claude Code'a yanıtıdır, reddet ve talimat yanıtları gibi):
108
+ `allow(message)`, bir policy işleme izin verildiğinde bile Claude'a bilgilendirici bağlam gönderebilir. Hook handler aşağıdaki JSON'u **stdout'a** yazar (bir config dosyasına değil — bu handler'ın Claude Code'a yanıtıdır, reddet ve talimat yanıtları gibi):
109
109
 
110
110
  ```json
111
111
  // Hook handler işlemi tarafından stdout'a yazılır
@@ -154,7 +154,7 @@ Tür: `string` (mutlak yol)
154
154
 
155
155
  Dosya her hook olayında yeniden yüklenir - önbelleğe alma yoktur. Yazma ayrıntıları için [Özel Politikalar](/tr/custom-policies) bölümüne bakın.
156
156
 
157
- ### Kural tabanlı politikalar (v0.0.2-beta.7+)
157
+ ### Kural tabanlı politikalar
158
158
 
159
159
  Açık `customPoliciesPath` öğesine ek olarak, failproofai `.failproofai/policies/` dizinlerinden policy dosyalarını otomatik olarak keşfeder ve yükler:
160
160
 
@@ -39,7 +39,7 @@ failproofai policies --install --custom ./my-policies.js
39
39
 
40
40
  ## Özel politikaları yüklemenin iki yolu
41
41
 
42
- ### Seçenek 1: Kural tabanlı (önerilen, v0.0.2-beta.7+)
42
+ ### Seçenek 1: Kural tabanlı (önerilen)
43
43
 
44
44
  `.failproofai/policies/` dizinine `*policies.{js,mjs,ts}` dosyalarını bırakın ve otomatik olarak yüklenirler — hiçbir bayrak veya yapılandırma değişikliğine gerek yoktur. Bu git hook'ları gibi çalışır: bir dosya bırakın ve çalışır.
45
45
 
@@ -125,11 +125,8 @@ customPolicies.add({
125
125
  `policyParams` içinde `hint` alanı ekleyerek herhangi bir `deny` veya `instruct` iletisine ekstra rehberlik ekleyebilirsiniz — kod değişikliğine gerek yoktur. Bu özel (`custom/`), proje kural (`.failproofai-project/`) ve kullanıcı kural (`.failproofai-user/`) politikaları için de çalışır. Ayrıntılar için [Yapılandırma → hint](/tr/configuration#hint-cross-cutting) bölümüne bakın.
126
126
  </Tip>
127
127
 
128
- ### Bilgilendirici izin iletileri (beta)
128
+ ### Bilgilendirici izin iletileri
129
129
 
130
- <Note>
131
- `allow(message)` v0.0.2-beta.3'ten beri mevcut olan bir beta özelliğidir. API gelecek sürümlerde değişebilir. Önceki sürümler yalnızca `allow()` öğesini argümanlar olmadan destekler.
132
- </Note>
133
130
 
134
131
  `allow(message)` işleme izin verir **ve** Claude'a bilgilendirici bir ileti gönderir. İleti, hook işleyicisinin stdout yanıtında `additionalContext` olarak iletilir — `instruct` tarafından kullanılan mekanizmayla aynı, ancak anlamsal olarak farklı: bir uyarı değil, bir durum güncellemesidir.
135
132
 
@@ -103,9 +103,9 @@ Handler thực thi giới hạn 1 MB cho stdin. Các payload vượt quá giới
103
103
  - Mã thoát: `0`
104
104
  - Stdout trống
105
105
 
106
- **Allow with message (beta):**
106
+ **Allow with message:**
107
107
 
108
- Kể từ v0.0.2-beta.3, `allow(message)` cho phép một chính sách gửi context thông tin trở lại Claude ngay cả khi thao tác được cho phép. Hook handler ghi JSON sau đây vào **stdout** (không phải một tệp cấu hình — đây là phản hồi của handler process đối với Claude Code, giống như các phản hồi deny và instruct ở trên):
108
+ `allow(message)` cho phép một chính sách gửi context thông tin trở lại Claude ngay cả khi thao tác được cho phép. Hook handler ghi JSON sau đây vào **stdout** (không phải một tệp cấu hình — đây là phản hồi của handler process đối với Claude Code, giống như các phản hồi deny và instruct ở trên):
109
109
 
110
110
  ```json
111
111
  // Được ghi vào stdout bởi hook handler process
@@ -153,7 +153,7 @@ Loại: `string` (đường dẫn tuyệt đối)
153
153
 
154
154
  Tệp được tải mới trên mỗi sự kiện hook - không có lưu cache. Xem [Chính sách tùy chỉnh](/vi/custom-policies) để biết chi tiết tác giả.
155
155
 
156
- ### Chính sách dựa trên quy ước (v0.0.2-beta.7+)
156
+ ### Chính sách dựa trên quy ước
157
157
 
158
158
  Ngoài `customPoliciesPath` rõ ràng, failproofai tự động phát hiện và tải các tệp chính sách từ thư mục `.failproofai/policies/`:
159
159
 
@@ -39,7 +39,7 @@ failproofai policies --install --custom ./my-policies.js
39
39
 
40
40
  ## Hai cách để tải chính sách tùy chỉnh
41
41
 
42
- ### Lựa chọn 1: Dựa trên quy ước (được khuyến nghị, v0.0.2-beta.7+)
42
+ ### Lựa chọn 1: Dựa trên quy ước (được khuyến nghị)
43
43
 
44
44
  Thả các tệp `*policies.{js,mjs,ts}` vào `.failproofai/policies/` và chúng sẽ được tải tự động — không cần cờ hoặc thay đổi cấu hình. Cách này hoạt động giống như git hooks: thả một tệp, nó sẽ hoạt động.
45
45
 
@@ -125,11 +125,8 @@ customPolicies.add({
125
125
  Bạn có thể nối thêm hướng dẫn bổ sung vào bất kỳ thông báo `deny` hoặc `instruct` nào bằng cách thêm trường `hint` trong `policyParams` — không cần thay đổi mã. Điều này hoạt động cho các chính sách tùy chỉnh (`custom/`), quy ước dự án (`.failproofai-project/`), và quy ước người dùng (`.failproofai-user/`) cũng vậy. Xem [Cấu hình → hint](/vi/configuration#hint-cross-cutting) để biết chi tiết.
126
126
  </Tip>
127
127
 
128
- ### Thông báo allow thông tin (beta)
128
+ ### Thông báo allow thông tin
129
129
 
130
- <Note>
131
- `allow(message)` là tính năng beta có sẵn từ v0.0.2-beta.3. API có thể thay đổi trong các phiên bản tương lai. Các phiên bản trước chỉ hỗ trợ `allow()` mà không có đối số.
132
- </Note>
133
130
 
134
131
  `allow(message)` cho phép hoạt động **và** gửi một thông báo thông tin quay lại cho Claude. Thông báo được gửi dưới dạng `additionalContext` trong phản hồi stdout của trình xử lý hook — cơ chế tương tự được sử dụng bởi `instruct`, nhưng về mặt ngữ nghĩa khác: đó là một cập nhật trạng thái, không phải cảnh báo.
135
132
 
@@ -102,9 +102,9 @@ failproofai 包含两个独立的子系统:
102
102
  - 退出码:`0`
103
103
  - stdout 为空
104
104
 
105
- **允许并携带消息(测试版):**
105
+ **允许并携带消息:**
106
106
 
107
- 自 v0.0.2-beta.3 起,`allow(message)` 允许策略在操作被允许时仍向 Claude 发送信息性上下文。钩子处理器将以下 JSON 写入 **stdout**(这不是配置文件——这是处理器对 Claude Code 的响应,与 deny 和 instruct 响应的方式相同):
107
+ `allow(message)` 允许策略在操作被允许时仍向 Claude 发送信息性上下文。钩子处理器将以下 JSON 写入 **stdout**(这不是配置文件——这是处理器对 Claude Code 的响应,与 deny 和 instruct 响应的方式相同):
108
108
 
109
109
  ```json
110
110
  // Written to stdout by the hook handler process
@@ -153,7 +153,7 @@ resolved: { allowPatterns: ["sudo systemctl status"] } ← 向下回落到 glob
153
153
 
154
154
  每次 hook 事件触发时都会重新加载该文件,不存在缓存机制。关于编写自定义策略的详情,请参阅[自定义策略](/zh/custom-policies)。
155
155
 
156
- ### 基于约定的策略(v0.0.2-beta.7+)
156
+ ### 基于约定的策略
157
157
 
158
158
  除显式指定的 `customPoliciesPath` 外,failproofai 还会自动发现并加载 `.failproofai/policies/` 目录中的策略文件:
159
159
 
@@ -39,7 +39,7 @@ failproofai policies --install --custom ./my-policies.js
39
39
 
40
40
  ## 两种加载自定义策略的方式
41
41
 
42
- ### 方式一:基于约定(推荐,v0.0.2-beta.7+)
42
+ ### 方式一:基于约定(推荐)
43
43
 
44
44
  将 `*policies.{js,mjs,ts}` 文件放入 `.failproofai/policies/` 目录,它们会被自动加载——无需任何标志或配置更改。这类似于 git hooks:放入文件即可生效。
45
45
 
@@ -125,11 +125,8 @@ customPolicies.add({
125
125
  您可以通过在 `policyParams` 中添加 `hint` 字段,为任何 `deny` 或 `instruct` 消息追加额外指导——无需修改代码。这对自定义(`custom/`)、项目约定(`.failproofai-project/`)和用户约定(`.failproofai-user/`)策略同样适用。详情请参阅[配置 → hint](/zh/configuration#hint-cross-cutting)。
126
126
  </Tip>
127
127
 
128
- ### 信息性 allow 消息(Beta)
128
+ ### 信息性 allow 消息
129
129
 
130
- <Note>
131
- `allow(message)` 是自 v0.0.2-beta.3 起提供的 Beta 功能。API 可能在未来版本中发生变化。早期版本仅支持不带参数的 `allow()`。
132
- </Note>
133
130
 
134
131
  `allow(message)` 允许操作**并**向 Claude 发送一条信息性消息。该消息以 `additionalContext` 的形式通过 hook 处理器的 stdout 响应发送——与 `instruct` 使用相同的机制,但语义不同:它是状态更新,而非警告。
135
132
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs"
@@ -9,7 +9,7 @@ const currentPort = parseInt(process.env.PORT, 10) || 3000
9
9
  const hostname = process.env.HOSTNAME || '0.0.0.0'
10
10
 
11
11
  let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
12
- const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"0.0.4"},"typescript":{"ignoreBuildErrors":false},"typedRoutes":false,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.ts","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":true,"customCacheHandler":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{"serverFunctions":true,"browserToTerminal":"warn"},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/failproofai/failproofai","cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"appNewScrollHandler":false,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"partialFallbacks":false,"dynamicOnHover":false,"varyParams":false,"prefetchInlining":false,"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"imgOptSkipMetadata":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"viewTransition":false,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":false,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"gestureTransition":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":"warn","lockDistDir":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":false,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.ts","turbopack":{"root":"/home/runner/work/failproofai/failproofai"},"distDirRoot":".next"}
12
+ const nextConfig = {"env":{"NEXT_PUBLIC_APP_VERSION":"0.0.5"},"typescript":{"ignoreBuildErrors":false},"typedRoutes":false,"distDir":"./.next","cleanDistDir":true,"assetPrefix":"","cacheMaxMemorySize":52428800,"configOrigin":"next.config.ts","useFileSystemPublicRoutes":true,"generateEtags":true,"pageExtensions":["tsx","ts","jsx","js"],"poweredByHeader":true,"compress":true,"images":{"deviceSizes":[640,750,828,1080,1200,1920,2048,3840],"imageSizes":[32,48,64,96,128,256,384],"path":"/_next/image","loader":"default","loaderFile":"","domains":[],"disableStaticImages":false,"minimumCacheTTL":14400,"formats":["image/webp"],"maximumRedirects":3,"maximumResponseBody":50000000,"dangerouslyAllowLocalIP":false,"dangerouslyAllowSVG":false,"contentSecurityPolicy":"script-src 'none'; frame-src 'none'; sandbox;","contentDispositionType":"attachment","localPatterns":[{"pathname":"**","search":""}],"remotePatterns":[],"qualities":[75],"unoptimized":true,"customCacheHandler":false},"devIndicators":{"position":"bottom-left"},"onDemandEntries":{"maxInactiveAge":60000,"pagesBufferLength":5},"basePath":"","sassOptions":{},"trailingSlash":false,"i18n":null,"productionBrowserSourceMaps":false,"excludeDefaultMomentLocales":true,"reactProductionProfiling":false,"reactStrictMode":null,"reactMaxHeadersLength":6000,"httpAgentOptions":{"keepAlive":true},"logging":{"serverFunctions":true,"browserToTerminal":"warn"},"compiler":{},"expireTime":31536000,"staticPageGenerationTimeout":60,"output":"standalone","modularizeImports":{"@mui/icons-material":{"transform":"@mui/icons-material/{{member}}"},"lodash":{"transform":"lodash/{{member}}"}},"outputFileTracingRoot":"/home/runner/work/failproofai/failproofai","cacheComponents":false,"cacheLife":{"default":{"stale":300,"revalidate":900,"expire":4294967294},"seconds":{"stale":30,"revalidate":1,"expire":60},"minutes":{"stale":300,"revalidate":60,"expire":3600},"hours":{"stale":300,"revalidate":3600,"expire":86400},"days":{"stale":300,"revalidate":86400,"expire":604800},"weeks":{"stale":300,"revalidate":604800,"expire":2592000},"max":{"stale":300,"revalidate":2592000,"expire":31536000}},"cacheHandlers":{},"experimental":{"appNewScrollHandler":false,"useSkewCookie":false,"cssChunking":true,"multiZoneDraftMode":false,"appNavFailHandling":false,"prerenderEarlyExit":true,"serverMinification":true,"linkNoTouchStart":false,"caseSensitiveRoutes":false,"cachedNavigations":false,"partialFallbacks":false,"dynamicOnHover":false,"varyParams":false,"prefetchInlining":false,"preloadEntriesOnStart":true,"clientRouterFilter":true,"clientRouterFilterRedirects":false,"fetchCacheKeyPrefix":"","proxyPrefetch":"flexible","optimisticClientCache":true,"manualClientBasePath":false,"cpus":3,"memoryBasedWorkersCount":false,"imgOptConcurrency":null,"imgOptTimeoutInSeconds":7,"imgOptMaxInputPixels":268402689,"imgOptSequentialRead":null,"imgOptSkipMetadata":null,"isrFlushToDisk":true,"workerThreads":false,"optimizeCss":false,"nextScriptWorkers":false,"scrollRestoration":false,"externalDir":false,"disableOptimizedLoading":false,"gzipSize":true,"craCompat":false,"esmExternals":true,"fullySpecified":false,"swcTraceProfiling":false,"forceSwcTransforms":false,"largePageDataBytes":128000,"typedEnv":false,"parallelServerCompiles":false,"parallelServerBuildTraces":false,"ppr":false,"authInterrupts":false,"webpackMemoryOptimizations":false,"optimizeServerReact":true,"strictRouteTypes":false,"viewTransition":false,"removeUncaughtErrorAndRejectionListeners":false,"validateRSCRequestHeaders":false,"staleTimes":{"dynamic":0,"static":300},"reactDebugChannel":true,"serverComponentsHmrCache":true,"staticGenerationMaxConcurrency":8,"staticGenerationMinPagesPerWorker":25,"transitionIndicator":false,"gestureTransition":false,"inlineCss":false,"useCache":false,"globalNotFound":false,"browserDebugInfoInTerminal":"warn","lockDistDir":true,"proxyClientMaxBodySize":10485760,"hideLogsAfterAbort":false,"mcpServer":true,"turbopackFileSystemCacheForDev":true,"turbopackFileSystemCacheForBuild":false,"turbopackInferModuleSideEffects":true,"turbopackPluginRuntimeStrategy":"childProcesses","optimizePackageImports":["lucide-react","date-fns","lodash-es","ramda","antd","react-bootstrap","ahooks","@ant-design/icons","@headlessui/react","@headlessui-float/react","@heroicons/react/20/solid","@heroicons/react/24/solid","@heroicons/react/24/outline","@visx/visx","@tremor/react","rxjs","@mui/material","@mui/icons-material","recharts","react-use","effect","@effect/schema","@effect/platform","@effect/platform-node","@effect/platform-browser","@effect/platform-bun","@effect/sql","@effect/sql-mssql","@effect/sql-mysql2","@effect/sql-pg","@effect/sql-sqlite-node","@effect/sql-sqlite-bun","@effect/sql-sqlite-wasm","@effect/sql-sqlite-react-native","@effect/rpc","@effect/rpc-http","@effect/typeclass","@effect/experimental","@effect/opentelemetry","@material-ui/core","@material-ui/icons","@tabler/icons-react","mui-core","react-icons/ai","react-icons/bi","react-icons/bs","react-icons/cg","react-icons/ci","react-icons/di","react-icons/fa","react-icons/fa6","react-icons/fc","react-icons/fi","react-icons/gi","react-icons/go","react-icons/gr","react-icons/hi","react-icons/hi2","react-icons/im","react-icons/io","react-icons/io5","react-icons/lia","react-icons/lib","react-icons/lu","react-icons/md","react-icons/pi","react-icons/ri","react-icons/rx","react-icons/si","react-icons/sl","react-icons/tb","react-icons/tfi","react-icons/ti","react-icons/vsc","react-icons/wi"],"trustHostHeader":false,"isExperimentalCompile":false},"htmlLimitedBots":"[\\w-]+-Google|Google-[\\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight","bundlePagesRouterDependencies":false,"configFileName":"next.config.ts","turbopack":{"root":"/home/runner/work/failproofai/failproofai"},"distDirRoot":".next"}
13
13
 
14
14
  process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
15
15
 
@@ -212,6 +212,36 @@ function getThirdPartyCheckRuns(cwd: string, sha: string): CiCheck[] {
212
212
  }
213
213
  }
214
214
 
215
+ /** Fetch commit statuses (legacy Status API) and normalize to CiCheck format. */
216
+ function getCommitStatuses(cwd: string, sha: string): CiCheck[] {
217
+ try {
218
+ const json = execFileSync(
219
+ "gh",
220
+ [
221
+ "api",
222
+ `repos/{owner}/{repo}/commits/${sha}/statuses`,
223
+ "--jq",
224
+ 'map({name: .context, state: .state}) | unique_by(.name)',
225
+ ],
226
+ {
227
+ cwd,
228
+ encoding: "utf8",
229
+ timeout: 15000,
230
+ },
231
+ ).trim();
232
+
233
+ if (!json || json === "[]") return [];
234
+ const statuses = JSON.parse(json) as Array<{ name: string; state: string }>;
235
+ return statuses.map((s) => ({
236
+ name: s.name,
237
+ status: s.state === "pending" ? "in_progress" : "completed",
238
+ conclusion: s.state === "pending" ? "" : s.state === "success" ? "success" : "failure",
239
+ }));
240
+ } catch {
241
+ return [];
242
+ }
243
+ }
244
+
215
245
  /**
216
246
  * Check if a command matches an allow pattern using token-by-token comparison.
217
247
  * The "*" token is a wildcard. Extra command tokens beyond the pattern are allowed,
@@ -859,7 +889,7 @@ function requireCommitBeforeStop(ctx: PolicyContext): PolicyResult {
859
889
 
860
890
  if (status.length > 0) {
861
891
  return deny(
862
- "You have uncommitted changes in the working directory. Commit all changes before stopping.",
892
+ "You have uncommitted changes in the working directory. Commit all changes now.",
863
893
  );
864
894
  }
865
895
  return allow("All changes are committed.");
@@ -937,7 +967,7 @@ function requirePushBeforeStop(ctx: PolicyContext): PolicyResult {
937
967
  if (!hasTracking) {
938
968
  return deny(
939
969
  `Branch "${branch}" has not been pushed to remote "${remote}". ` +
940
- `Push your branch with: git push -u ${remote} ${branch}`,
970
+ `Run now: git push -u ${remote} ${branch}`,
941
971
  );
942
972
  }
943
973
 
@@ -952,7 +982,7 @@ function requirePushBeforeStop(ctx: PolicyContext): PolicyResult {
952
982
  const commitCount = unpushed.split("\n").length;
953
983
  return deny(
954
984
  `You have ${commitCount} unpushed commit${commitCount > 1 ? "s" : ""} on branch "${branch}". ` +
955
- `Push your changes with: git push`,
985
+ `Run now: git push`,
956
986
  );
957
987
  }
958
988
 
@@ -1024,7 +1054,7 @@ function requirePrBeforeStop(ctx: PolicyContext): PolicyResult {
1024
1054
  // gh pr view exits non-zero when no PR exists
1025
1055
  return deny(
1026
1056
  `No pull request found for branch "${branch}". ` +
1027
- `Create one with: gh pr create`,
1057
+ `Run now: gh pr create`,
1028
1058
  );
1029
1059
  }
1030
1060
 
@@ -1035,7 +1065,7 @@ function requirePrBeforeStop(ctx: PolicyContext): PolicyResult {
1035
1065
  }
1036
1066
 
1037
1067
  return deny(
1038
- `Pull request for branch "${branch}" is ${pr.state.toLowerCase()}. Create a new PR with: gh pr create`,
1068
+ `Pull request for branch "${branch}" is ${pr.state.toLowerCase()}. Run now: gh pr create`,
1039
1069
  );
1040
1070
  } catch {
1041
1071
  return allow("Could not check PR status, skipping.");
@@ -1075,13 +1105,15 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
1075
1105
 
1076
1106
  // 2. Third-party check runs (CodeRabbit, SonarCloud, Codecov, etc.)
1077
1107
  let thirdPartyChecks: CiCheck[] = [];
1108
+ let commitStatuses: CiCheck[] = [];
1078
1109
  const sha = getHeadSha(cwd);
1079
1110
  if (sha) {
1080
1111
  thirdPartyChecks = getThirdPartyCheckRuns(cwd, sha);
1112
+ commitStatuses = getCommitStatuses(cwd, sha);
1081
1113
  }
1082
1114
 
1083
1115
  // 3. Merge all checks
1084
- const allChecks = [...workflowRuns, ...thirdPartyChecks];
1116
+ const allChecks = [...workflowRuns, ...thirdPartyChecks, ...commitStatuses];
1085
1117
 
1086
1118
  if (allChecks.length === 0) return allow(`No CI runs found for branch "${branch}".`);
1087
1119
 
@@ -1091,7 +1123,7 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
1091
1123
  if (failing.length > 0) {
1092
1124
  const names = failing.map((r) => `"${r.name}"`).join(", ");
1093
1125
  return deny(
1094
- `CI checks are failing on branch "${branch}": ${names}. Fix the failing checks before stopping.`,
1126
+ `CI checks are failing on branch "${branch}": ${names}. Fix the failing checks now.`,
1095
1127
  );
1096
1128
  }
1097
1129
 
@@ -1101,7 +1133,7 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
1101
1133
  if (pending.length > 0) {
1102
1134
  const names = pending.map((r) => `"${r.name}"`).join(", ");
1103
1135
  return deny(
1104
- `CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete and verify they pass.`,
1136
+ `CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete, then verify they pass.`,
1105
1137
  );
1106
1138
  }
1107
1139
 
@@ -130,6 +130,17 @@ export async function evaluatePolicies(
130
130
  };
131
131
  }
132
132
 
133
+ if (eventType === "Stop") {
134
+ return {
135
+ exitCode: 2,
136
+ stdout: "",
137
+ stderr: `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}\n\nYou MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`,
138
+ policyName: policy.name,
139
+ reason,
140
+ decision: "deny",
141
+ };
142
+ }
143
+
133
144
  // Other event types: exit 2
134
145
  return {
135
146
  exitCode: 2,
@@ -165,10 +176,13 @@ export async function evaluatePolicies(
165
176
  if (eventType === "Stop") {
166
177
  // Stop hook: exitCode 2 blocks Claude from stopping.
167
178
  // Reason goes to stderr so Claude Code receives it as context.
179
+ const policyAttribution = policyNames.length === 1
180
+ ? `policy: ${policyNames[0]}`
181
+ : `policies: ${policyNames.join(", ")}`;
168
182
  return {
169
183
  exitCode: 2,
170
184
  stdout: "",
171
- stderr: combined,
185
+ stderr: `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}\n\nYou MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`,
172
186
  policyName: policyNames[0],
173
187
  policyNames,
174
188
  reason: combined,
package/README.md CHANGED
@@ -202,7 +202,7 @@ failproofai policies --install --custom ./my-policies.js
202
202
  | Function | Effect |
203
203
  |----------|--------|
204
204
  | `allow()` | Permit the operation |
205
- | `allow(message)` | Permit and send informational context to Claude *(beta)* |
205
+ | `allow(message)` | Permit and send informational context to Claude |
206
206
  | `deny(message)` | Block the operation; message shown to Claude |
207
207
  | `instruct(message)` | Add context to Claude's prompt; does not block |
208
208
 
@@ -220,7 +220,7 @@ failproofai policies --install --custom ./my-policies.js
220
220
 
221
221
  Custom hooks support transitive local imports, async/await, and access to `process.env`. Errors are fail-open (logged to `~/.failproofai/hook.log`, built-in policies continue). See [docs/custom-hooks.mdx](docs/custom-hooks.mdx) for the full guide.
222
222
 
223
- ### Convention-based policies (v0.0.2-beta.7+)
223
+ ### Convention-based policies
224
224
 
225
225
  Drop `*policies.{js,mjs,ts}` files into `.failproofai/policies/` and they're automatically loaded — no `--custom` flag or config changes needed. Works like git hooks: drop a file, it just works.
226
226
 
package/dist/cli.mjs CHANGED
@@ -321,6 +321,30 @@ function getThirdPartyCheckRuns(cwd, sha) {
321
321
  return [];
322
322
  }
323
323
  }
324
+ function getCommitStatuses(cwd, sha) {
325
+ try {
326
+ const json = execFileSync("gh", [
327
+ "api",
328
+ `repos/{owner}/{repo}/commits/${sha}/statuses`,
329
+ "--jq",
330
+ "map({name: .context, state: .state}) | unique_by(.name)"
331
+ ], {
332
+ cwd,
333
+ encoding: "utf8",
334
+ timeout: 15000
335
+ }).trim();
336
+ if (!json || json === "[]")
337
+ return [];
338
+ const statuses = JSON.parse(json);
339
+ return statuses.map((s) => ({
340
+ name: s.name,
341
+ status: s.state === "pending" ? "in_progress" : "completed",
342
+ conclusion: s.state === "pending" ? "" : s.state === "success" ? "success" : "failure"
343
+ }));
344
+ } catch {
345
+ return [];
346
+ }
347
+ }
324
348
  function matchesAllowedPattern(cmd, pattern) {
325
349
  const cmdTokens = parseArgvTokens(cmd);
326
350
  const patTokens = parseArgvTokens(pattern);
@@ -830,7 +854,7 @@ function requireCommitBeforeStop(ctx) {
830
854
  timeout: 5000
831
855
  }).trim();
832
856
  if (status.length > 0) {
833
- return deny("You have uncommitted changes in the working directory. Commit all changes before stopping.");
857
+ return deny("You have uncommitted changes in the working directory. Commit all changes now.");
834
858
  }
835
859
  return allow("All changes are committed.");
836
860
  } catch {
@@ -877,7 +901,7 @@ function requirePushBeforeStop(ctx) {
877
901
  hasTracking = true;
878
902
  } catch {}
879
903
  if (!hasTracking) {
880
- return deny(`Branch "${branch}" has not been pushed to remote "${remote}". ` + `Push your branch with: git push -u ${remote} ${branch}`);
904
+ return deny(`Branch "${branch}" has not been pushed to remote "${remote}". ` + `Run now: git push -u ${remote} ${branch}`);
881
905
  }
882
906
  const unpushed = execFileSync("git", ["log", `${remote}/${branch}..HEAD`, "--oneline"], {
883
907
  cwd,
@@ -887,7 +911,7 @@ function requirePushBeforeStop(ctx) {
887
911
  if (unpushed.length > 0) {
888
912
  const commitCount = unpushed.split(`
889
913
  `).length;
890
- return deny(`You have ${commitCount} unpushed commit${commitCount > 1 ? "s" : ""} on branch "${branch}". ` + `Push your changes with: git push`);
914
+ return deny(`You have ${commitCount} unpushed commit${commitCount > 1 ? "s" : ""} on branch "${branch}". ` + `Run now: git push`);
891
915
  }
892
916
  return allow(`All commits pushed to "${remote}".`);
893
917
  } catch {
@@ -929,13 +953,13 @@ function requirePrBeforeStop(ctx) {
929
953
  timeout: 15000
930
954
  }).trim();
931
955
  } catch {
932
- return deny(`No pull request found for branch "${branch}". ` + `Create one with: gh pr create`);
956
+ return deny(`No pull request found for branch "${branch}". ` + `Run now: gh pr create`);
933
957
  }
934
958
  const pr = JSON.parse(prJson);
935
959
  if (pr.state === "OPEN") {
936
960
  return allow(`PR #${pr.number} exists: ${pr.url}`);
937
961
  }
938
- return deny(`Pull request for branch "${branch}" is ${pr.state.toLowerCase()}. Create a new PR with: gh pr create`);
962
+ return deny(`Pull request for branch "${branch}" is ${pr.state.toLowerCase()}. Run now: gh pr create`);
939
963
  } catch {
940
964
  return allow("Could not check PR status, skipping.");
941
965
  }
@@ -961,22 +985,24 @@ function requireCiGreenBeforeStop(ctx) {
961
985
  }
962
986
  } catch {}
963
987
  let thirdPartyChecks = [];
988
+ let commitStatuses = [];
964
989
  const sha = getHeadSha(cwd);
965
990
  if (sha) {
966
991
  thirdPartyChecks = getThirdPartyCheckRuns(cwd, sha);
992
+ commitStatuses = getCommitStatuses(cwd, sha);
967
993
  }
968
- const allChecks = [...workflowRuns, ...thirdPartyChecks];
994
+ const allChecks = [...workflowRuns, ...thirdPartyChecks, ...commitStatuses];
969
995
  if (allChecks.length === 0)
970
996
  return allow(`No CI runs found for branch "${branch}".`);
971
997
  const failing = allChecks.filter((r) => r.status === "completed" && r.conclusion !== "success" && r.conclusion !== "skipped");
972
998
  if (failing.length > 0) {
973
999
  const names = failing.map((r) => `"${r.name}"`).join(", ");
974
- return deny(`CI checks are failing on branch "${branch}": ${names}. Fix the failing checks before stopping.`);
1000
+ return deny(`CI checks are failing on branch "${branch}": ${names}. Fix the failing checks now.`);
975
1001
  }
976
1002
  const pending = allChecks.filter((r) => r.status === "in_progress" || r.status === "queued" || r.status === "waiting");
977
1003
  if (pending.length > 0) {
978
1004
  const names = pending.map((r) => `"${r.name}"`).join(", ");
979
- return deny(`CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete and verify they pass.`);
1005
+ return deny(`CI checks are still running on branch "${branch}": ${names}. Wait for all checks to complete, then verify they pass.`);
980
1006
  }
981
1007
  return allow(`All CI checks passed on branch "${branch}".`);
982
1008
  } catch {
@@ -1457,6 +1483,18 @@ async function evaluatePolicies(eventType, payload, session, config) {
1457
1483
  decision: "deny"
1458
1484
  };
1459
1485
  }
1486
+ if (eventType === "Stop") {
1487
+ return {
1488
+ exitCode: 2,
1489
+ stdout: "",
1490
+ stderr: `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}
1491
+
1492
+ You MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`,
1493
+ policyName: policy.name,
1494
+ reason,
1495
+ decision: "deny"
1496
+ };
1497
+ }
1460
1498
  return {
1461
1499
  exitCode: 2,
1462
1500
  stdout: "",
@@ -1480,10 +1518,13 @@ async function evaluatePolicies(eventType, payload, session, config) {
1480
1518
  `);
1481
1519
  const policyNames = instructEntries.map((e) => e.policyName);
1482
1520
  if (eventType === "Stop") {
1521
+ const policyAttribution = policyNames.length === 1 ? `policy: ${policyNames[0]}` : `policies: ${policyNames.join(", ")}`;
1483
1522
  return {
1484
1523
  exitCode: 2,
1485
1524
  stdout: "",
1486
- stderr: combined,
1525
+ stderr: `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}
1526
+
1527
+ You MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`,
1487
1528
  policyName: policyNames[0],
1488
1529
  policyNames,
1489
1530
  reason: combined,
@@ -1910,7 +1951,7 @@ var init_hook_activity_store = __esm(() => {
1910
1951
  });
1911
1952
 
1912
1953
  // package.json
1913
- var version2 = "0.0.4";
1954
+ var version2 = "0.0.5";
1914
1955
  var init_package = () => {};
1915
1956
 
1916
1957
  // src/posthog-key.ts
@@ -3202,7 +3243,7 @@ import { realpathSync as realpathSync2 } from "fs";
3202
3243
  import { dirname as dirname5, resolve as resolve8 } from "path";
3203
3244
  import { fileURLToPath as fileURLToPath2 } from "url";
3204
3245
  // package.json
3205
- var version = "0.0.4";
3246
+ var version = "0.0.5";
3206
3247
 
3207
3248
  // bin/failproofai.mjs
3208
3249
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs"