Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Claude Hooks Sdk Laravel Package

beyondcode/claude-hooks-sdk

A Laravel/PHP SDK for building Claude Code hooks with clean, typed classes instead of raw shell scripts. Register hooks, handle events, and integrate Claude Code automation directly into your Laravel apps with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a thin adapter around the Claude Code hook contract: Claude spawns your PHP script, pipes a JSON event to stdin, and reads a decision from stdout/exit code. The SDK’s whole job is to turn that into typed objects and return values. Get it working as plumbing before you write guardrail logic.

  1. Install into a dev-only dependency. Claude Code hooks are workstation/agent tooling, not application runtime — keep it out of require.

    composer require --dev beyondcode/claude-hooks-sdk
    

    On Laravel the service provider auto-registers. Publish any config/commands it ships (php artisan vendor:publish) and read the generated config plus the package’s examples/ directory before reading anything else — the SDK surface is small, examples are the fastest onboarding.

  2. Wire one hook into .claude/settings.json. Start with the safest possible hook: an audit-logging PreToolUse that dumps the raw payload and always allows. This confirms the PHP binary is on PATH, stdin reaches your script, and events actually fire — before you touch a single rule.

    {
      "hooks": {
        "PreToolUse": [{
          "matcher": "Bash",
          "hooks": [{ "type": "command", "command": "php artisan claude:hook audit-bash" }]
        }]
      }
    }
    
  3. Confirm the loop. Run one Bash tool call in Claude Code and check storage/logs/laravel.log for the dumped payload. Once you see a real event, you have a working hook; everything after that is business logic.

  4. First real use case. A PreToolUse write-blocker on .env, CI configs, and infra paths. It is high value, low false-positive, and independently shippable — exactly the kind of guardrail that justifies the dependency in week one.

Implementation Patterns

One small class per hook. The SDK’s typed event classes make each hook a unit-testable object: you construct it, hand it a typed event, and assert on the returned decision. Resist the urge to build a monolithic dispatcher — one hook, one class, one policy.

Return decisions, not exit codes. The value of the SDK is that you express allow / deny / inject-context as return values, and the SDK maps them onto Claude’s exit-code-plus-JSON contract. This is what makes hooks testable without mocking stdio.

class BlockSecretWrites
{
    public function handle(PreToolUse $event): Decision
    {
        $path = $event->toolInput('file_path');

        if ($path && preg_match('/(\.env|secrets?\.ya?ml)/', $path)) {
            return Decision::deny(
                "Writes to {$path} are blocked. Use .env.example or ask a human."
            );
        }

        return Decision::allow();
    }
}

Match by event type, not by string grep. Use the matcher in settings.json to route events to the right hook, and use the SDK’s typed classes to branch inside. Matchers keep unrelated events from firing your script — that’s your first defense against latency bloat.

Layer the guardrails by lifecycle stage. The day-to-day pattern that scales is:

  • PreToolUse — the enforcement layer. Secret write-blocker, destructive-command blocker (rm -rf, git push --force, migrate:fresh on prod).
  • PostToolUse — the quality layer. Run Pint/PHPStan on Edit/Write and return failures into the agent’s context so it self-corrects on the next turn.
  • SessionStart — context injection. Branch, ticket ID, and feature-flag state prepended to the session.
  • Stop / Notification — observability. Slack/email when a long-running task finishes.

Make deny messages actionable. The reason returned in Decision::deny() is fed back to the model. “Blocked” alone causes the agent to stall or retry the same call. Say what was blocked, why, and what to do instead.

Wrap behind one internal interface. Even on a small surface, define your own Hook contract plus one payload DTO and let the SDK be the only implementation behind it. This is the difference between absorbing a schema change in one class versus every hook in the repo.

Test at the class boundary, not the process boundary. Because events are typed, new BlockSecretWrites()->handle(new PreToolUse([...])) is a normal unit test. No proc_open, no fixtures on disk. Put a happy path, a denied path, and one "malformed payload" case in every hook’s test file.

Route audit logs through Laravel’s existing stack. Use the SDK’s input-accessor methods to pull tool name, tool input, decision, and a hash of the payload, then log through your normal channels/metrics so the compliance story reuses infrastructure you already trust.

Gotchas and Tips

A hook that never fires is a config bug, not an SDK bug. This is the single most common day-one failure. Check in order: (1) .claude/settings.json is valid JSON, (2) the matcher actually matches the tool, (3) the command path resolves from Claude’s working directory (use absolute paths or an Artisan command, not a relative php hooks/x.php), (4) the PHP binary exists on PATH on that machine/CI runner. Do not touch the hook class until all four are green.

Fail open by default; fail closed by exception. A crashing or timing-out hook silently degrades the agent session — or blocks it entirely mid-task. Wrap the SDK dispatch in a try/catch that returns an explicit allow-with-warning on unexpected errors, and reserve deny for policy you actually mean. Log the caught exception so you notice a broken hook before the team does.

Bound the latency. Every blocking hook adds wall-clock time to every agent interaction. Keep handlers sub-100ms: no HTTP calls to slow internal services inside PreToolUse, no Composer autoloading you don’t need, and a hard timeout in settings.json as a backstop. If a hook needs a network call, move it to PostToolUse or Stop.

Schema drift is the real risk. Claude Code’s hook event schema is owned by Anthropic and moves independently of this SDK, which has a single July 2025 release. Pin the exact Composer version, wrap the SDK behind your own interface, and add a canary: a hook that logs array_keys($payload) to a separate channel so you see new or renamed fields after any Claude Code upgrade before they break a rule.

Handle unknown payload shapes defensively. Don’t array_map over an assumed tool_input shape. Use the SDK’s accessors and treat missing keys as null. That tolerates schema additions across versions without a code change.

Windows and CI portability are environmental gotchas, not code ones. Test the exact OS images your CI uses. If your team or pipeline includes Windows runners, verify the hook command form works there before you commit to the pattern.

Instrument from day one. Log, per hook invocation: event type, tool name, decision (allow/deny), deny reason, and handler duration. This is what proves the guardrails are working, surfaces false-positive rates you can tune, and gives you the audit trail for “who let the agent do X?” without reconstructing it later.

Make the SDK replaceable. The intended exit path is: if upstream goes quiet or the schema changes materially, every hook can be swapped for a raw PHP script that reads php://stdin and exit(0|2). Design every hook to depend on your interface, not on the SDK’s classes, so that swap is one file.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource