Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 48 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,58 @@ Categories per release: **Added**, **Changed**, **Deprecated**, **Removed**, **F

## [Unreleased]

### Added

- Added Claude Code hook parity for optional `prompt_id`, eight Notification
types, Stop/SubagentStop background-task and session-cron registries, six
permission-update variants, the `manual` set-mode alias, `disableAllHooks`,
`continueOnBlock`, Agent background execution input, and injected
ExitPlanMode plan fields.
- Added event-aware hook-handler schemas and dedicated builder methods for
PostToolUseFailure, Stop, and SubagentStop feedback modes.
- Added `UserPromptSubmitOutput.suppressOriginalPrompt` and
`blockPrompt(reason, options?)` support for omitting the original prompt from
block messages.
- Added `HookOutputBuilder.postToolUseContext()` for non-block PostToolUse
context and tool-output replacement.
- Added `HookOutputBuilder.failureContext()` for non-block PostToolUseFailure
context injection.

### Changed

- Notification output is restricted to universal hook fields.
- Stop and SubagentStop block outputs require a present `reason` string (empty
string accepted) and remain distinct from non-error `additionalContext`
feedback.
- SubagentStop analysis treats blank agent transcripts as unavailable and
includes `last_assistant_message` when scoring completion errors.
- `stopFailureLog()` is a deprecated no-op compatibility shim because Claude
Code ignores StopFailure output and exit code.
- Project-authored hook documentation was audited against refreshed official
mirrors on 2026-07-12, including matcher semantics, handler support,
timeout overrides, root restrictions, tool inputs, and environment defaults.
- `HookOutputBuilder.feedback()` and `failureFeedback()` are documented as the
block-feedback paths; non-block replace/context helpers are separate.
- `preCompactOutputSchema` / `PreCompactOutput` reject PreCompact
`hookSpecificOutput` injection (block or universal fields only).
- `exitPlanModeToolInputSchema` strips unknown keys like other tool-input
schemas instead of using `.strict()`.
- `sessionStartContext(options)` preserves empty strings and empty `watchPaths`
via presence checks rather than truthiness.

### Fixed

- Canonicalized existing hook-event working directories before project-root
filtering so symlinked paths still reach an open project's endpoint.
- Exercised the actual esbuild-bundled forwarder in subprocess tests, including
its exit-zero, empty-output behavior for malformed stdin.
- PreCompact no longer overwrites detailed SessionStart restore context with the
abbreviated systemMessage board; both are persisted in a single write.
- Custom notification commands again expand `{title}`, `{message}`, `{priority}`,
and `{icon}` placeholders while still exporting `CLAUDE_NOTIFICATION_*` env vars.
- Notification placeholder expansion substitutes shell-safe env refs instead of
interpolating raw title/message text into `sh -c` (command-injection fix), and
is quote-aware so single-quoted legacy forms such as `'{title}'` still expand.

## [0.2.0] - 2026-07-12

Expand Down Expand Up @@ -81,7 +127,7 @@ Development milestone for `@libar-dev/claude-code-hooks` before the first public

### Added

