> ## Documentation Index
> Fetch the complete documentation index at: https://docs.befailproof.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> כיצד Handler ה-Hook, טעינת Config, והערכת Policy עובדים באופן פנימי

מסמך זה מסביר כיצד failproofai עובד באופן פנימי: כיצד מערכת ה-Hook מיירטת קריאות Tool של Agent, כיצד Config נטען ומוזג, כיצד Policies מוערכות, וכיצד ה-Dashboard עוקב אחרי פעילות Agent.

***

## Overview

ל-failproofai יש שתי תת-מערכות עצמאיות:

1. **Hook handler** - CLI subprocess מהיר שClaude Code קורא לו בכל קריאת Tool של Agent. מעריך Policies ומחזיר החלטה.
2. **Agent Monitor (Dashboard)** - אפליקציית Next.js לעקיבה אחרי סשנים של Agent וניהול Policies.

שתי התת-מערכות משתפות קבצי Config ב-`~/.failproofai/` ובתיקייה `.failproofai/` של הפרויקט, אך הן רצות כתהליכים נפרדים ותקשרו רק דרך מערכת הקבצים.

***

## Hook handler

### Integration עם Claude Code

כאשר אתה מריץ `failproofai policies --install`, הוא כותב entries כמו זה ל-`~/.claude/settings.json`:

```json theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "failproofai --hook PreToolUse"
          }
        ]
      }
    ],
    "PostToolUse": [ ... ]
  }
}
```

Claude Code קורא אחר כך ל-`failproofai --hook PreToolUse` כ-subprocess לפני כל קריאת Tool, ומעביר JSON payload על stdin.

### פורמט Payload

```json theme={null}
{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/myproject/sessions/abc123.jsonl",
  "cwd": "/home/user/myproject",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "sudo apt install nodejs" }
}
```

עבור אירועי `PostToolUse`, ה-payload גם כולל `tool_result` עם Output של ה-Tool.

ה-Handler אוכף מגבלת stdin של 1 MB. Payloads החוצים זאת מושלכים וכל Policies באופן implicit מאפשרים.

### Response format

**Deny (PreToolUse):**

```json theme={null}
{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason": "Blocked by failproofai: sudo command blocked"
  }
}
```

**Deny (PostToolUse):**

```json theme={null}
{
  "hookSpecificOutput": {
    "additionalContext": "Blocked by failproofai because: API key detected in output"
  }
}
```

**Instruct (כל אירוע למעט Stop):**

```json theme={null}
{
  "hookSpecificOutput": {
    "additionalContext": "Instruction from failproofai: Verify tests pass before committing."
  }
}
```

**Stop event instruct:**

* Exit code: `2`
* Reason כתוב ל-stderr (לא stdout)

**Allow:**

* Exit code: `0`
* stdout ריק

**Allow עם Message:**

`allow(message)` מאפשר לPolicy לשלוח Context informational חזרה ל-Claude גם כאשר הפעולה מותרת. ה-Hook Handler כותב את ה-JSON הבא ל-**stdout** (לא קובץ Config — זה ה-Response של ה-Handler ל-Claude Code, בדיוק כמו Deny ו-Instruct responses למעלה):

```json theme={null}
// Written to stdout by the hook handler process
{
  "hookSpecificOutput": {
    "additionalContext": "All CI checks passed on branch 'feat/my-feature'."
  }
}
```

* Exit code: `0` (הפעולה מותרת)
* כאשר multiple Policies מחזירות `allow` עם Message, ה-Messages שלהם מצורפות עם newlines לתוך string `additionalContext` יחיד
* אם אף Policy לא מספק Message, stdout ריק (כמו קודם)

### Processing pipeline

`src/hooks/handler.ts` מיישם את כל ה-Pipeline:

```text theme={null}
stdin JSON
  → parse payload (max 1 MB)
  → extract session metadata (session_id, cwd, tool_name, tool_input, etc.)
  → readMergedHooksConfig(cwd)    ← merges project + local + global config
  → register enabled builtin policies with resolved params
  → load custom policies from customPoliciesPath (if set)
  → register custom policies into policy registry
  → evaluate all policies (builtins first, then custom)
      → first deny short-circuits
      → instruct decisions accumulate
      → allow messages accumulate
  → write JSON decision to stdout
  → persist event to ~/.failproofai/hook-activity.jsonl
  → exit
```

כל התהליך רץ בפחות מ-100ms עבור Payloads טיפוסיים בלי LLM calls.

***

## Configuration loading

`src/hooks/hooks-config.ts` מיישם שלוש-scope Config loading.

```text theme={null}
[1] {cwd}/.failproofai/policies-config.json        ← project  (highest priority)
[2] {cwd}/.failproofai/policies-config.local.json  ← local
[3] ~/.failproofai/policies-config.json             ← global   (lowest priority)
```

Merge logic:

* `enabledPolicies` - deduplicated union על כל שלושת הקבצים
* `policyParams` - per-policy key, הקובץ הראשון שמגדיר אותו מנצח לחלוטין
* `customPoliciesPath` - הקובץ הראשון שמגדיר אותו מנצח
* `llm` - הקובץ הראשון שמגדיר אותו מנצח

ה-Web Dashboard משתמש ב-`readHooksConfig()` (global only) לקריאה וכתיבה, מכיוון שהוא לא קרוא עם Project cwd.

***

## Policy evaluation

`src/hooks/policy-evaluator.ts` מריץ Policies בסדר.

עבור כל Policy:

1. חפש את ה-`params` schema של ה-Policy (אם יש לו).
2. קרא `policyParams[policy.name]` מה-Merged Config.
3. Merge user-provided values על schema defaults לייצור `ctx.params`.
4. קרא `policy.fn(ctx)` עם ה-Resolved Context.
5. אם ה-Result הוא `deny`, עצור מיד וחזור בהחלטה זו.
6. אם ה-Result הוא `instruct`, צבור את ה-Message והמשך.
7. אם ה-Result הוא `allow`, המשך ל-Policy הבא.

לאחר שכל Policies רצים:

* אם כל `deny` חזר, emit את ה-Deny Response.
* אם כל `instruct` returns נאספו, emit Response `instruct` יחיד עם כל Messages מצורפות.
* אחרת, emit Allow Response (stdout ריק, exit 0).

***

## Builtin policies

`src/hooks/builtin-policies.ts` מגדיר את כל 39 ה-Builtin Policies כ-`BuiltinPolicyDefinition` objects:

```typescript theme={null}
interface BuiltinPolicyDefinition {
  name: string;
  description: string;
  fn: (ctx: PolicyContext) => PolicyResult;
  match: {
    events: HookEventType[];
    tools?: string[];
  };
  defaultEnabled: boolean;
  category: string;
  beta?: boolean;
  params?: PolicyParamsSchema;
}
```

Policies שמקבלות `params` מכריזות `PolicyParamsSchema` עם Types וDefaults עבור כל Parameter. ה-Policy Evaluator משדר Resolved Values ל-`ctx.params` לפני קריאה ל-`fn`. Policy Functions קוראות `ctx.params` בלי Null-Guarding כי Defaults תמיד מיושמות ראשון.

Pattern Matching בתוך Policies משתמש בParsed Command Tokens (argv), לא Raw String Matching. זה מונע Bypass דרך Shell Operator Injection (למשל Pattern עבור `sudo systemctl status *` לא יכול להיות bypassed על ידי Appending `; rm -rf /` לפקודה).

***

## Custom policies

`src/hooks/custom-hooks-registry.ts` מיישם `globalThis`-backed Registry:

```typescript theme={null}
const REGISTRY_KEY = "__failproofai_custom_hooks__";

export const customPolicies = {
  add(hook: CustomHook): void { ... }
};

export function getCustomHooks(): CustomHook[] { ... }
export function clearCustomHooks(): void { ... }  // used in tests
```

`src/hooks/custom-hooks-loader.ts` טוען את Policy File של ה-User:

1. קרא `customPoliciesPath` מ-Config; דלג אם חסר.
2. Resolve ל-Absolute Path; בדוק אם הקובץ קיים.
3. כתוב מחדש את כל `from "failproofai"` Imports ל-Actual Dist Path כדי שה-`customPolicies` יתמוקד ל-`globalThis` Registry אותו.
4. כתוב מחדש באופן Recursive Transitive Local Imports כדי להבטיח ESM Compatibility.
5. כתוב Temporary `.mjs` Files ו-`import()` את Entry File.
6. קרא `getCustomHooks()` כדי לאחזר Registered Hooks.
7. נקה את כל Temp Files בבלוק `finally`.

בכל Error (File not found, Syntax Error, Import Failure), ה-Error נרשם ל-`~/.failproofai/hook.log` ו-Loader מחזיר Array ריק. Builtin Policies לא מושפעות.

Custom Policies מוערכות לאחר כל Builtin Policies. Custom Policy `deny` עדיין Short-Circuits Further Custom Policies (אבל כל ה-Builtins כבר רצו בנקודה זו).

***

## Activity logging

לאחר כל Hook Event, ה-Handler Appends JSONL Line ל-`~/.failproofai/hook-activity.jsonl`:

```json theme={null}
{
  "timestamp": "2026-04-06T12:34:56.789Z",
  "sessionId": "abc123",
  "eventType": "PreToolUse",
  "toolName": "Bash",
  "policyName": "block-sudo",
  "decision": "deny",
  "reason": "sudo command blocked by failproofai",
  "durationMs": 12
}
```

שורה אחת לכל Policy שעשה Decide שאינו Allow. Allow Decisions לא נרשמות (כדי לשמור את הקובץ קטן).

***

## Dashboard architecture

ה-Dashboard היא אפליקציית **Next.js 16** באמצעות App Router עם React Server Components ו-Server Actions.

```text theme={null}
app/
  layout.tsx                  ← Root layout (theme, telemetry, nav)
  projects/page.tsx           ← Server component: list all Claude projects
  project/[name]/page.tsx     ← Server component: list sessions in a project
  project/[name]/session/
    [sessionId]/page.tsx      ← Server component: render session viewer
  policies/page.tsx           ← Client component: policy management + activity log
  actions/
    get-hooks-config.ts       ← Read config + policy list
    update-hooks-config.ts    ← Toggle policy on/off
    update-policy-params.ts   ← Update policy parameters
    get-hook-activity.ts      ← Paginate/search activity log
    install-hooks-web.ts      ← Install/remove hooks from the browser
  api/
    download/[project]/[session]/route.ts   ← Per-CLI session export (JSONL or JSON)
```

**Data flow:**

* Page Components קוראים ל-`lib/projects.ts` ו-`lib/log-entries.ts` כדי לקרוא Project/Session Data ישירות מ-Filesystem (אין API Layer עבור Reads).
* ה-Policies Page משתמשת ב-Server Actions עבור כל Mutations (Toggle, Params Update, Install/Remove).
* ה-Session Viewer מנתח Claude's JSONL Transcript Format ומרנדר Timeline של Messages וTool Calls.

**Key Design Decisions:**

* No Database - כל Persistent State נמצא בקבצים רגילים (`~/.failproofai/`, `~/.claude/projects/`).
* Server Actions לMutations - אין צורך ב-REST API עבור CRUD Operations.
* React Server Components לRead Pages - Fast Initial Load, לא Client Bundle עבור Data Fetching.
* Client Components רק כאשר Interactivity נדרשת (Policy Toggles, Activity Search, Log Viewer).

***

## File layout

```text theme={null}
failproofai/
├── bin/
│   └── failproofai.mjs           # CLI router (hook / dashboard / install / etc.)
├── src/hooks/
│   ├── handler.ts                # Hook event pipeline
│   ├── builtin-policies.ts       # 39 policy definitions
│   ├── policy-evaluator.ts       # Policy execution engine
│   ├── policy-registry.ts        # Policy registration and lookup
│   ├── policy-types.ts           # TypeScript interfaces
│   ├── hooks-config.ts           # Multi-scope config loading
│   ├── custom-hooks-registry.ts  # globalThis-backed hook registry
│   ├── custom-hooks-loader.ts    # ESM loader for user JS hooks
│   ├── manager.ts                # install / remove / list operations
│   ├── install-prompt.ts         # Interactive policy selection prompt
│   ├── hook-logger.ts            # Logging to hook.log
│   ├── hook-activity-store.ts    # Persist activity to hook-activity.jsonl
│   └── llm-client.ts             # LLM API client (for AI-powered policies)
├── app/                          # Next.js dashboard (pages + server actions)
├── lib/                          # Shared utilities
│   ├── projects.ts               # Enumerate Claude projects from filesystem
│   ├── log-entries.ts            # Parse Claude transcript JSONL format
│   ├── paths.ts                  # Resolve system paths
│   └── ...
├── components/                   # Shared React UI components
├── contexts/                     # React context providers (theme, auto-refresh, telemetry)
├── examples/                     # Example custom hook files
└── __tests__/                    # Unit and E2E tests
```
