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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0v1egkj._.js → [root-of-the-server]__0a~g15g._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okos0k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0lty_fo._.js → [root-of-the-server]__0qn95h3._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
- package/.next/standalone/.next/static/chunks/{0de3q2juhg_dr.js → 09ikntpt2-o9b.js} +1 -1
- package/.next/standalone/.next/static/chunks/{11zlh73ggln0w.js → 0_yayar~bpphd.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0b2_069x5qnxg.js → 0em7tspi4kylh.js} +2 -2
- package/.next/standalone/.next/static/chunks/{0y-bi_mp2rv4l.js → 0lgbwkfqmnsmc.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0uxpbrcv44lga.js → 0sme4lkv.tgn-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0tw_xfxb1tto..js → 0yumumfzx_f27.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0p5sfob-upg0g.js → 13juklu.vksks.js} +1 -1
- package/.next/standalone/.next/static/chunks/17manv47o-~wp.js +1 -0
- package/.next/standalone/CHANGELOG.md +9 -0
- package/.next/standalone/README.md +2 -2
- package/.next/standalone/dist/cli.mjs +52 -11
- package/.next/standalone/docs/ar/architecture.mdx +2 -2
- package/.next/standalone/docs/ar/configuration.mdx +1 -1
- package/.next/standalone/docs/ar/custom-policies.mdx +2 -5
- package/.next/standalone/docs/architecture.mdx +2 -2
- package/.next/standalone/docs/configuration.mdx +1 -1
- package/.next/standalone/docs/custom-policies.mdx +2 -6
- package/.next/standalone/docs/de/architecture.mdx +2 -2
- package/.next/standalone/docs/de/configuration.mdx +1 -1
- package/.next/standalone/docs/de/custom-policies.mdx +2 -5
- package/.next/standalone/docs/es/architecture.mdx +2 -2
- package/.next/standalone/docs/es/configuration.mdx +1 -1
- package/.next/standalone/docs/es/custom-policies.mdx +2 -5
- package/.next/standalone/docs/fr/architecture.mdx +2 -2
- package/.next/standalone/docs/fr/configuration.mdx +1 -1
- package/.next/standalone/docs/fr/custom-policies.mdx +2 -5
- package/.next/standalone/docs/he/architecture.mdx +2 -2
- package/.next/standalone/docs/he/configuration.mdx +1 -1
- package/.next/standalone/docs/he/custom-policies.mdx +2 -5
- package/.next/standalone/docs/hi/architecture.mdx +2 -2
- package/.next/standalone/docs/hi/configuration.mdx +1 -1
- package/.next/standalone/docs/hi/custom-policies.mdx +2 -5
- package/.next/standalone/docs/i18n/README.ar.md +2 -2
- package/.next/standalone/docs/i18n/README.de.md +2 -2
- package/.next/standalone/docs/i18n/README.es.md +2 -2
- package/.next/standalone/docs/i18n/README.fr.md +2 -2
- package/.next/standalone/docs/i18n/README.he.md +2 -2
- package/.next/standalone/docs/i18n/README.hi.md +2 -2
- package/.next/standalone/docs/i18n/README.it.md +2 -2
- package/.next/standalone/docs/i18n/README.ja.md +2 -2
- package/.next/standalone/docs/i18n/README.ko.md +2 -2
- package/.next/standalone/docs/i18n/README.pt-br.md +2 -2
- package/.next/standalone/docs/i18n/README.ru.md +2 -2
- package/.next/standalone/docs/i18n/README.tr.md +2 -2
- package/.next/standalone/docs/i18n/README.vi.md +2 -2
- package/.next/standalone/docs/i18n/README.zh.md +2 -2
- package/.next/standalone/docs/it/architecture.mdx +2 -2
- package/.next/standalone/docs/it/configuration.mdx +1 -1
- package/.next/standalone/docs/it/custom-policies.mdx +2 -5
- package/.next/standalone/docs/ja/architecture.mdx +2 -2
- package/.next/standalone/docs/ja/configuration.mdx +1 -1
- package/.next/standalone/docs/ja/custom-policies.mdx +2 -5
- package/.next/standalone/docs/ko/architecture.mdx +2 -2
- package/.next/standalone/docs/ko/configuration.mdx +1 -1
- package/.next/standalone/docs/ko/custom-policies.mdx +2 -5
- package/.next/standalone/docs/pt-br/architecture.mdx +2 -2
- package/.next/standalone/docs/pt-br/configuration.mdx +1 -1
- package/.next/standalone/docs/pt-br/custom-policies.mdx +2 -5
- package/.next/standalone/docs/ru/architecture.mdx +2 -2
- package/.next/standalone/docs/ru/configuration.mdx +1 -1
- package/.next/standalone/docs/ru/custom-policies.mdx +2 -5
- package/.next/standalone/docs/tr/architecture.mdx +2 -2
- package/.next/standalone/docs/tr/configuration.mdx +1 -1
- package/.next/standalone/docs/tr/custom-policies.mdx +2 -5
- package/.next/standalone/docs/vi/architecture.mdx +2 -2
- package/.next/standalone/docs/vi/configuration.mdx +1 -1
- package/.next/standalone/docs/vi/custom-policies.mdx +2 -5
- package/.next/standalone/docs/zh/architecture.mdx +2 -2
- package/.next/standalone/docs/zh/configuration.mdx +1 -1
- package/.next/standalone/docs/zh/custom-policies.mdx +2 -5
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/src/hooks/builtin-policies.ts +40 -8
- package/.next/standalone/src/hooks/policy-evaluator.ts +15 -1
- package/README.md +2 -2
- package/dist/cli.mjs +52 -11
- package/package.json +1 -1
- package/src/hooks/builtin-policies.ts +40 -8
- package/src/hooks/policy-evaluator.ts +15 -1
- package/.next/standalone/.next/static/chunks/0xjz3w.yw5tza.js +0 -1
- /package/.next/standalone/.next/static/{LayjxQulxIcNH19Lqgjbf → hYQM6iCWnF1W5XDpsIRhV}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{LayjxQulxIcNH19Lqgjbf → hYQM6iCWnF1W5XDpsIRhV}/_clientMiddlewareManifest.js +0 -0
- /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
|
|
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
|
|
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
|
-
**Разрешить с
|
|
107
|
+
**Разрешить с сообщением:**
|
|
108
108
|
|
|
109
|
-
|
|
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
|
-
### Политики на основе конвенций
|
|
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: На основе соглашений (
|
|
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
|
|
106
|
+
**İzinle mesaj:**
|
|
107
107
|
|
|
108
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
106
|
+
**Allow with message:**
|
|
107
107
|
|
|
108
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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()}.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}". ` + `
|
|
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}". ` + `
|
|
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}". ` + `
|
|
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()}.
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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"
|