- Full TypeScript coverage of all 28 Claude Code hook events (SessionStart through
- Full TypeScript coverage of all 30 Claude Code hook events (SessionStart through
ElicitationResult).
- `HookOutputBuilder` with methods for every output pattern across all hook types.
- Zod-based validation: per-event input/output schemas, tool-input schemas for 15 tools,
Expand All @@ -105,7 +151,7 @@ Development milestone for `@libar-dev/claude-code-hooks` before the first public
- `.nvmrc` pinning the development Node version.
- Full developer documentation tree: getting-started guide, hook-writing walkthrough,
settings.json configuration reference, cookbook, troubleshooting guide, and a complete
API reference (all 28 hook events, `HookOutputBuilder` methods, validator catalogue,
API reference (all 30 hook events, `HookOutputBuilder` methods, validator catalogue,
public type catalogue, and environment variable reference).

### Changed
Expand Down
50 changes: 35 additions & 15 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ const bashInput = validateBashToolInput(input); // Returns typed BashToolInput
## HookOutputBuilder Methods

- `permission(decision, reason, options?)` — PreToolUse allow/deny/ask/defer with optional `updatedInput`, `additionalContext`
- `feedback(reason, additionalContext?, updatedMCPToolOutput?)` — PostToolUse feedback
- `feedback(reason, additionalContext?, updatedMCPToolOutput?, updatedToolOutput?)` — PostToolUse block feedback with optional output replacement
- `postToolUseContext(options)` — PostToolUse non-block context and/or tool-output replacement
- `failureFeedback(reason, additionalContext?)` — PostToolUseFailure block feedback without output replacement
- `failureContext(additionalContext)` — PostToolUseFailure non-block context injection
- `allowPermission(options?)` / `denyPermission(options?)` — PermissionRequest decisions
- `permissionRequestSetMode(mode, destination?)` — PermissionRequest mode update helper
- `permissionRequestSetMode(mode, destination?)` — PermissionRequest mode update helper, including the `manual` output alias
- `permissionDeniedRetry(retry)` — PermissionDenied retry guidance
- `elicitation(action, content?, hookEventName?)` — Elicitation and ElicitationResult action output
- `watchPaths(paths)` — CwdChanged/FileChanged watch list output
Expand All @@ -71,10 +74,13 @@ const bashInput = validateBashToolInput(input); // Returns typed BashToolInput
- `teammateStop(reason)` — TeammateIdle stop output
- `batchBlock(reason)` — PostToolBatch block output
- `subagentContext(context)` — SubagentStart context injection
- `subagentStopContext(reason)` — SubagentStop block/context helper
- `sessionStartContext(context)` — SessionStart context injection
- `addContext(context)` / `blockPrompt(reason)` / `sessionTitle(title)` — UserPromptSubmit helpers
- `stopFailureLog(systemMessage?)` — StopFailure observability output
- `stopBlock(reason)` / `stopContext(context)` — blocking and non-error Stop feedback modes
- `subagentStopBlock(reason)` / `subagentStopAdditionalContext(context)` — blocking and non-error SubagentStop feedback modes
- `subagentStopContext(reason)` — deprecated blocking compatibility alias
- `setupContext(context)` / `messageDisplayContent(content)` — Setup and display-only output
- `sessionStartContext(contextOrOptions)` — SessionStart context, initial message, title, watch paths, and skill reload
- `addContext(context)` / `blockPrompt(reason, options?)` / `sessionTitle(title)` — UserPromptSubmit helpers (`options.suppressOriginalPrompt`)
- `stopFailureLog(systemMessage?)` — deprecated no-op because StopFailure ignores output and exit code
- `success(message?)` / `error(reason, stopExecution?)` — Universal helpers

## Hook Handler Types
Expand Down Expand Up @@ -181,7 +187,7 @@ import { validateHooksConfig } from '../validation/index.js';
const config = validateHooksConfig(parsed); // validates full settings hooks structure
```

Config supports common handler fields `if`, `timeout`, `statusMessage`, and `once`. Command handlers also support `async`, `asyncRewake`, and `shell`. Settings-root restriction fields include `allowManagedHooksOnly`, `allowedHttpHookUrls`, and `httpHookAllowedEnvVars`.
Config supports common handler fields `if`, `timeout`, `statusMessage`, and `once`; prompt/agent handlers add `continueOnBlock`; command handlers add `args`, `async`, `asyncRewake`, and `shell`. Runtime semantics are narrower than validation: `if` only runs on tool events and `once` is honored only in skill frontmatter. Settings-root fields are `disableAllHooks`, `allowManagedHooksOnly`, `allowedHttpHookUrls`, and `httpHookAllowedEnvVars`. Event-aware schemas enforce the handler support matrix; MessageDisplay deliberately remains generic because upstream does not classify its handler types.

## Build System

Expand All @@ -208,15 +214,29 @@ Hook behavior is configurable through environment variables. The library reads:
- TypeScript validation: `CLAUDE_HOOK_TS_FULL_CHECK`, `CLAUDE_HOOK_TS_BLOCK_ON_ERROR`, `CLAUDE_HOOK_TS_TIMEOUT`, `CLAUDE_HOOK_TS_STRICT_FILES`, `CLAUDE_HOOK_CONVEX_VALIDATION`
- Notifications: `CLAUDE_HOOK_DESKTOP_NOTIFICATIONS`, `CLAUDE_HOOK_CONSOLE_NOTIFICATIONS`, `CLAUDE_HOOK_NOTIFICATIONS_IN_CI`, `CLAUDE_HOOK_NOTIFICATION_COMMAND`, `CLAUDE_HOOK_SLACK_WEBHOOK`, `CLAUDE_HOOK_EMAIL_TO`, `CLAUDE_HOOK_EMAIL_FROM`, `CLAUDE_HOOK_SMTP_SERVER`
- Session context/end: `CLAUDE_HOOK_SESSION_GIT`, `CLAUDE_HOOK_SESSION_DEPS`, `CLAUDE_HOOK_SESSION_CHANGES`, `CLAUDE_HOOK_SESSION_DEV_STATUS`, `CLAUDE_HOOK_SESSION_MAX_COMMITS`, `CLAUDE_HOOK_SESSION_MAX_CHANGES`, `CLAUDE_HOOK_CONTEXT_FILES`, `CLAUDE_HOOK_CLEANUP_TEMP`, `CLAUDE_HOOK_SAVE_STATS`, `CLAUDE_HOOK_GENERATE_SUMMARY`, `CLAUDE_HOOK_ARCHIVE_TRANSCRIPT`, `CLAUDE_HOOK_SEND_NOTIFICATIONS`, `CLAUDE_HOOK_MAX_TEMP_AGE`
- Prompt/stop/subagent/pre-compact: `CLAUDE_HOOK_CHECK_SECRETS`, `CLAUDE_HOOK_ADD_CONTEXT`, `CLAUDE_HOOK_VALIDATE_STRUCTURE`, `CLAUDE_HOOK_CHECK_INJECTION`, `CLAUDE_HOOK_MAX_PROMPT_LENGTH`, `CLAUDE_HOOK_BLOCK_INJECTION`, `CLAUDE_HOOK_CHECK_TASKS`, `CLAUDE_HOOK_CHECK_GIT`, `CLAUDE_HOOK_CHECK_TESTS`, `CLAUDE_HOOK_MAX_CONTINUATIONS`, `CLAUDE_HOOK_VALIDATE_SUBAGENT`, `CLAUDE_HOOK_CHECK_SUBAGENT_ERRORS`, `CLAUDE_HOOK_LOG_SUBAGENT_METRICS`, `CLAUDE_HOOK_SUBAGENT_MAX_RETRIES`, `CLAUDE_HOOK_SAVE_CONTEXT`, `CLAUDE_HOOK_EXTRACT_DECISIONS`, `CLAUDE_HOOK_CREATE_BACKUP`, `CLAUDE_HOOK_MAX_CONTEXT_SIZE`
- Prompt/stop/subagent/pre-compact: `CLAUDE_HOOK_CHECK_SECRETS`, `CLAUDE_HOOK_ADD_CONTEXT`, `CLAUDE_HOOK_VALIDATE_STRUCTURE`, `CLAUDE_HOOK_CHECK_INJECTION`, `CLAUDE_HOOK_MAX_PROMPT_LENGTH`, `CLAUDE_HOOK_BLOCK_INJECTION`, `CLAUDE_HOOK_CHECK_TASKS`, `CLAUDE_HOOK_CHECK_GIT`, `CLAUDE_HOOK_CHECK_TESTS`, `CLAUDE_HOOK_VALIDATE_SUBAGENT`, `CLAUDE_HOOK_CHECK_SUBAGENT_ERRORS`, `CLAUDE_HOOK_LOG_SUBAGENT_METRICS`, `CLAUDE_HOOK_SAVE_CONTEXT`, `CLAUDE_HOOK_EXTRACT_DECISIONS`, `CLAUDE_HOOK_CREATE_BACKUP`, `CLAUDE_HOOK_MAX_CONTEXT_SIZE`

Processing CLIs have a small separate env surface that is not loaded through
`getConfig()`. Today that includes `CLAUDE_TAIL_MARKER_ROOTS` for
`claude-session-tail --marker-dir`. Keep hook env-var docs and processing CLI
docs separate. Library consumers of the tail APIs should pass the per-call
`allowedMarkerRoots` option instead of relying on that env var.
Processing CLIs have a separate env surface that is not loaded through `getConfig()`, including `CLAUDE_TAIL_MARKER_ROOTS` for `claude-session-tail --marker-dir`. Keep hook env-var docs and processing CLI docs separate. Library consumers of the tail APIs should pass the per-call `allowedMarkerRoots` option instead of relying on that env var.

Set `CLAUDE_HOOK_DEBUG=true` or `CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose` for verbose logging. The default hook timeout is 60 seconds. `CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS` defaults to 1500 ms and is capped at 60000 ms.
Set `CLAUDE_HOOK_DEBUG=true` or `CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose` for verbose library logging. `CLAUDE_HOOK_TIMEOUT` defaults this library's runner to 60 seconds; Claude Code settings handlers instead default to 600 seconds for command/HTTP/MCP, 30 for prompt, and 60 for agent, with 30-second UserPromptSubmit and 10-second MessageDisplay overrides. `CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS` defaults to 1500 ms and is capped at 60000 ms.

## Code review (Greptile)

This is a public OSS repo. **Greptile is available here permanently** (OSS free forever) for PR bot review and local CLI review. Prefer it as the primary automated reviewer for this repository.

**Local (pre-push):** Commit first, then review committed work against the base branch. Agents should use structured output.

```bash
greptile whoami # must be signed in (check text; exit 0 even when signed out)
greptile review -b main --json # or omit -b for the repo default base
greptile review status --json # whether HEAD already has a completed review
```

- Findings still exit `0`; non-zero means the review did not finish.
- Triage `securityIssue: true`, then `P0` / `P1` / `P2`. Aim for confidence `5` with zero comments when polishing a branch (`greploop` skill if iterating).
- PR bot comments are fetched with `gh` (`gh api repos/.../pulls/<n>/comments`), not with the Greptile CLI.

**Do not** treat CodeRabbit (or other review bots) as the source of truth on this repo when Greptile is configured.

## Public Repository Hygiene

Expand All @@ -232,4 +252,4 @@ Planning and context files created for agent workflows are ephemeral and must no

## Compatibility Notes

`PermissionRequest` now uses nested `hookSpecificOutput.decision` with `behavior: "allow" | "deny"` and optional permission updates. The old top-level allow/deny style should not be used for new code.
`PermissionRequest` uses nested `hookSpecificOutput.decision` with `behavior: "allow" | "deny"` and the six documented permission-update variants. Stop and SubagentStop have separate block and non-error additional-context modes; block output requires a reason. Notification accepts only universal output. StopFailure is side-effect-only. The old top-level PermissionRequest allow/deny style should not be used.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ echo '{"hook_event_name":"PreToolUse","session_id":"s1","transcript_path":"/tmp/

| Module | Contents |
|--------|----------|
| `@libar-dev/agent-harness-kit/types` | TypeScript types for all 30 events + `HookOutputBuilder` |
| `@libar-dev/agent-harness-kit/types` | TypeScript types for all 30 events, event-aware settings types, and `HookOutputBuilder` |
| `@libar-dev/agent-harness-kit/utils` | `executeHook`, `outputJson`, `isProtectedFile`, `isDangerousCommand`, logging |
| `@libar-dev/agent-harness-kit/validation` | Zod schemas, per-event validators, 15 tool-input validators, `validateHooksConfig` |
| `@libar-dev/agent-harness-kit/validation` | Zod schemas for all 30 events, tool-input validators, output schemas, and event-aware `validateHooksConfig` |
| `@libar-dev/agent-harness-kit/pre-tool-use` | Reference handlers: bash validator, file protector, ESLint-disable blocker |
| `@libar-dev/agent-harness-kit/post-tool-use` | Reference handlers: Prettier formatter, TypeScript checker |
| `@libar-dev/agent-harness-kit/lifecycle` | Reference handlers: setup, session start/end, notifications, message display, stop, subagents, elicitation |
Expand All @@ -86,12 +86,12 @@ echo '{"hook_event_name":"PreToolUse","session_id":"s1","transcript_path":"/tmp/

- **[Getting Started](docs/guides/getting-started.md)** — install, sub-path imports, 5-minute walkthrough
- **[Writing Your First Hook](docs/guides/writing-your-first-hook.md)** — `executeHook` skeleton, validators, `permission()`, testing
- **[Configuring settings.json](docs/guides/configuring-settings-json.md)** — 5 handler types, matcher syntax, `if`/`timeout`/`async`
- **[Configuring settings.json](docs/guides/configuring-settings-json.md)** — handler matrix, matcher semantics, accepted/inert fields, timeouts, and root restrictions
- **[Cookbook](docs/guides/cookbook.md)** — 10 copy-pasteable recipes
- **[Hook Events Reference](docs/reference/hook-events.md)** — all 30 events with input/output shapes
- **[HookOutputBuilder Reference](docs/reference/output-builder.md)** — every method with examples
- **[Validators Reference](docs/reference/validators.md)** — tool-input validators, type guards, config validators
- **[Environment Variables](docs/reference/environment-variables.md)** — all `CLAUDE_HOOK_*` vars
- **[Hook Events Reference](docs/reference/hook-events.md)** — all 30 events with exact implemented input/output contracts
- **[HookOutputBuilder Reference](docs/reference/output-builder.md)** — every implemented method, including distinct Stop/SubagentStop feedback modes
- **[Validators Reference](docs/reference/validators.md)** — input/output schemas, tool routing, type guards, and event-aware config validation
- **[Environment Variables](docs/reference/environment-variables.md)** — `getConfig()`, Claude Code process vars, and direct reference-handler configuration
- **[Session tailing](docs/internal/tail-session.md)** — CLI and public library APIs for live transcript ingestion
- **[Full docs index](docs/README.md)**

Expand Down
Loading
Loading