Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@ThomsenDrake
Copy link
Contributor

@ThomsenDrake ThomsenDrake commented Jan 16, 2026

Internal Hooks: Event-Driven Automation for Agent Commands

This PR introduces internal hooks, a new first-class feature that enables event-driven automation in response to agent commands and lifecycle events. Hooks allow you to trigger custom TypeScript functions when events like /new, /reset, or /stop occur, without modifying Clawdbot's core code.

What Are Internal Hooks?

Internal hooks provide an extensible system for automating actions in response to agent events:

  • Listen to agent commands - React when users issue /new, /reset, /stop
  • Run custom TypeScript code - Full access to Node.js APIs, file system, external services
  • No core modifications needed - Extend behavior without touching Clawdbot internals
  • Event-driven architecture - Handlers are called automatically when events fire

Use cases:

  • Save session context to memory when starting a new conversation
  • Log all commands for auditing and debugging
  • Automatically commit code changes before resetting
  • Send notifications to external services
  • Archive conversations to cloud storage
  • Any automation triggered by agent lifecycle events

Example: Simple Hook Handler

import type { InternalHookHandler } from '../../src/hooks/internal-hooks.js';

const myHandler: InternalHookHandler = async (event) => {
  if (event.type !== 'command' || event.action !== 'new') {
    return;
  }

  console.log(`[my-hook] New command triggered`);
  console.log(`  Session: ${event.sessionKey}`);
  
  // Your custom logic here - file operations, API calls, etc.
  
  // Optionally send message to user
  event.messages.push('✨ Hook executed!');
};

export default myHandler;

Hooks System: First-Class Feature Alongside Skills

This implementation makes hooks a first-class feature with the same patterns as skills: automatic discovery, CLI management, and eligibility gating.

Automatic Directory-Based Discovery

Hooks are automatically discovered from three tiers (matching skills pattern):

  1. Workspace hooks (<workspace>/hooks/) - Per-agent, highest precedence
  2. Managed hooks (~/.clawdbot/hooks/) - User-installed, shared across workspaces
  3. Bundled hooks (<clawdbot>/dist/hooks/bundled/) - Shipped with Clawdbot (compiled)

No manual config editing required.

CLI Management Suite

# List all available hooks
clawdbot hooks internal list [--eligible] [--json] [--verbose]

# Get detailed information
clawdbot hooks internal info <name> [--json]

# Check eligibility status
clawdbot hooks internal check [--json]

# Enable/disable hooks with one command
clawdbot hooks internal enable session-memory
clawdbot hooks internal disable command-logger

HOOK.md Metadata Format

Each hook is a directory containing:

  • HOOK.md - YAML frontmatter + JSON metadata + Markdown documentation
  • handler.ts (source) / handler.js (compiled) - Handler implementation
---
name: my-hook
description: "Short description"
homepage: https://docs.clawd.bot/hooks/my-hook
metadata: {"clawdbot":{"emoji":"🔗","events":["command:new"],"requires":{"bins":["node"]}}}
---

Eligibility Gating

Hooks can declare requirements that are automatically checked:

  • Required binaries on PATH
  • Required environment variables
  • Required config values
  • OS platform compatibility

Only eligible hooks are loaded and presented to users.

Onboarding Integration

The onboarding wizard now automatically discovers eligible hooks and presents them for selection, just like skills. No hardcoded lists.

Configuration Format

{
  "hooks": {
    "internal": {
      "enabled": true,
      "entries": {
        "session-memory": { "enabled": true },
        "command-logger": { "enabled": false }
      }
    }
  }
}

Legacy handlers[] format still supported for backwards compatibility.

Architecture

New Modules (8):

  • src/hooks/types.ts - Core type definitions
  • src/hooks/workspace.ts - Three-tier directory scanning
  • src/hooks/frontmatter.ts - HOOK.md metadata parser
  • src/hooks/config.ts - Eligibility checking
  • src/hooks/bundled-dir.ts - Bundled hooks resolver (supports both dev and compiled paths)
  • src/hooks/hooks-status.ts - Status report builder
  • src/cli/hooks-internal-cli.ts - Full CLI suite
  • scripts/copy-hook-metadata.ts - Build step to copy HOOK.md files alongside compiled JS

Core Hook System:

  • src/hooks/internal-hooks.ts - Event registration and triggering
  • src/hooks/loader.ts - Updated for directory discovery + legacy support
  • src/commands/onboard-hooks.ts - Uses discovery instead of hardcoded config
  • src/gateway/server-startup.ts - Loads hooks on startup

Event Triggers:

  • src/auto-reply/reply/commands-core.ts - Triggers command events
  • src/auto-reply/reply/commands-session.ts - Triggers session events

Bundled Hooks: Real-World Examples

1. Session Memory Hook (Solves Real User Pain)

Problem: Users have reported that some of the cheaper models (Minimax, z.ai, DeepSeek, etc.) are less eager to update memories on their own during conversations. They often need explicit prompting to save context to memory, which creates friction.

Solution: The session-memory bundled hook automatically saves session context when you issue /new, forcing memory updates without requiring model cooperation.

What it does:

  1. Triggers on /new command
  2. Finds the previous session file (most recent before reset)
  3. Extracts the last 15 lines of conversation
  4. Uses LLM to generate a descriptive filename slug
  5. Saves session metadata and conversation content to ~/clawd/memory/YYYY-MM-DD-slug.md

Example output files:

  • 2026-01-16-vendor-pitch.md
  • 2026-01-16-api-design.md
  • 2026-01-16-bug-fix.md
  • 2026-01-16-1430.md (fallback timestamp if slug generation fails)

Onboarding Option:

During clawdbot onboard, users are now prompted to enable this hook:

? Enable internal hooks?
  [ ] Skip for now
  [x] 💾 session-memory - Save session context to memory when /new command is issued

This ensures memory is always captured, regardless of which model you're using.

2. Command Logger Hook

Logs all command events to ~/.clawdbot/logs/commands.log in JSONL format for auditing and debugging.

What it does:

  • Captures all command events (/new, /reset, /stop, etc.)
  • Logs timestamp, action, session key, sender ID, source
  • Appends to log file in JSONL format
  • Runs silently in the background

Use cases:

  • Debugging: Track when commands were issued and from which source
  • Auditing: Monitor command usage across different channels
  • Analytics: Analyze command patterns and frequency

NPM Distribution & Compilation

Fixes Applied:

1. Bundled Hooks Compilation (c227a83)

Issue: Initial implementation shipped bundled hooks as TypeScript source files, which failed to load when installed via npm with error:

Stripping types is currently unsupported for files under node_modules

Solution:

  • Moved bundled hooks from hooks/bundled/ to src/hooks/bundled/ for TypeScript compilation
  • Added scripts/copy-hook-metadata.ts build step to copy HOOK.md files alongside compiled JS
  • Updated resolveBundledHooksDir() to resolve both compiled (dist/hooks/bundled/) and source (src/hooks/bundled/) locations
  • Bundled hooks now ship as compiled JavaScript in dist/hooks/bundled/ with metadata files

2. LLM Slug Generator Import Path (c227a83)

Issue: Session-memory hook failed with module not found error due to incorrect import path (dist/hooks/dist/hooks/llm-slug-generator.js).

Solution: Fixed import path resolution to correctly locate llm-slug-generator.js at dist/hooks/llm-slug-generator.js from compiled handler location.

3. Conversation Content in Memory Files (5cab0c7)

Issue: Session-memory hook was only saving session metadata (session key, ID, source) without the actual conversation content.

Solution:

  • Store sessionContent in accessible scope (not just for slug generation)
  • Add "## Conversation Summary" section to memory file output
  • Include the last 15 lines of conversation in the saved memory

This ensures hooks work correctly in both development (TypeScript source) and production (compiled JavaScript from npm), and that memory files contain complete conversation context.

Documentation

  • docs/internal-hooks.md - Complete guide (779 lines)

    • What internal hooks are and how they work
    • Directory-based discovery system
    • Three-tier hierarchy explanation
    • HOOK.md metadata format
    • CLI command reference
    • Bundled hooks documentation
    • Creating custom hooks guide
    • Event types and handler API
    • Best practices and troubleshooting
  • docs/cli/hooks.md - CLI reference (232 lines)

    • All hooks internal commands
    • Usage examples
    • Bundled hooks quick reference

Testing

  • ✅ All hook tests passing (43 tests)
  • ✅ CLI commands tested and verified
  • ✅ Gateway integration confirmed with compiled bundled hooks
  • ✅ Build successful
  • ✅ Lint checks pass (0 warnings, 0 errors)
  • ✅ 3406/3407 tests passing (99.97%)
  • ✅ Hooks load correctly when installed via npm/Volta
  • ✅ Session-memory hook tested end-to-end with real conversation capture

The 1 failing test is a pre-existing timezone flaky test unrelated to this PR.

Benefits

  • New Capability - Event-driven automation without core modifications
  • Consistency with Skills - Same discovery, management, and eligibility patterns
  • No Manual Config Editing - Simple CLI commands for enable/disable
  • Automatic Discovery - Hooks are found automatically from standard locations
  • Better Documentation - Each hook has its own HOOK.md with full docs
  • Eligibility Checking - Hooks with missing requirements are clearly identified
  • Improved UX - Onboarding wizard presents available hooks automatically
  • Solves Real Problems - Session memory hook addresses pain with cheaper models
  • Production Ready - Hooks compile to JavaScript and work in npm distribution

This introduces internal hooks as a first-class feature alongside skills, with two practical bundled examples that demonstrate the system's capabilities and solve real user needs.

Drake Thomsen added 8 commits January 16, 2026 10:50
Implements an extensible event-driven hook system that allows custom automations to trigger on agent commands (e.g., /new, /stop). Hooks are configured via config.json and loaded dynamically from TypeScript modules.

Features:
- Core hook system with register/trigger/event types
- Dynamic hook loader for external modules
- Integration with command processing (/new, /reset, /stop)
- Example handlers: session-memory, command-logger
- Comprehensive tests (25 tests total)
- Full documentation in docs/internal-hooks.md

Similar to Claude Code's hook system, this enables users to extend clawdbot's behavior without modifying core code.
Adds a step to the onboarding wizard that prompts users to enable
the session memory hook (/new saves context to disk).

Users can:
- Learn what internal hooks do
- Enable the session-memory handler (default: yes)
- See where session files will be saved
- Disable if they prefer

Includes comprehensive tests (5 tests) for the onboard-hooks module.
- Added debug console.log statements to hook loader
- Created JavaScript version of session-memory handler
- Added hooks/** to package.json files array
- Fixed hook loading for testing

The hook system now works when tested directly.
The internal hooks configuration was being stripped during config
validation because it wasn't included in the Zod schema.

Added:
- InternalHookHandlerSchema for handler config
- InternalHooksSchema for the internal section
- Added 'internal' field to hooks object in ClawdbotSchema

The gateway now successfully loads and registers internal hook handlers
at startup. Config validation no longer strips the hooks.internal section.
Removed debug console.log statements from:
- src/hooks/loader.ts
- src/gateway/server-startup.ts

The gateway now logs cleanly:
[hooks] loaded 1 internal hook handler

Ready for production use.
- Implement LLM slug generator that creates descriptive filenames
- Fix session-memory hook to access OLD session before reset
- Hook now finds previous session by modification time
- Add cache-busting to hook loader to prevent stale modules
- Add hook messages array to internal hook events
- Route hook confirmation messages back to user
- Create separate memory files per session with format: YYYY-MM-DD-slug.md
- Remove stale JavaScript version of session-memory handler
- Add debug logging throughout hook execution

Fixes issue where hook received empty NEW session instead of conversation history.
The hook now correctly generates descriptive slugs like 'vendor-pitch' based on
conversation content using the configured LLM provider.
Redesigns internal hooks system to match skills installation experience
with automatic discovery, CLI management, and eligibility gating.

**New Infrastructure (7 modules):**
- src/hooks/types.ts - Core type definitions
- src/hooks/workspace.ts - Three-tier directory scanning
- src/hooks/frontmatter.ts - HOOK.md metadata parser
- src/hooks/config.ts - Eligibility checking (bins, env, config, os)
- src/hooks/bundled-dir.ts - Bundled hooks directory resolver
- src/hooks/hooks-status.ts - Status report builder
- src/cli/hooks-internal-cli.ts - Full CLI suite

**CLI Commands:**
- clawdbot hooks internal list [--eligible] [--json] [--verbose]
- clawdbot hooks internal info <name> [--json]
- clawdbot hooks internal check [--json]
- clawdbot hooks internal enable <name>
- clawdbot hooks internal disable <name>

**Hook Discovery:**
Three-tier hierarchy (workspace → managed → bundled):
1. <workspace>/hooks/ - Per-agent, highest precedence
2. ~/.clawdbot/hooks/ - User-installed, shared
3. <clawdbot>/hooks/bundled/ - Shipped with Clawdbot

**HOOK.md Format:**
YAML frontmatter + JSON metadata + Markdown docs:
- metadata.clawdbot.emoji - Display emoji
- metadata.clawdbot.events - Events to listen for
- metadata.clawdbot.requires - Requirements (bins, env, config, os)
- metadata.clawdbot.homepage - Documentation URL

**Bundled Hooks:**
Migrated to new structure with HOOK.md:
- hooks/bundled/session-memory/ - Saves context on /new
- hooks/bundled/command-logger/ - Logs all commands
- hooks/bundled/README.md - Comprehensive guide

**Onboarding Integration:**
- Automatic hook discovery in wizard
- Presents eligible hooks for selection
- Uses new entries config format

**Configuration:**
New format (backwards compatible):
{
  "hooks": {
    "internal": {
      "enabled": true,
      "entries": {
        "session-memory": { "enabled": true },
        "command-logger": { "enabled": false }
      }
    }
  }
}

Legacy handlers[] format still supported.

**Documentation:**
- docs/internal-hooks.md - Complete rewrite (779 lines)
- docs/cli/hooks.md - CLI reference (232 lines)

**Testing:**
- All hook tests passing (43 tests)
- CLI commands tested and verified
- Gateway integration confirmed
- Build successful

Closes #XXX
- Remove empty fallback objects in spread operations
- Remove unused fsp variable
- Await writeConfigFile promises to avoid floating promises

All lint checks now pass (0 warnings, 0 errors)
@ThomsenDrake ThomsenDrake changed the title feat: directory-based hook discovery and CLI management feat: Internal Hooks: Event-Driven Automation for Agent Commands Jan 16, 2026
Drake Thomsen added 2 commits January 16, 2026 12:29
Fixes bundled hooks failing to load when installed via npm with error:
"Stripping types is currently unsupported for files under node_modules"

Changes:
- Move bundled hooks from hooks/bundled/ to src/hooks/bundled/ for compilation
- Add scripts/copy-hook-metadata.ts to copy HOOK.md files during build
- Update build script to include hook metadata copy step
- Update resolveBundledHooksDir() to resolve both compiled (dist/hooks/bundled)
  and source (src/hooks/bundled) locations
- Fix import paths in hook handlers (../../internal-hooks.js)
- Remove hooks/** from package.json files array (now ships compiled dist/hooks/)

The bundled hooks now ship as compiled JavaScript in dist/hooks/bundled/ with
their HOOK.md metadata files, allowing proper loading when installed globally.
The session-memory hook was only saving session metadata (session key, ID, source)
without including the actual conversation content in the memory file.

Changes:
- Store sessionContent in accessible scope (not just for slug generation)
- Add "## Conversation Summary" section to memory file output
- Include the last 15 lines of conversation in the saved memory

This ensures memory files contain the full conversation context, matching the
format of manually created memory files and providing useful context for future
sessions.
@steipete
Copy link
Contributor

Landed on main:

Closing PR since the changes are now on main. Thanks!

@steipete steipete closed this Jan 17, 2026
@steipete
Copy link
Contributor

This is a really cool new feature, kudos, Drake!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants